获取 JPanel 的主机 JFrame 关闭时的钩子,而不使用窗口关闭事件



我意识到正式的方法是侦听添加到 JFrame 主机的窗口关闭事件,然后简单地在 JPanel 上调用清理方法。然而,我很好奇。我最近才在我的面板中获得了关闭(关闭、离开、现在离开(钩子的需求,该钩子已经在我的程序中使用。而且,出于好奇,我不想重新访问这个面板被放置在通用旧 JFrame(不需要特殊处理(中的地方,并且它只是显示为一个对话框,等待输入,然后被关闭。我不想让这个假设的其他程序员(我(去向那些已经使用我的面板的帧添加窗口侦听器,要求他们现在调用新的关闭方法。面板中的代码需要添加功能,那么为什么要让那些 Framer 都去更改他们的代码来添加窗口侦听器呢?难道一个JPanel不能自己发现这一点吗?

因此,我为JPanel做了一个黑客,以检测它何时被关闭,而JFrame不需要窗口侦听器。对我来说,JPanel 现在更有自我意识了......它有自己的窗口关闭检测策略,独立于使用我的 JPanel 需要编写侦听器和调用关闭钩子的编码器。我只是想尝试这样做,而不必返回并更改现有代码。

也意识到,我的黑客只适用于DISPOSE_ON_CLOSE,但是......无论如何,这就是我用于对话的内容。

有人可以告诉我我应该怎么做吗?有没有我可以挖掘的财产活动?这一定是我刚刚在这里所做的一种可以开火的进攻。这一定是大错特错的。然而,我有我自己意识的 JPanel 关闭钩子,它可以工作。有人请把我引导到其他地方(没有父 JFrame 使用自己的窗口关闭事件的明显选择,我想看看 JPanel 是否可以自己意识到这一点。

我的黑客工作...告诉我它有什么问题。它一定是错的,即使它对我有用。右?

以下代码片段是一个正在运行的模型。

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
public class JPanelClosing extends JPanel {
    static JFrame frame; // This frame is only here for the mmockup ... what
                         // follows after would be part of your own custom JPanel.
    private boolean formClosing = false;
    private boolean filterEvent = true;
    public JPanelClosing() {
        initComponents();
    }
    private void initComponents() {
        addPropertyChangeListener(new java.beans.PropertyChangeListener() {
            @Override
            public void propertyChange(java.beans.PropertyChangeEvent evt) {
                formPropertyChange(evt);
            }
        });
    }
    private void formPropertyChange(java.beans.PropertyChangeEvent evt) {                                    
        // This is a hack I came up with. The JPanel fires two events when
        // used in a waiting input dialog of an unkown JFrame that hosts it.
        // When the JFrame DefaultCloseOperation is set to DISPOSE_ON_CLOSE:
        // PropertyChangeEvent fires twice when it opens, and twice when it closes.
        // So, I filter out the two events to pick one, like using !valueIsAdjusting.
        // Then, I filter whether it's state one, opening, or state two, closing.
        // This is all kept track of using two field variables; filterEvent, and formClosing 
// With DISPOSE_ON_CLOSE, (on my machine) I get:
// Form opened.
// Form Closed.
        // (EXIT_ON_CLOSE and HIDE_ON_CLOSE will only produce 'Form opened')
        if (!filterEvent) {
            if ( formClosing ) {
                System.out.println("Form Closed.");
System.exit(0);
            } else {
                formClosing = true;
                System.out.println("Form opened.");
            }
            filterEvent = true;
        } else { // end if value not adjusting
            filterEvent = false;
        }
    }
    public static void main (String args[] ) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                frame = new JFrame();
                final JPanel panel = new JPanelClosing();
                frame.setContentPane(panel);
                frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
                frame.setVisible(true);
            }
        });
    }
}

我不知道这是否是规范的做事方式,但是对于 AncestorListener,如果保存组件的主窗口被调用的 ancestorAdd 方法设置为可见,您会收到通知。发生这种情况时,您可以通过SwingUtilities.getWindowAncestor(...)获取窗口祖先,然后将所需的任何侦听器添加到此窗口。例如:

包装 pkg1;

import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.*;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
@SuppressWarnings("serial")
public class TestClosing extends JPanel {
public TestClosing() {
setPreferredSize(new Dimension(500, 400));
// addPropertyChangeListener(evt -> System.out.println(evt));
addAncestorListener(new MyAncestorListener());
}

private class MyAncestorListener implements AncestorListener {
@Override
public void ancestorAdded(AncestorEvent ae) {
Window window = SwingUtilities.getWindowAncestor(TestClosing.this);
if (window != null) {
window.addWindowListener(new MyWindowListener());
}
}
@Override
public void ancestorMoved(AncestorEvent ae) {}
@Override
public void ancestorRemoved(AncestorEvent ae) {}
}
private class MyWindowListener extends WindowAdapter {
@Override
public void windowClosed(WindowEvent e) {
// TODO desired actions
System.out.println("window closed");
}
@Override
public void windowClosing(WindowEvent e) {
// TODO desired actions
System.out.println("window closing");
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui() {
TestClosing mainPanel = new TestClosing();
JFrame frame = new JFrame("Test Closing");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}

我想一个组件监听器,对componentShown(ComponentEvent ce)做出反应也可以做到这一点。

确定要做你正在做的事情吗?

首先,此代码显然不起作用。在我的系统上,"表单已关闭"从不打印。

其次,我在 istener 中捕获的是"layeredContainerLayer",当窗口打开时"ancestor"属性会发生变化。"ancestor"改变可能是你想要的,或多或少,也许?

但是,您能否确保只按该顺序触发单个"layeredContainerLayer"源事件,或者它只发生一次,或者没有其他事件被传播?即使你深入挖掘以真正了解正在发生的事情,以及以什么顺序,仍然不要这样做。这是一个丑陋的黑客。

也许实现一些依赖于Runtime.getRuntime().addShutdownHook(...)和您自己的听众的东西,通知任何想要收到关闭通知的人。

最新更新