过去,当有人JPopupMenu
可见时,默认情况下会选择第一项:http://weblogs.java.net/blog/alexfromsun/archive/2008/02/jtrayicon_updat.html
现在默认行为是弹出菜单而不选择任何项目。我想创建一个JPopupMenu
,其中包含一个项目,该项目将弹出选中并在鼠标指针下居中。我设法让项目以鼠标为中心弹出,但我JMenuItem
拒绝渲染,就像它被选中一样。如果我将鼠标移出项目并返回其中,则选择正确。
有什么想法吗?
这是我的测试用例:
import java.awt.Component;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
public class Test extends JFrame
{
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setSize(800, 600);
frame.getContentPane().addMouseListener(new MouseAdapter()
{
@Override
public void mousePressed(MouseEvent e)
{
if (e.isPopupTrigger())
popupTriggered(e);
}
@Override
public void mouseReleased(MouseEvent e)
{
if (e.isPopupTrigger())
popupTriggered(e);
}
private void popupTriggered(MouseEvent e)
{
JPopupMenu menu = new JPopupMenu();
final JMenuItem item = new JMenuItem("This is a JMenuItem");
menu.add(item);
Point point = e.getPoint();
int x = point.x - (item.getPreferredSize().width / 2);
int y = point.y - (item.getPreferredSize().height / 2);
menu.show((Component) e.getSource(), x, y);
}
});
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
}
答案 0 :(得分:6)
秘密结果是MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{menu, ...});
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.SwingUtilities;
/**
* Demonstrates programmatic {@link JMenuItem} selection;
* specifically how to make the first item selected by default
*/
public class TestPopup extends JFrame {
public static void main(String[] args) {
final JFrame frame = new JFrame("TestPopup");
frame.setSize(640, 480);
frame.getContentPane().addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger()) {
popupTriggered(e);
}
}
private void popupTriggered(MouseEvent e) {
final JPopupMenu menu = new JPopupMenu();
final JMenuItem item0 = new JMenuItem("JMenuItem 0");
final JMenuItem item1 = new JMenuItem("JMenuItem 1");
menu.add(item0);
menu.add(item1);
menu.pack();
// use invokeLater or just do this after the menu has been shown
SwingUtilities.invokeLater(new Runnable() {
public void run() {
MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{menu, item0});
}
});
int x = (int) ((int) (frame.getSize().width - (menu.getPreferredSize().width / 2.)) / 2.);
int y = (int) ((int) (frame.getSize().height - (menu.getPreferredSize().height / 2.)) / 2.);
menu.show(frame, x, y);
// doesn't work:
//item0.setSelected(true);
// doesn't work:
//menu.getSelectionModel().setSelectedIndex(0);
// bingo; see also MenuKeyListener / MenuKeyEvent
// MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{menu, item0});
}
});
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
答案 1 :(得分:1)
MenuSelectionManager.defaultManager()确实是一个很好的解决方案,但是当你试图预先选择你的JPopupMenu的子菜单(它将隐藏父菜单)时它将无法工作。 此外,它会混淆其他键盘导航行为(你不能按左键隐藏子菜单等)。
不幸的是,在Swing中这个问题没有很好的解决方案...... 我的解决方案很丑陋,但遗憾的是工作完美:
public static void setMenuSelectedIndex(final JPopupMenu popupMenu, final int index) {
SwingUtilities.invokeLater(new Runnable(){public void run()
{
for (int i=0; i < index+1; i++) {
popupMenu.dispatchEvent(new KeyEvent(popupMenu, KeyEvent.KEY_PRESSED, 0, 0, KeyEvent.VK_DOWN, '\0'));
}
}});
}
正如你所看到的,我基本上是在弹出菜单上模拟'Down'键盘按键......
更好的解决方案可能不是硬编码模拟VK_DOWN,而是读取Popup的输入映射并确定哪个KeyCode意味着“选择下一个菜单项” - 但我认为我们大多数人都会相处这个黑客...
您可能还想查看此方法,该方法选择菜单项后会选择该项 它利用了以前的方法
public static void setSelectedIndexWhenVisible(final JMenu menu, final int index) {
menu.getPopupMenu().addPopupMenuListener(new PopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
PopupUtils.setMenuSelectedIndex(menu.getPopupMenu(), index);
menu.getPopupMenu().removePopupMenuListener(this);
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
}
});
}
答案 2 :(得分:0)
现在默认行为是弹出菜单而不选择任何项目。
实际上,我认为这是正确的行为,至少在Windows中是这样。其他非Java应用程序也这样做。即使菜单中只有一个项目,我认为不值得破坏这个约定。如果您不这么认为,可以将选择索引设置为sean.bright's answer。
所以,我终于有机会在Java 1.6.0_11
上尝试一下,并发现了一些不一致的行为:如果弹出菜单突出了父框架,则自动选择该项目;如果弹出菜单完全出现在父框架内,则不会选择任何内容。听起来像是一个Swing bug,它至少可以保证RFE的行为一致。
答案 3 :(得分:0)
这很奇怪。
我尝试使用Windows,使用 Java 1.5.0_08 甚至 1.6.0_07 ,第一个元素会自动被选中,就像你一样期待它。
所以我尝试使用 1.6.0_11 ,它不再起作用,最初没有选择第一个元素。 选择selectionModel中的元素似乎没有帮助。
一种解决方法(我并不自豪)是在显示弹出菜单后使用 MouseEvent 的坐标自动移动鼠标。也许有人有更好的主意?
import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
public class SelectedPopupMenu extends JFrame {
public SelectedPopupMenu() {
addMouseListener(new MouseAdapter() {
public void mouseClicked(final MouseEvent e) {
JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add(new JMenuItem("Test-Item"));
popupMenu.add(new JMenuItem("Test-Item-2"));
// do not care to really hit the center of the popup
popupMenu.show(SelectedPopupMenu.this, e.getX() - 30, e.getY() - 10);
try {
// shake mouse, so that first element is selected even in Java 1.6.0_11
Robot robot = new Robot();
robot.mouseMove(e.getX() + 1, e.getY());
robot.mouseMove(e.getX(), e.getY());
} catch (AWTException ex) {
ex.printStackTrace();
}
}
});
}
public static void main(String[] args) {
JFrame frame = new SelectedPopupMenu();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800, 600);
frame.setVisible(true);
}
}