如何防止在单击 JPopupMenu 外部关闭时触发其他事件?

6
我希望能用JPopupMenu复制右键菜单的一些属性:
  1. 当菜单打开时,如果您单击其他地方,菜单会关闭。
  2. 当菜单打开时,如果您单击其他地方,不会发生其他事件。
我已经成功实现了第一个部分。但是当我单击其他地方时,可能会发生其他事件。例如,假设我有一个按钮A,它执行某个操作B。当前,如果JPopupMenu打开,并且我单击A,则JPopupMenu将关闭并执行B。我希望JPopupMenu关闭而不执行B。这可行吗?
谢谢

1
我不明白为什么你不希望用户单击按钮时触发按钮事件,但每当JPopupMenu事件被触发时,禁用按钮点击事件,然后在菜单关闭时重新启用该事件。 - Hunter McMillen
@BCarpe,这是一个简单的“检查-然后-执行”的情况吗?当打开JPopupMenu时设置一个标志,然后在您的actionPerformed方法中进行检查,只有在未设置标志时才执行它们的例程。 - mre
1
@BCarpe 如果我在浏览器中右键单击,然后点击不同于当前所在选项卡的选项卡,则会选择新选项卡。(Chrome) - Hunter McMillen
@Hunter:@BCarpe:我可以确认Firefox也会这样做。如果你点击链接,它也会起作用。 - unholysampler
@Hunter 嗯... 当我使用 Chrome (@unholysampler 或 Firefox) 时,我得到的不是这样的结果... 但我在使用 Ubuntu,如果你得到的是这样的结果,那么它可能并不完全符合惯例。也许我会把它留在原地,让用户点击空白区域来关闭弹出菜单... - BCarpe
显示剩余2条评论
2个回答

11

这个方法可行,而且简单易懂...但是某些外观和感觉可能会覆盖它。

UIManager.put("PopupMenu.consumeEventOnClose", Boolean.TRUE);

值得注意的是,这只消耗了MOUSE_PRESSED事件,而随后的MOUSE_CLICKED事件不会被消耗。您可以通过在mousePressed()中设置标记并在mouseReleased()中进行测试来模拟鼠标单击。如果初始鼠标按下被消耗,则在mouseReleased()中该标记将不会被设置。

private boolean pressed = false;

@Override
public void mousePressed(MouseEvent e) {
    pressed = true;
}

@Override
public void mouseReleased(MouseEvent e) {
    if (pressed) {
         // do click stuff
    }
    pressed = false;
}

这对我很有用,可以禁用这种行为 :) 当弹出菜单已经打开时,它会导致后续的右键单击问题,显示新菜单但不选择行。 - Thomas W
我有完全相反的问题。当我检查它时,这个值是true,所以我将它设置为false(并且再次确认它保持为false),但我的事件仍然被消费。 - Mark Jeronimus

4
考虑到您在问题和评论中所说的内容,我将以以下方式解决您的问题。
从技术上讲,您有两个选择:
1.每当用户将鼠标移动到弹出窗口之外时隐藏弹出窗口。这样,您就不会遇到用户单击的问题,因为弹出窗口会自动消失。
2.全局捕获此特定鼠标事件,并在左键单击时消耗该事件(如果弹出窗口可见)。我在下面的示例中展示了这种解决方案。
    import java.awt.AWTEvent;
    import java.awt.Toolkit;
    import java.awt.event.AWTEventListener;
    import java.awt.event.ActionEvent;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import javax.swing.AbstractAction;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JMenu;
    import javax.swing.JPanel;
    import javax.swing.JPopupMenu;
    import javax.swing.SwingUtilities;

    public class DisableClickWhenPopupVisibleTest
    {
        public static void main(String[] args)
        {
            SwingUtilities.invokeLater(new Runnable()
            {
                @Override
                public void run()
                {               
                    final JPopupMenu popup = new JPopupMenu();
                    popup.add(new JMenu("aAaa"));
                    JPanel contentPane = new JPanel();
                    contentPane.add(popup);
                    JButton b = new JButton();
                    b.setAction(new AbstractAction("Button")
                    {
                        private static final long serialVersionUID = 1L;
                        @Override
                        public void actionPerformed(ActionEvent e)
                        {
                            System.out.println("b actionPerformed");
                        }
                    });
                    contentPane.add(b);
                    contentPane.addMouseListener(new MouseAdapter() {
                        @Override
                        public void mousePressed(MouseEvent e)
                        {
                            showPopup(e);
                        }
                        @Override
                        public void mouseReleased(MouseEvent e)
                        {
                            showPopup(e);
                        }
                        private void showPopup(MouseEvent e)
                        {
                            if(e.isPopupTrigger())
                                popup.show(e.getComponent(), e.getX(), e.getY());
                        }
                    });
                    //use global mouse event capture to disable left click on anything when popup is visible
                    Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
                        @Override
                        public void eventDispatched(AWTEvent event)
                        {
                            MouseEvent me = (MouseEvent)event;
                            if(me.getID() == MouseEvent.MOUSE_PRESSED)
                            {
                                System.out.println("eventDispatched popup.vis="+popup.isVisible());
                                if( me.getButton() == MouseEvent.BUTTON3)
                                {   
                                    System.out.println("BUTTON3");
                                }   
                                else if(me.getButton() == MouseEvent.BUTTON1)
                                {
                                    System.out.println("BUTTON1");
                                    if(popup.isVisible())
                                        me.consume();
                                }
                            }
                        }
                    }, AWTEvent.MOUSE_EVENT_MASK);                      
                    JFrame f = new JFrame();
                    f.setContentPane(contentPane);
                    f.setSize(400, 300);
                    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    f.setVisible(true);
                }
            });
        }
    }

你可以右键轻微点击按钮左侧,然后弹出窗口将显示。然后,如果你在按钮上点击,它的动作将不会被调用。只有在隐藏弹出窗口时,动作才会正常调用。这个功能由以下代码行提供: Toolkit.getDefaultToolkit().addAWTEventListener(...)。你可以注释掉这一行,观察到动作会在任何情况下发生,就像你目前经历的一样。

谢谢!是的,全局监听器正是我正在寻找的。 - BCarpe

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