【问题标题】:In Java, where to put code to reliably fire on window close?在Java中,在哪里放置代码以在窗口关闭时可靠地触发?
【发布时间】:2015-07-21 04:25:25
【问题描述】:

我有几个要保存默认值的窗口,在用户采取操作关闭窗口之后(通过标题栏中的按钮、菜单项或我提供的按钮)以及在它被释放之前的。

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

如何确保代码在窗口被处理之前运行?这是窗口本身应该能够处理的任务。我对 Closed、Closing 和 dispose 事件的不可靠性感到有点沮丧。我错过了什么?

【问题讨论】:

标签: java swing


【解决方案1】:

使用WindowListenerwindowClosed()方法。一般来说,在 Java 中,您不能依赖处置。不保证会调用此方法。

【讨论】:

  • 这与我原帖中的第二点有何不同?如果我提供自己的按钮或菜单项,windowClosed() 不会触发。
  • 你用有趣的名字称呼它(甚至链接显示正确),所以我错过了它。一般来说 - 使用所有方法,因为它们会触发不同的事件,如 Closed 和 Closing。另外,我可以建议您为每种方法添加日志记录吗?它可能太快了,您可能会错过它。
【解决方案2】:

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

我创建了一个应用程序,它在 windowDeactivatedwindowClosingWindowClosed 事件中的每一个上触发 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 为关闭窗口的业务增加了很大的灵活性和可靠性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-06
    • 1970-01-01
    • 2015-06-27
    • 1970-01-01
    相关资源
    最近更新 更多