在Java中,在哪里放置代码以在窗口关闭时可靠地触发?

我有几个窗口,我想保存默认值,在用户采取行动关闭窗口(通过标题栏中的按钮,菜单项或我提供的按钮)之后,在它被处理之前。

  • 有些窗口我可以DISPOSE_ON_CLOSE但有些窗口我需要信息才能处理它们()。
  • formWindowClosed似乎只在窗口被处理时触发,我不能总是依赖它(见上下文 )
  • formWindowClosing似乎只有当用户从标题栏的系统菜单关闭窗口时才会触发,即使我在自己的菜单操作处理程序中调用System.exit(0)
  • 根据GC文档,并不总是调用dispose()方法,并且在关闭应用程序时经常会忽略它。
  • 我添加了一个ShutdownHook来运行System.runFinalization()但代码仍未执行。 这可能为时已晚,因为有些窗户将被处理掉。

如何在窗口处理之前确保代码运行? 窗口本身应该能够处理这项任务。 我对封闭,关闭和处置事件的不可靠性感到沮丧。 我错过了什么?

使用WindowListener windowClosed()方法。 通常在Java中,您不能依赖于处置。 无法保证将调用此方法。

在这里找到一些类似的问题之后:

  • WindowAdapter windowClosed方法未运行
  • 不合规JVM的解决方法是不发送WindowClosing事件
  • 关闭JFrame窗口时的消息

