从 JButton 显示/隐藏 JPopupMenu; FocusListener 不起作用?
我需要一个带有附加下拉式菜单的 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;
}
}
});
我一直在与此斗争现在太久了。如果有人可以告诉我这有什么问题,那就太好了!
谢谢!
代码:
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);
}
}
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:
- 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.
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.)
// 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);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
这是我刚刚提出的 Amber Shah“大黑客”建议的一个变体。如果没有 isShowingPopup 标志...
它不是防弹的,但它工作得很好,直到有人以令人难以置信的缓慢单击来关闭弹出窗口(或非常快的第二次单击来重新打开它...)。
正如我在评论中所说,这不是最优雅的解决方案,但它非常简单,并且适用于 98% 的情况。
接受建议!
Here's a variant of Amber Shah's "big hack" suggestion I just made. Without the isShowingPopup flag...
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...).
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.
Open to suggestions!
这是另一种方法,即使不优雅,也不算太糟糕,而且据我所知,它是有效的。首先,在最顶部,我添加了第二个布尔值,名为
showPopup
。FocusListener
必须如下所示:isShowingPopup
布尔值在其他任何地方都不会更改 - 如果它获得焦点,则假定它已显示,如果它失去焦点,则假定它已显示事实并非如此。接下来,按钮上的
ActionListener
有所不同:现在是真正的新部分。它是按钮上的
MouseListener
:基本上,
mousePressed
在菜单失去焦点之前被调用,因此isShowingPopup
反映了弹出窗口是否在按钮之前显示被按下。然后,如果菜单在那里,我们只需将showPopup
设置为false
,这样actionPerformed
方法在被调用后就不会显示菜单(松开鼠标后)。除了一种情况外,这在每种情况下都表现得如预期:如果菜单正在显示并且用户在按钮上按下鼠标但在按钮之外释放鼠标,则永远不会调用
actionPerformed
。这意味着showPopup
仍然为 false,并且下次按下按钮时不会显示菜单。要解决此问题,mouseReleased
方法会重置showPopup
。据我所知,mouseReleased
方法是在actionPerformed
之后调用的。我对最终的按钮进行了一些尝试,对按钮做了我能想到的所有事情,它按预期工作。然而,我不能 100% 确定事件总是以相同的顺序发生。
最终,我认为这至少值得尝试。
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
.The
FocusListener
has to be as follows: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.Next, the
ActionListener
on the button is different:Now comes the really new bit. It's a
MouseListener
on the button:Basically,
mousePressed
gets called before the menu loses focus, soisShowingPopup
reflects whether the popup was shown before the button is pressed. Then, if the menu was there, we just setshowPopup
tofalse
, so that theactionPerformed
method does not show the menu once it gets called (after the mouse is let go).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 thatshowPopup
remained false and the menu was not shown the next time the button was pressed. To fix this, themouseReleased
method resetsshowPopup
. ThemouseReleased
method gets called afteractionPerformed
, as far as I can tell.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.
您可以使用 JPopupMenu.isVisible() 而不是布尔变量来检查弹出菜单的当前状态。
You could use the JPopupMenu.isVisible() instead of your Boolean variable to check the current state of the popup menu.
您是否尝试过将
ComponentListener
添加到JPopupMenu
中,以便您知道它何时显示和隐藏(并相应地更新您的isShowingPopup
标志)?我不确定倾听焦点变化是否一定是正确的方法。Have you tried adding a
ComponentListener
to theJPopupMenu
, so that you know when it's been shown and hidden (and update yourisShowingPopup
flag accordingly)? I'm not sure listening for focus changes is necessarily the right approach.您需要的是一个 PopupMenuListener:
我将其插入您的代码中并验证它是否有效。
What you need is a PopupMenuListener:
I inserted this into your code and verified that it works.
好吧,如果没有看到您的所有代码,我无法确定,但是弹出窗口是否有可能实际上根本没有获得焦点?我之前在 Swing 中遇到过无法正确聚焦的问题,所以它可能是罪魁祸首。尝试在菜单上调用
setFocusable(true)
,然后在显示菜单时调用requestFocus()
。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 callingrequestFocus()
when you make the menu appear.我尝试了Tikhon Jelvis的答案(引入focusListener和mouseListener的智能组合)。它在 Linux (Java7/gtk) 上对我不起作用。 :-(
阅读 http:// /docs.oracle.com/javase/7/docs/api/javax/swing/JComponent.html#requestFocus%28%29 写着“请注意,不鼓励使用此方法,因为它的行为是平台性的 顺便
可能是侦听器调用的顺序随 Java7 发生变化,或者随 GTK 与 Windows 发生变化。如果您想独立于平台,我不会推荐此解决方案。
说一句:我在 stackoverflow 上创建了一个新帐户来给出此提示似乎我不允许对他的答案发表评论(因为声誉),但似乎我有一个编辑它的按钮:-)。
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). :-(
Reading http://docs.oracle.com/javase/7/docs/api/javax/swing/JComponent.html#requestFocus%28%29 there is written "Note that the use of this method is discouraged because its behavior is platform dependent."
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.
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. :-)