如何关闭JDialog并通知窗口事件监听器?

34

有没有一种方法可以通过代码关闭JDialog,同时仍然会通知窗口事件侦听器? 我已经尝试将visible设置为false和disposing,但似乎都不起作用。

有没有一种方法可以通过代码关闭JDialog,同时仍然会通知窗口事件侦听器? 我已经尝试将visible设置为false和disposing,但似乎都不起作用。
5个回答

50

关闭窗口(使用dispose())和隐藏它(使用setVisible(false))是不同的操作,并产生不同的事件——而从操作系统关闭它则是另一种产生不同事件的操作。

这三种操作都会产生windowDeactivated事件,告诉您窗口失去了焦点,但dispose()会接着产生windowClosed事件,而从操作系统关闭则会首先产生windowClosing事件。如果您想以相同方式处理它们,可以设置窗口在关闭时被释放:

window.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

一般来说,setVisible(false) 表示您可能希望再次使用窗口,因此它不会触发任何窗口事件(除了 windowDeactivated)。如果您想检测窗口的隐藏,则需要使用一个 ComponentListener

window.addComponentListener(new ComponentAdapter() {
  @Override
  public void componentHidden(ComponentEvent e) {
    System.out.println("componentHidden()");
  }
})

请注意,这基本上只适用于显式的setVisible()调用。如果您需要更普遍地检测隐藏,可以使用HierarchyListener,但这可能会带来更多麻烦。

  window.addHierarchyListener(new HierarchyListener() {
    @Override
      public void hierarchyChanged(HierarchyEvent e) {
        System.out.println("valid: " + window.isValid());
        System.out.println("showing: " + window.isShowing());
      }
  });

注意,当您销毁一个窗口时,您将收到一些 HierarchyEvent,首先是为隐藏和然后为失效,但当您使用 setVisible() 隐藏它时,它仍然有效,因此您不会收到失效。


3
谢谢David。我只是在使用dispose时听取windowClosing事件,我没有意识到只有操作系统关闭它才会产生windowClosing事件。 - Jeff Storey
我不得不自己艰难地找到答案。 :) - David Moles
好的,也许我还不理解问题。但是原帖中说他希望“关闭监听器仍然能够被通知”。我的建议是不要使用dispose方法,而是发送一个windowClosing事件给窗口。这将和用户点击“X”按钮一样。窗口将被销毁并且窗口监听器会被通知。只需要两行代码。你似乎比我更好地理解了问题,所以也许你可以测试一下我的建议,看看是否有意义。 - camickr
1
我的困惑在于我不需要在windowClosing上使用它,而是要在windowClosed上使用。我并不想拦截windowClosing事件,因此把它放在windowClosed中,无论对话框是通过隐藏和调用dispose来处理还是用户单击X关闭,我都可以获得该事件。 - Jeff Storey
camickr,发布windowClosing事件会得到一个windowClosing事件和(假设默认关闭操作不是EXIT_ON_CLOSE)一个windowDeactivated事件--正如你所说,就像用户点击了X一样。如果你只想关闭窗口并获得一些事件,那似乎并不明显比使用dispose()得到的事件更有用。它确实具有与X相匹配的优点,因此您可以以同样的方式处理X和程序化的关闭,但是您也可以通过DISPOSE_ON_CLOSE来获取(反向的)内容。在我看来,这更为简洁,但可能因人而异。 - David Moles
简而言之:使用dispose()并监听windowClosed()。 - teh_senaus

3
我似乎没有你的问题。当我使用以下代码时,windowDeactivated() 会被调用,无论是 setVisible(false) 还是 dispose(),并且 windowClosed() 也会在 dispose() 中被调用。

ClosingDialog.java:

