我需要一个带有附加下拉样式菜单的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);
}
}
答案 0 :(得分:3)
以下是Amber Shah的“大黑客”建议的变体。没有isShowingPopup标志......
它不是防弹的,但它可以很好地工作,直到有人用一个非常慢的点击来关闭弹出窗口(或者非常快速的第二次点击以重新打开它......)。
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);
}
}
正如我在评论中所说,这不是最优雅的解决方案,但它非常简单,98%的情况下都有效。
向建议开放!
答案 1 :(得分:1)
您可以使用JPopupMenu.isVisible()代替布尔变量来检查弹出菜单的当前状态。
答案 2 :(得分:1)
您是否尝试将ComponentListener
添加到JPopupMenu
,以便了解它何时被显示和隐藏(并相应地更新isShowingPopup
标志)?我不确定倾听焦点变化必然是正确的方法。
答案 3 :(得分:1)
你需要的是一个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;
}
});
我已将其插入您的代码并验证其有效。
答案 4 :(得分: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;
}
});
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;
}
}
});
现在真的是新的一点。它是按钮上的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;
}
});
基本上,mousePressed
在菜单失去焦点之前被调用,因此isShowingPopup
反映在按下按钮之前是否显示了弹出窗口。然后,如果菜单在那里,我们只需将showPopup
设置为false
,这样actionPerformed
方法一旦调用就不会显示菜单(在放开鼠标后)。
在每种情况下都表现如预期的那样但只有一个:如果显示菜单并且用户将鼠标按在按钮上但是将其释放到按钮之外,则从未调用actionPerformed
。这意味着showPopup
仍为假,下次按下按钮时菜单未显示。要解决此问题,mouseReleased
方法会重置showPopup
。据我所知,mouseReleased
方法在actionPerformed
之后被调用。
我玩了一下结果按钮,做了我能想到的所有按钮,它按预期工作。但是,我并非100%确定事件总是以相同的顺序发生。
最终,我认为这至少值得尝试。
答案 5 :(得分:0)
好吧,如果没有看到你的所有代码,我无法确定,但弹出窗口是否真的无法实现焦点?我之前在Swing中没有正确关注事物的问题,所以它可能是罪魁祸首。尝试在菜单上调用setFocusable(true)
,然后在出现菜单时调用requestFocus()
。
答案 6 :(得分:0)
我尝试了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的改变而改变。如果你想独立于平台,我不会推荐这个解决方案。
BTW:我在stackoverflow上创建了一个新帐户来提供此提示。我似乎不允许对他的回答发表评论(因为声誉)。但似乎我有一个按钮来编辑它。这个stackoverflow是一个非常有趣的事情。 : - )