In Java, where to put the code to run reliably when the window is closed?
I have multiple windows that I want to keep the defaults from, after the user takes an action to close the window (with a button on the title bar, a menu item, or a button that I have provided) and before it is removed of.
- On some windows I can DISPOSE_ON_CLOSE, but I need information before they are removed ().
- formWindowClosed seems to only fire when the window is removed, which I can't always rely on (see above and below)
- formWindowClosing only seems to fire when the user closes the window from the system title menu, although I call System.exit (0) on my own menu action handler
- According to the GC documentation, the dispose () method is not always called and is often ignored when the application is shut down.
- added a ShutdownHook to run System.runFinalization (), but the code is still not executing.It may be too late as some windows will be removed by this time.
How can I ensure that the code will run before the window is deleted? This is a task that a window should be able to take care of on its own. I am upset about the unreliability of closed, closure and disposition events. What am I missing?
source to share
After looking for a number of similar questions:
- WindowAdapterClosed method not working
- Workaround for incompatible JVMs not dispatching WindowClosing events
- JFrame window close message
I created an application that produced statements System.out.println()
for each of the events windowDeactivated
, windowClosing
and WindowClosed
and tried to close both windows JFrame
and a JDialog
with the System X button and with, which is simple 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);
}
});
}
}
From this, I created a WindowListener that reliably runs once and only once, asking for permission if necessary before allowing the close. It works for JFrame and JDialog. I chose to define "closed" as any time the window transitions from visible to invisible and "open" as any time the window transitions from invisible to visible. This will not enable display / deionization. windowClosing
fees will not be enforced if confirmClose
permission to close is denied, which prevents the window from being invisible.
/**
*
* @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);
}
}
Adding this as a windowlistener to a JDialog or JFrame extension and implementing the CloseDuties version adds more flexibility and robustness for closing the window.
source to share
Use WindowListener windowClosed () . In general, in Java, you cannot rely on an order. This method is not guaranteed to be called.
source to share