public class ClosingDialog extends JDialog {
    public ClosingDialog(Frame owner, String title, boolean modal) {
        super(owner, title, modal);
        JPanel contentPanel = (JPanel) this.getContentPane();

        JButton setVisButton = new JButton("setVisible( false )");
        setVisButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                ClosingDialog.this.setVisible(false);
            }
        });

        JButton disposeButton = new JButton("dispose()");
        disposeButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                ClosingDialog.this.dispose();
            }
        });

        contentPanel.setLayout(new FlowLayout());

        contentPanel.add(setVisButton);
        contentPanel.add(disposeButton);

        this.addWindowListener(new WindowListener() {
            public void windowActivated(WindowEvent e) {
                System.out.println("windowActivated");
            }

            public void windowClosed(WindowEvent e) {
                System.out.println("windowClosed");
            }

            public void windowClosing(WindowEvent e) {
                System.out.println("windowClosing");
            }

            public void windowDeactivated(WindowEvent e) {
                System.out.println("windowDeactivated");
            }

            public void windowDeiconified(WindowEvent e) {
                System.out.println("windowDeiconified");
            }

            public void windowIconified(WindowEvent e) {
                System.out.println("windowIconified");
            }

            public void windowOpened(WindowEvent e) {
                System.out.println("windowOpened");
            }
        });

        this.setSize(300, 300);
    }
}

2
所有的windowDeactivated表示的是窗口失去了焦点。你并不能确定这个窗口是否真的在关闭。一般来说,WindowListeners会监听窗口关闭事件,以便在窗口关闭时执行一些特殊处理。 - camickr
是的,我尝试使用windowClosing并没有收到那个事件。 - Jeff Storey
这就是为什么我5个小时前给出了我的解决方案。 - camickr
1
错误:对disposeButton的操作应该调用ClosingDialog.this.dispose(),而不是再次调用setVisible(false)。如果你这样做,你会得到一个windowDeactivated和一个windowClosed事件--至少我是这样的。windowClosing事件通常用于通过操作系统控制(例如窗口的红色X)来关闭窗口。 - David Moles
谢谢,David。完全错过了! - Joseph Gordon

1

向窗口发送窗口关闭事件。请查看关闭应用程序条目中的ExitAction示例。


3
如果你不给出否定投票的理由,人们如何学习任何东西? - camickr
3
不是我给它投了反对票,但也许是因为你似乎并没有尝试理解Jeff的问题,而只是在你最有可能相关的现有博客文章中找到了链接。你发表了两个抱怨关于Jeff不喜欢你的回答,却没有进一步尝试找出为什么对他不起作用,这使得答案本身看起来更不吸引人。 - David Moles
我同意我可能有些不太清楚,但是我的评论表明我已经多次查看此内容,以确保我理解问题。我的其他评论旨在让Jeff重新审视我的建议。我仍然坚信我有一个一行代码的解决方案,可以创建非显示面板的图像。 - camickr
在我看来,对原问题提出问题(或提出替代假设)比仅仅更加努力地研究它和/或暗示发帖者没有认真看待你的答案更为有效。你似乎也喜欢发布评论(像这样的评论)抱怨发帖者未能欣赏你对其他问题的回答,我不确定这会帮助谁。但是,嘿,你的声望比我的高40%,回答数量只多28%,因此显然负评不能太让你沮丧。 - David Moles
2
我的经验是人们喜欢被“喂筷子”式地给出答案。因此,当提供“发布代码”和“跟随链接并进行一些阅读”的选项时,前者通常会被选择。因此,我的评论旨在促使发布者实际阅读链接。顺便说一下,我的建议在使用DISPOSE_ON_CLOSE时会生成windowClosing和WindowClosed事件。 - camickr

0

我想从代码中触发一个windowClosing事件(就像用户点击X一样),因为我在JDialog中有一个额外的关闭按钮,并且希望当单击X和单击按钮时运行相同的WindowListener(我使用WindowAdapter实现) 。仅运行dispose()只会触发windowClosed,而不是windowClosing,我希望在窗口关闭之前出现确认消息。我还没有成功通过JDialog的processWindowEvent方法触发windowClosing,因为它是受保护的。

以下是我如何使其工作的:

WindowAdapter adapter = (WindowAdapter)jdialog.getWindowListeners()[0];
adapter.windowClosing(new WindowEvent((Window)jdialog, WindowEvent.WINDOW_CLOSING));

希望这能帮助到某个人。

0

未经测试的建议:

您尝试过使用getWindowListeners(),然后迭代每个WindowListener来触发windowClosed()吗?

编辑:上述建议是错误的。为了纪念而保留。

恐怕在我的简单示例中调用dialog.dispose()对我很有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接