为什么我的JPopupMenu没有调用componentHidden方法?

5

当我的JPopupMenu被隐藏时,无论是因为选择了一个项目、菜单被取消或者调用了setVisible(false),我都希望得到通知。这是我的测试代码:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class A extends ComponentAdapter implements Runnable, ActionListener {
    private JButton b;

    public static void main(String[] args) {
        EventQueue.invokeLater(new A());
    }

    public void run() {
        JFrame f = new JFrame("Test");
        b = new JButton("Click me");
        b.addActionListener(this);
        f.add(b);
        f.pack();
        f.setVisible(true);
    }

    public void actionPerformed(ActionEvent e) {
        JPopupMenu pm = new JPopupMenu();
        pm.addComponentListener(this);
        pm.add("Popup...");
        pm.add("...menu!");
        pm.show(b, 10, 10);
    }

    public void componentShown(ComponentEvent e) { System.out.println("componentShown"); }
    public void componentHidden(ComponentEvent e) { System.out.println("componentHidden"); }
}

无论我如何与菜单交互,两个 ComponentListener 方法都没有被调用。为什么呢?有没有不同/更好/正确的方法来找出我的 JPopupMenu 何时隐藏?
谢谢, Cameron

2
相关问题(@heycam,其中的一些讨论可能会有用):https://dev59.com/ikzSa4cB1Zd3GeqPnH5y - Ash
1
正如@Ash所建议的那样,可以查看PopupMenuListener - trashgod
太好了,谢谢Ash和trashgod。我不知道我怎么会忽略PopupMenuListeners!虽然ComponentListeners对于JPopupMenus没有被调用还是有点有趣的。 - heycam
我认为专门使用 PopupMenuListener 的唯一原因是与普通的 ComponentListener 不匹配。我总是对实现 EventListener 的大量类感到敬畏: file:///Users/Shared/javadoc/jdk6/api/java/util/EventListener.html - trashgod
@Ash: 我同意,特别是对于监听器来说。它可能会导致反模式,然后必须根据事件源的不同而有一个大的if-block,而每个源都有一个不同的监听器实例更加适合。但对于SSCCE来说,这是完美的。 - Mark Peters
显示剩余3条评论
1个回答

6

JPopupMenu有一个特殊的监听器用于可见性变化事件:

pm.addPopupMenuListener(new PopupMenuListener() {
    @Override
    public void popupMenuCanceled(PopupMenuEvent e) {
        System.out.println("cancelled");
    }

    @Override
    public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
        System.out.println("vanishing");
    }

    @Override
    public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
        System.out.println("appearing");
    }
});

需要注意的是,正如方法名称所示,它们在可见性更改之前被调用,因此,如果您在事件处理程序中的某个地方调用了 isVisible(),您应该意识到这一点,例如:

@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
    updateMenu();
}

private void updateMenu() {
    if (!menu.isVisible()) { // this won't work!
        // perform some updates
    }
}

关于为什么 ComponentListener 在菜单消失时没有发送事件,可能是因为这个原因:

只有在调用组件的 setVisible 方法时,才会触发 component-hidden 和 component-shown 事件。例如,窗口可能会被最小化成图标(iconified),但不会触发 component-hidden 事件。

来源:ComponentListener 教程(也许不是官方的,但出自权威之口)。

结合 JPopupMenu 的 setVisible 实现考虑:

    public void setVisible(boolean b) {
        // Not supported for MenuComponents
    }

你可能知道它是如何发生的,但不知道为什么会发生(有什么理由和在哪里可以找到适当的文档记录?)


如果他们的评论是答案,我会接受其中之一。我将保持问题开放,看看是否有人可以告诉我为什么 ComponentListeners 不适用于 JPopupMenus。 - heycam
感谢指向教程的指针。我刚刚检查了JDK源代码,JPopUpMenu确实有一个已实现的setVisible(boolean)方法。(我也使用它来dismiss我的菜单,使用setVisible(false)。)从代码中可以看到,至少“visible”属性更改事件将被分派。此外,show(Component,int,int)在最后调用setVisible(true)。鉴于此,我想不出任何理由不调用componentHidden。 - heycam
深入挖掘,Component.setVisible(boolean) 将分派组件的显示/隐藏事件。JPopupMenu.setVisible(boolean) 覆盖了它,不调用 super.setVisible() 或明确分派事件。 - heycam
抱歉,我说错了,我本来想说一件事,但看着另一件事。我的编辑应用于PopupMenu,但原则对于JPopupMenu也是一样的。是的,我想表达的是该代码没有过滤到对Component.setVisible的调用。 - Mark Peters

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