从JButton显示/隐藏JPopupMenu; FocusListener无法正常工作?

Joa*_*nis 8 java swing jbutton jpopupmenu

我需要一个带有附加下拉样式菜单的JButton.所以我拿了一个JPopupMenu并以你在下面的代码中看到的方式将它附加到JButton.它需要做的是:

  • 单击时显示弹出窗口
  • 如果再次点击则隐藏它
  • 如果在弹出窗口中选择了某个项目,则隐藏它
  • 如果用户点击屏幕中的其他位置,则隐藏它

这4周工作上的事情,但我使用,因为布尔标志,如果用户点击了其他位置或选择一个项目,我必须在按钮上点击两次才能再次之前.这就是为什么我尝试添加一个FocusListener(绝对没有响应)来修复它并在这些情况下将标志设置为false.

编辑:最后一次尝试回答帖子......

以下是监听器:(它在一个扩展JButton的类中,所以第二个监听器在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;
  }
 }
});
Run Code Online (Sandbox Code Playgroud)

我现在已经用这个太久了.如果有人能给我一个关于这个问题的线索,那就太好了!

谢谢!

码:

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

}
Run Code Online (Sandbox Code Playgroud)

Tik*_*vis 1

这是另一种方法,即使不优雅,也不算太糟糕,而且据我所知,它是有效的。首先,在最顶部,我添加了第二个布尔值,名为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;
        }
    });
Run Code Online (Sandbox Code Playgroud)

布尔isShowingPopup值在其他任何地方都不会改变——如果它获得焦点,它假设它已显示,如果它失去焦点,它假设它没有显示。

接下来,ActionListener按钮上的内容有所不同:

   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;
            }
        }
    });
Run Code Online (Sandbox Code Playgroud)

现在真正的新内容来了。这是MouseListener按钮上的:

    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;
        }
    });
Run Code Online (Sandbox Code Playgroud)

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

除了一种情况外,这在每种情况下都表现得如预期:如果菜单正在显示并且用户在按钮上按下鼠标但在按钮之外释放鼠标,则actionPerformed永远不会被调用。这意味着showPopup仍然是错误的,并且下次按下按钮时不会显示菜单。为了解决这个问题,该mouseReleased方法重置showPopup。据我所知,该mouseReleased方法是在 之后调用的。actionPerformed

我对最终的按钮进行了一些尝试,对按钮做了我能想到的所有事情,并且它按预期工作。然而,我不能 100% 确定事件总是以相同的顺序发生。

最终,我认为这至少值得尝试。