我创建了一个应用程序,它在每个windowDeactivatedwindowClosingWindowClosed事件上触发了System.out.println()语句,并尝试使用系统X按钮和一个只有setVisible(false)的按钮关闭JFrameJDialog窗口:

 /** * Test program to explore the relationship between defaultCloseOperation * states, and the sequence of events triggered when closing a window * with the (X) button vs using setVisible(false). * * @author MaskedCoder * */ package testwindowadapter; import java.awt.EventQueue; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import javax.swing.WindowConstants; /** * Class to listen for window close events */ public class WindowNotifier extends WindowAdapter implements WindowListener { public WindowNotifier() { super(); } @Override public void windowClosing(WindowEvent e) { super.windowClosing(e); System.out.println(e.getComponent().getClass().getSimpleName() + ".windowClosing fired"); } @Override public void windowClosed(WindowEvent e) { super.windowClosed(e); //To change body of generated methods, choose Tools | Templates. System.out.println(e.getComponent().getClass().getSimpleName() + ".windowClosed fired"); } @Override public void windowDeactivated(WindowEvent e) { super.windowDeactivated(e); System.out.println(e.getComponent().getClass().getSimpleName() + ".windowDeactivated fired"); } } /** * Creates new form TestDialog */ public class TestDialog extends javax.swing.JDialog { public TestDialog(java.awt.Frame parent, boolean modal) { super(parent, modal); initComponents(); addWindowListener(new WindowNotifier()); cboDefaultCloseOp.setSelectedIndex(getDefaultCloseOperation()); } /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always * regenerated by the Form Editor. */ @SuppressWarnings("unchecked") //  private void initComponents() { btnClose = new javax.swing.JButton(); cboDefaultCloseOp = new javax.swing.JComboBox(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setTitle("WindowAdapter Test"); btnClose.setText("Close window"); btnClose.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { btnCloseActionPerformed(evt); } }); cboDefaultCloseOp.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "DO_NOTHING_ON_CLOSE", "HIDE_ON_CLOSE", "DISPOSE_ON_CLOSE" })); cboDefaultCloseOp.addItemListener(new java.awt.event.ItemListener() { public void itemStateChanged(java.awt.event.ItemEvent evt) { cboDefaultCloseOpItemStateChanged(evt); } }); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(cboDefaultCloseOp, javax.swing.GroupLayout.PREFERRED_SIZE, 225, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(layout.createSequentialGroup() .addGap(58, 58, 58) .addComponent(btnClose))) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(cboDefaultCloseOp, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(18, 18, 18) .addComponent(btnClose) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); pack(); }//  private void btnCloseActionPerformed(java.awt.event.ActionEvent evt) { setVisible(false); } private void cboDefaultCloseOpItemStateChanged(java.awt.event.ItemEvent evt) { setDefaultCloseOperation(cboDefaultCloseOp.getSelectedIndex()); } // Variables declaration - do not modify private javax.swing.JButton btnClose; private javax.swing.JComboBox cboDefaultCloseOp; // End of variables declaration } /** * Creates new form TestFrame */ public class TestFrame extends javax.swing.JFrame { public TestFrame() { super(); initComponents(); addWindowListener(new WindowNotifier()); cboDefaultCloseOp.setSelectedIndex(getDefaultCloseOperation()); } /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always * regenerated by the Form Editor. */ @SuppressWarnings("unchecked") //  private void initComponents() { cboDefaultCloseOp = new javax.swing.JComboBox(); btnClose = new javax.swing.JButton(); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); cboDefaultCloseOp.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "DO_NOTHING_ON_CLOSE", "HIDE_ON_CLOSE", "DISPOSE_ON_CLOSE", "EXIT_ON_CLOSE" })); cboDefaultCloseOp.addItemListener(new java.awt.event.ItemListener() { public void itemStateChanged(java.awt.event.ItemEvent evt) { cboDefaultCloseOpItemStateChanged(evt); } }); btnClose.setText("Close window"); btnClose.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { btnCloseActionPerformed(evt); } }); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(cboDefaultCloseOp, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(layout.createSequentialGroup() .addGap(41, 41, 41) .addComponent(btnClose))) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(cboDefaultCloseOp, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(18, 18, 18) .addComponent(btnClose) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); pack(); }//  private void btnCloseActionPerformed(java.awt.event.ActionEvent evt) { setVisible(false); } private void cboDefaultCloseOpItemStateChanged(java.awt.event.ItemEvent evt) { setDefaultCloseOperation(cboDefaultCloseOp.getSelectedIndex()); } // Variables declaration - do not modify private javax.swing.JButton btnClose; private javax.swing.JComboBox cboDefaultCloseOp; // End of variables declaration } public class TestWindowAdapter { public TestWindowAdapter() { } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { // TestDialog MainWin = new TestDialog(null, true); TestFrame MainWin = new TestFrame(); MainWin.setVisible(true); } }); } } 

从那时起,我创建了一个WindowListener,它可以一次性地只触发一次,可选地在允许关闭之前请求权限。 它适用于JFrame和JDialog。 我选择将“关闭”定义为窗口从可见到不可见的任何时间,并在窗口从不可见到可见时随时“打开”。 这不包括图标化/取消图标化。 如果confirmClose拒绝关闭权限,则不会执行windowClosing任务,从而防止窗口变得不可见。

 /** * * @author MaskedCoder */ package testwindowadapter; import java.awt.Window; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.WindowConstants; public class ReliableOneShotCloseListener extends WindowAdapter implements WindowListener { public interface CloseDuties { public boolean confirmClose(WindowEvent e); public void windowClosing(WindowEvent e); } private CloseDuties closeDuties; private int defaultCloseOperation; private boolean windowClosingFired = false; public ReliableOneShotCloseListener(int iniDefaultCloseOperation, CloseDuties iniCloseDuties) { super(); closeDuties = iniCloseDuties; defaultCloseOperation = iniDefaultCloseOperation; } private int getDefaultCloseOperation(WindowEvent e) { if(e.getComponent() instanceof JFrame) { return ((JFrame) e.getComponent()).getDefaultCloseOperation(); } else if(e.getComponent() instanceof JDialog) { return ((JDialog) e.getComponent()).getDefaultCloseOperation(); } else throw new IllegalArgumentException("WindowEvent.getComponent() is " + e.getComponent().getClass().getSimpleName() + ", must be JFrame or JDialog."); } private void setDefaultCloseOperation(WindowEvent e, int newDefaultCloseOperation) { if(e.getComponent() instanceof JFrame) { ((JFrame) e.getComponent()).setDefaultCloseOperation(newDefaultCloseOperation); } else if(e.getComponent() instanceof JDialog) { ((JDialog) e.getComponent()).setDefaultCloseOperation(newDefaultCloseOperation); } else throw new IllegalArgumentException("WindowEvent.getComponent() is " + e.getComponent().getClass().getSimpleName() + ", must be JFrame or JDialog."); } private void performCloseDuties(WindowEvent e) { if(!windowClosingFired) { if(closeDuties.confirmClose(e)) { setDefaultCloseOperation(e, defaultCloseOperation); closeDuties.windowClosing(e); windowClosingFired = true; } else setDefaultCloseOperation(e, WindowConstants.DO_NOTHING_ON_CLOSE); } } public int getDefaultCloseOperation() { return defaultCloseOperation; } public void setDefaultCloseOperation(int newDefaultCloseOperation) { defaultCloseOperation = newDefaultCloseOperation; } @Override public void windowOpened(WindowEvent e) { windowClosingFired = false; } @Override public void windowClosing(WindowEvent e) { performCloseDuties(e); } @Override public void windowClosed(WindowEvent e) { performCloseDuties(e); } @Override public void windowActivated(WindowEvent e) { windowClosingFired = false; } @Override public void windowDeactivated(WindowEvent e) { if(!e.getComponent().isVisible()) performCloseDuties(e); } } 

将此作为windowlistener添加到JDialog或JFrame扩展并实现CloseDuties的版本为关闭窗口的业务增加了很大的灵活性和可靠性。