【问题标题】:Obtaining a hook for when a JPanel's host JFrame closes, without using a Window Closing Event获取 JPanel 的主机 JFrame 关闭时的钩子,而不使用窗口关闭事件
【发布时间】:2020-05-27 20:33:21
【问题描述】:

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

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

也意识到,我的 hack 仅适用于 DISPOSE_ON_CLOSE,但是……这就是我在对话中使用的方法。

有人可以告诉我应该怎么做吗?有我可以挖掘的财产事件吗?这必须是我刚刚在这里所做的一次可以开火的进攻。这一定是非常错误的。然而,我有我自己的 JPanel 关闭钩子,它可以工作。有人请引导我到别处(没有明显选择父 JFrame 使用它自己的窗口关闭事件,我想看看 JPanel 是否可以自己意识到这一点。)

我的 hack 有效……告诉我它有什么问题。它一定是错的,即使它对我有用。对吧?

下面的 sn-p 是一个正在运行的模型。

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);
            }
        });
    }
}

【问题讨论】:

  • 使用 AncestorEvent 怎么样?
  • 谢谢,我会检查的。它说它可以检测到: ANCESTOR_REMOVED 祖先组件已从可见对象的层次结构中删除(隐藏)并且不再显示。也许这会奏效。如果我发现这更正式,我会使用它并回帖。

标签: java swing


【解决方案1】:

我不知道这是否是规范的做事方式,但是使用 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) 做出反应也可以解决问题。

【讨论】:

  • 谢谢。我会玩这个。显然,只监听框架上的窗口关闭事件是最直接的选择。我只是想知道其他人是否知道 JPanel 可以做到这一点,同时只更改它自己的源代码。你的方式在我的机器上产生了结果,感谢你和其他人一样,引导我参加 AncestorEvent。以前没用过,下面是我的介绍。
  • 有趣的反馈:你的方式,我更信任,得到了与我类似的结果,因为只有 DISPOSE_ON_CLOSE 会导致 windowClosed 和 windowClosing 一起调用。 HIDE 和 EXIT 只触发 windowClosing,从不调用 windowClosed。我的行为方式相同,仅适用于 DISPOSE_ON_CLOSE 场景的附加行为。有道理......退出只是离开,隐藏永远不会离开......所以两者都只产生关闭'。但是,Dispose 确实离开了,但系统还没有退出,所以我们可以看到关闭的事件发生了。但我的 hack 也以同样的方式行事。有趣的。 :)
【解决方案2】:

确定你想做你正在做的事吗?

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

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

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

也许实现一些依赖于Runtime.getRuntime().addShutdownHook(...) 和你自己的监听器来通知任何想要被通知关闭的人。

【讨论】:

  • 我知道这是一个丑陋的黑客。这就是我问方向的原因。我想我只会添加窗口侦听器。我想如果 JPanel 有办法可靠地知道这一点,我们会在库中看到它的调用。这就是我问的原因。我只是将窗口关闭事件添加到框架并在面板上调用清理方法。新要求。感谢您的建议。
猜你喜欢
  • 2015-06-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多