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?

+3


source to share


2 answers


After looking for a number of similar questions:

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.

0


source


Use WindowListener windowClosed () . In general, in Java, you cannot rely on an order. This method is not guaranteed to be called.



0


source







All Articles