从JButton显示/隐藏JPopupMenu; FocusListener无法正常工作? [英] Showing/hiding a JPopupMenu from a JButton; FocusListener not working?

查看:91
本文介绍了从JButton显示/隐藏JPopupMenu; FocusListener无法正常工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要一个带有附加的下拉菜单的JButton.因此,我采用了JPopupMenu并将其附加到JButton上,就像在下面的代码中看到的那样.它需要做的是:

I needed a JButton with an attached dropdown style menu. So I took a JPopupMenu and attached it to the JButton in the way you can see in the code below. What it needs to do is this:

  • 单击时显示弹出窗口
  • 如果第二次单击,则将其隐藏
  • 如果在弹出窗口中选择了一项,则将其隐藏
  • 如果用户单击屏幕上的其他位置,则隐藏它

这4种方法都能正常工作,但是由于我使用的是boolean标志,如果用户单击其他位置或选择了一个项目,则必须在该按钮上单击两次,然后它才能再次显示.这就是为什么我尝试添加一个FocusListener(它绝对没有响应)来解决此问题,并在这种情况下将标志设置为false.

These 4 things work, but because of the boolean flag I'm using, if the user clicks somewhere else or selects an item, I have to click twice on the button before it shows up again. That's why I tried to add a FocusListener (which is absolutely not responding) to fix that and set the flag false in these cases.

答案中的最后尝试...

以下是侦听器:(它在扩展JButton的类中,因此第二个侦听器在JButton上.)

Here are the listeners: (It's in a class extending JButton, so the second listener is on the JButton.)

// Show popup on left click.
menu.addFocusListener(new FocusListener() {
 @Override
 public void focusLost(FocusEvent e) {
  System.out.println("LOST FOCUS");
  isShowingPopup = false;
 }

 @Override
 public void focusGained(FocusEvent e) {
  System.out.println("GAINED FOCUS");
 }
});

addActionListener(new ActionListener() {
 @Override
 public void actionPerformed(ActionEvent e) {
  System.out.println("isShowingPopup: " + isShowingPopup);
  if (isShowingPopup) {
   isShowingPopup = false;
  } else {
   Component c = (Component) e.getSource();
   menu.show(c, -1, c.getHeight());
   isShowingPopup = true;
  }
 }
});

我与这个问题进行斗争已经太久了.如果有人可以告诉我这是怎么回事,那太好了!

I've been fighting with this for way too long now. If someone can give me a clue about what's wrong with this, it would be great!

谢谢!

代码:

public class Button extends JButton {

    // Icon.
    private static final ImageIcon ARROW_SOUTH = new ImageIcon("ArrowSouth.png");

    // Unit popup menu.
    private final JPopupMenu menu;

    // Is the popup showing or not?
    private boolean isShowingPopup = false;

    public Button(int height) {
        super(ARROW_SOUTH);
        menu = new JPopupMenu(); // menu is populated somewhere else

        // FocusListener on the JPopupMenu
        menu.addFocusListener(new FocusListener() {
            @Override
            public void focusLost(FocusEvent e) {
                System.out.println("LOST FOCUS");
                isShowingPopup = false;
            }

            @Override
            public void focusGained(FocusEvent e) {
                System.out.println("GAINED FOCUS");
            }
        });

        // ComponentListener on the JPopupMenu
        menu.addComponentListener(new ComponentListener() {
            @Override
            public void componentShown(ComponentEvent e) {
                System.out.println("SHOWN");
            }

            @Override
            public void componentResized(ComponentEvent e) {
                System.out.println("RESIZED");
            }

            @Override
            public void componentMoved(ComponentEvent e) {
                System.out.println("MOVED");
            }

            @Override
            public void componentHidden(ComponentEvent e) {
                System.out.println("HIDDEN");
            }
        });

        // ActionListener on the JButton
        addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("isShowingPopup: " + isShowingPopup);
                if (isShowingPopup) {
                    menu.requestFocus();
                    isShowingPopup = false;
                } else {
                    Component c = (Component) e.getSource();
                    menu.show(c, -1, c.getHeight());
                    isShowingPopup = true;
                }
            }
        });

        // Skip when navigating with TAB.
        setFocusable(true); // Was false first and should be false in the end.

        menu.setFocusable(true);
    }

}

推荐答案

这是另一种方法,即使不是很优雅,也算是不错的做法,据我所知,它是可行的.首先,在最顶部,我添加了另一个名为showPopup的布尔值.

Here is another approach which is not too bad of a hack, if not elegant, and which, as far as I could tell, works. First, at the very top, I added a second boolean called showPopup.

FocusListener必须如下所示:

    menu.addFocusListener(new FocusListener() {
        @Override
        public void focusLost(FocusEvent e) {
            System.out.println("LOST FOCUS");
            isShowingPopup = false;
        }

        @Override
        public void focusGained(FocusEvent e) {
            System.out.println("GAINED FOCUS");
            isShowingPopup = true;
        }
    });

isShowingPopup布尔值在其他任何地方都不会更改-如果它获得焦点,则假定它已显示,如果它失去焦点,则假定它没有.

The isShowingPopup boolean does not get changed anywhere else--if it gains focus, it assumes it's shown and if it loses focus, it assumes it isn't.

接下来,按钮上的ActionListener不同:

Next, the ActionListener on the button is different:

   addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("isShowingPopup: " + isShowingPopup);
            if (showPopup) {
                Component c = (Component) e.getSource();
                menu.show(c, -1, c.getHeight());
                menu.requestFocus();
            } else {
                showPopup = true;
            }
        }
    });

现在是真正新的东西.按钮上的MouseListener

Now comes the really new bit. It's a MouseListener on the button:

    addMouseListener(new MouseAdapter() {
        @Override
        public void mousePressed(MouseEvent e) {
            System.out.println("ispopup?: " + isShowingPopup);
            if (isShowingPopup) {
                showPopup = false;
            }
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            showPopup = true;
        }
    });

基本上,mousePressed在菜单失去焦点之前被调用,因此isShowingPopup反映是否在按下按钮之前显示了弹出窗口.然后,如果菜单在那里,我们只需将showPopup设置为false,以使actionPerformed方法在调用菜单后(放开鼠标后)不显示菜单.

Basically, mousePressed gets called before the menu loses focus, so isShowingPopup reflects whether the popup was shown before the button is pressed. Then, if the menu was there, we just set showPopup to false, so that the actionPerformed method does not show the menu once it gets called (after the mouse is let go).

在每种情况下,它的行为均符合预期,但有一种情况:如果显示菜单,并且用户在按钮上按下鼠标但将其释放到按钮之外,则永远不会调用actionPerformed.这意味着showPopup保持为false,并且下次按下按钮时不显示菜单.要解决此问题,mouseReleased方法将重置showPopup.据我所知,mouseReleased方法在actionPerformed之后被调用.

This behaved as expected in every case but one: if the menu was showing and the user pressed the mouse on the button but released it outside of it, actionPerformed was never called. This meant that showPopup remained false and the menu was not shown the next time the button was pressed. To fix this, the mouseReleased method resets showPopup. The mouseReleased method gets called after actionPerformed, as far as I can tell.

我在结果按钮上玩了一会儿,对按钮做了所有我能想到的事情,并且按预期工作.但是,我不确定100%是否一定会以相同的顺序发生这些事件.

I played around with the resulting button for a bit, doing all the things I could think of to the button, and it worked as expected. However, I am not 100% sure that the events will always happen in the same order.

最终,我认为这至少值得一试.

Ultimately, I think this is, at least, worth trying.

这篇关于从JButton显示/隐藏JPopupMenu; FocusListener无法正常工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