首页 > 解决方案 > 获取 JPanel 的主机 JFrame 关闭时的钩子,而不使用窗口关闭事件

问题描述

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

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

也意识到,我的 hack 只适用于 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);
            }
        });
    }
}

标签: javaswing

解决方案


我不知道这是否是规范的做事方式,但是使用 AncestorListener,如果包含您的组件的主窗口被调用的祖先添加方法设置为可见,您会收到通知。发生这种情况时,您可以通过获取窗口祖先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);
    }
}

我想一个 ComponentListener,对它做出反应componentShown(ComponentEvent ce)也可以做到这一点。


推荐阅读