java 从 JButton 显示/隐藏 JPopupMenu;FocusListener 不工作?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2421914/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Showing/hiding a JPopupMenu from a JButton; FocusListener not working?
提问by Joanis
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:
我需要一个带有下拉式菜单的 JButton。所以我拿了一个 JPopupMenu 并将它附加到 JButton 上,您可以在下面的代码中看到它。它需要做的是:
- show the popup when clicked
- hide it if clicked a second time
- hide it if an item is selected in the popup
- hide it if the user clicks somewhere else in the screen
- 单击时显示弹出窗口
- 如果第二次点击则隐藏它
- 如果在弹出窗口中选择了一个项目,则隐藏它
- 如果用户点击屏幕上的其他地方,则隐藏它
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.
这 4 件事有效,但由于我使用的布尔标志,如果用户单击其他地方或选择一个项目,我必须在按钮上单击两次才能再次显示。这就是为什么我尝试添加一个 FocusListener(绝对没有响应)来解决这个问题,并在这些情况下将标志设置为 false。
EDIT: Last attempt in an answer post...
编辑:最后一次尝试回答帖子......
Here are the listeners: (It's in a class extending JButton, so the second listener is on the JButton.)
下面是监听器:(它在一个扩展 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;
}
}
});
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!
我已经与这个斗争了太久了。如果有人能给我一个关于这有什么问题的线索,那就太好了!
Thanks!
谢谢!
Code:
代码:
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);
}
}
采纳答案by Tikhon Jelvis
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.
这是另一种方法,即使不是很优雅,也不是太糟糕,据我所知,它是有效的。首先,在最顶部,我添加了第二个名为showPopup.
The FocusListenerhas to be as follows:
在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;
}
});
The isShowingPopupboolean 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.
该isShowingPopup布尔没有得到改变其他地方-如果它获得焦点,它假定它表示,如果它失去焦点,它假定它不是。
Next, the ActionListeneron the button is different:
接下来,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;
}
}
});
Now comes the really new bit. It's a MouseListeneron the button:
现在来了真正的新东西。这是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;
}
});
Basically, mousePressedgets called before the menu loses focus, so isShowingPopupreflects whether the popup was shown before the button is pressed. Then, if the menu was there, we just set showPopupto false, so that the actionPerformedmethod does not show the menu once it gets called (after the mouse is let go).
基本上,mousePressed在菜单失去焦点之前被调用,因此isShowingPopup反映了在按下按钮之前是否显示了弹出窗口。然后,如果菜单在那里,我们只是设置showPopup为false,以便该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, actionPerformedwas never called. This meant that showPopupremained false and the menu was not shown the next time the button was pressed. To fix this, the mouseReleasedmethod resets showPopup. The mouseReleasedmethod gets called after actionPerformed, as far as I can tell.
除了一种情况外,这在所有情况下都符合预期:如果菜单正在显示并且用户在按钮上按下鼠标但在按钮之外释放它,actionPerformed则永远不会调用。这意味着它showPopup仍然是错误的,并且下次按下按钮时不会显示菜单。为了解决这个问题,该mouseReleased方法会重置showPopup。据我所知,该mouseReleased方法在 之后被调用actionPerformed。
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.
我玩了一会儿结果按钮,对按钮做了我能想到的所有事情,它按预期工作。但是,我不能 100% 确定事件总是以相同的顺序发生。
Ultimately, I think this is, at least, worth trying.
最终,我认为这至少值得一试。
回答by Joanis
Here's a variant of Amber Shah's "big hack" suggestion I just made. Without the isShowingPopup flag...
这是我刚刚提出的 Amber Shah 的“大黑客”建议的变体。没有 isShowingPopup 标志...
It's not bulletproof, but it works quite well until someone comes in with an incredibly slow click to close the popup (or a very fast second click to reopen it...).
它不是防弹的,但它工作得很好,直到有人用非常缓慢的点击关闭弹出窗口(或非常快的第二次点击以重新打开它......)。
public class Button extends JButton {
// Icon.
private static final ImageIcon ARROW_SOUTH = new ImageIcon("ArrowSouth.png");
// Popup menu.
private final JPopupMenu menu;
// Last time the popup closed.
private long timeLastShown = 0;
public Button(int height) {
super(ARROW_SOUTH);
menu = new JPopupMenu(); // Populated somewhere else.
// Show and hide popup on left click.
menu.addPopupMenuListener(new PopupMenuListener() {
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent arg0) {
timeLastShown = System.currentTimeMillis();
}
@Override public void popupMenuWillBecomeVisible(PopupMenuEvent arg0) {}
@Override public void popupMenuCanceled(PopupMenuEvent arg0) {}
});
addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if ((System.currentTimeMillis() - timeLastShown) > 300) {
Component c = (Component) e.getSource();
menu.show(c, -1, c.getHeight());
}
}
});
// Skip when navigating with TAB.
setFocusable(false);
}
}
As I said in comments, that's not the most elegant solution, but it's horribly simple and it works in 98% of the cases.
正如我在评论中所说,这不是最优雅的解决方案,但它非常简单,并且在 98% 的情况下都有效。
Open to suggestions!
打开建议!
回答by Chad Okere
You could use the JPopupMenu.isVisible()instead of your Boolean variable to check the current state of the popup menu.
您可以使用JPopupMenu.isVisible()而不是您的布尔变量来检查弹出菜单的当前状态。
回答by Ash
Have you tried adding a ComponentListenerto the JPopupMenu, so that you know when it's been shown and hidden (and update your isShowingPopupflag accordingly)? I'm not sure listening for focus changes is necessarily the right approach.
您是否尝试添加ComponentListener到JPopupMenu,让你知道什么时候它已经显示和隐藏(和更新isShowingPopup相应的标志)?我不确定倾听焦点变化是否一定是正确的方法。
回答by Amber Shah
What you need is a PopupMenuListener:
你需要的是一个 PopupMenuListener:
menu.addPopupMenuListener(new PopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent arg0) {
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent arg0) {
System.out.println("MENU INVIS");
isShowingPopup = false;
}
@Override
public void popupMenuCanceled(PopupMenuEvent arg0) {
System.out.println("MENU CANCELLED");
isShowingPopup = false;
}
});
I inserted this into your code and verified that it works.
我将其插入到您的代码中并验证它是否有效。
回答by Tikhon Jelvis
Well, I can't be sure without seeing all of your code, but is it possible that the popup never actually gets focus at all? I've had problems with things' not getting focus properly in Swing before, so it could be the culprit. Try calling setFocusable(true)on the menu and then calling requestFocus()when you make the menu appear.
好吧,如果没有看到您的所有代码,我无法确定,但是弹出窗口是否可能根本没有真正获得焦点?我之前在 Swing 中遇到过无法正确聚焦的问题,所以这可能是罪魁祸首。尝试调用setFocusable(true)菜单,然后requestFocus()在出现菜单时调用。
回答by tbayen
I tried the Answer of Tikhon Jelvis (introducing a smart combination of focusListener and mouseListener). It does not work for me on Linux (Java7/gtk). :-(
我尝试了 Tikhon Jelvis 的答案(引入了 focusListener 和 mouseListener 的智能组合)。它在 Linux (Java7/gtk) 上对我不起作用。:-(
Reading http://docs.oracle.com/javase/7/docs/api/javax/swing/JComponent.html#requestFocus%28%29there is written "Note that the use of this method is discouraged because its behavior is platform dependent."
阅读http://docs.oracle.com/javase/7/docs/api/javax/swing/JComponent.html#requestFocus%28%29有写“请注意,不鼓励使用此方法,因为它的行为是平台依赖。”
It may be that the order of listener calls changed with Java7 or it changes with GTK vs Windows. I would not recommend this solution if you want to be platform independent.
可能是因为 Java7 的侦听器调用顺序发生了变化,或者随 GTK 与 Windows 发生了变化。如果您想独立于平台,我不会推荐此解决方案。
BTW: I created a new account on stackoverflow to give this hint. It seems I am not allowed to comment to his answer (because of reputation). But it seems I have a button to edit it. This stackoverflow is a very funny thing. :-)
顺便说一句:我在 stackoverflow 上创建了一个新帐户来给出这个提示。似乎我不允许对他的回答发表评论(因为声誉)。但似乎我有一个按钮可以编辑它。这个stackoverflow是一件非常有趣的事情。:-)

