在这里找到一些类似的问题后:
我创建了一个应用程序,它在 windowDeactivated、windowClosing 和 WindowClosed 事件中的每一个上触发 System.out.println() 语句,并尝试使用系统 X 按钮和setVisible(false)的按钮:
/**
* 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")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
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();
}// </editor-fold>
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")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
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();
}// </editor-fold>
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);
}
}
将此作为窗口侦听器添加到 JDialog 或 JFrame 扩展并实现一个版本的 CloseDuties 为关闭窗口的业务增加了很大的灵活性和可靠性。