尝试单击子菜单中的项目时,很自然地会在其下方的菜单项中快速绘制鼠标。 Windows和Mac本身都通过在打开菜单之前稍微延迟来处理这个问题。 Swing JMenus不会处理这个问题,鼠标到达目标菜单项之前会打开鼠标暂时悬停的菜单。
例如,在下图中,如果我试图选择Item 3
,但在此过程中我的鼠标短暂地滑过Menu 2
,Menu 1
子菜单会在我到达之前消失。
有没有人有任何提示或建议来解决这个问题?我的想法是定义一个自定义MenuUI,为其鼠标处理程序添加一个计时器。
这是一些简单的示例代码,说明了我的问题:
public class Thing extends JFrame {
public Thing()
{
super();
this.setSize(new Dimension(500, 500));
final JPopupMenu pMenu = new JPopupMenu();
for (int i = 0; i < 5; i++)
{
JMenu menu = new JMenu("Menu " + i);
pMenu.add(menu);
for (int j = 0; j < 10; j++)
{
menu.add(new JMenuItem("Item " + j));
}
}
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
pMenu.show(Thing.this, e.getX(), e.getY());
}
});
}
public static void main(String[] args)
{
Thing t = new Thing();
t.setVisible(true);
}
}
在你的setDelay(delay)
变量上调用menu
,其中delay
参数是等待菜单显示的毫秒数,作为int。
以下代码行将延迟设置为1秒,因此用户必须在显示子菜单之前将菜单项“菜单n”鼠标悬停1秒钟:menu.setDelay(1000);
以下是已编辑代码的片段:
for (int i = 0; i < 5; i++)
{
JMenu menu = new JMenu("Menu " + i);
pMenu.add(menu);
for (int j = 0; j < 10; j++)
{
menu.add(new JMenuItem("Item " + j));
}
menu.setDelay(1000);
}
我想出了一个非常讨厌的解决方案。
我创建了一个扩展BasicMenuUI的UI类。我重写createMouseInputListener
方法返回自定义MouseInputListener
而不是handler
中的私有BasicMenuUI
对象。
然后我从GrepCode [1]获得了MouseInputListener
中handler
实现的代码,并将其复制到我的自定义监听器中。我做了一个改变,把计时器放在mouseEntered
。我对mouseEntered
的最终代码如下所示:
public void mouseEntered(MouseEvent e) {
timer.schedule(new TimerTask() {
@Override
public void run() {
if (menuItem.isShowing())
{
Point mouseLoc = MouseInfo.getPointerInfo().getLocation();
Point menuLoc = menuItem.getLocationOnScreen();
if (mouseLoc.x >= menuLoc.x && mouseLoc.x <= menuLoc.x + menuItem.getWidth() &&
mouseLoc.y >= menuLoc.y && mouseLoc.y <= menuLoc.y + menuItem.getHeight())
{
originalMouseEnteredStuff();
}
}
}
}, 100);
}
在调用mouseEntered
中的原始代码之前,我检查以确保鼠标仍在此菜单的区域内。我不希望鼠标刷过的所有菜单在100毫秒后弹出。
如果有人发现了更好的解决方案,请告诉我。
非常感谢,你救了我的一天!该解决方案按预期工作,但我建议使用Swing计时器确保代码由EDT执行。
此外,您应该在调用原始内容之前临时将菜单延迟设置为零。否则,用户必须等待两倍的延迟时间。
@Override
public void mouseEntered(MouseEvent e) {
if (menu.isTopLevelMenu() || menu.getDelay() == 0) {
originalMouseEnteredStuff(e);
} else {
final javax.swing.Timer timer = new javax.swing.Timer(menu.getDelay(), new DelayedMouseEnteredAction(e));
timer.setRepeats(false);
timer.start();
}
}
class DelayedMouseEnteredAction implements ActionListener
{
private final MouseEvent mouseEnteredEvent;
private DelayedMouseEnteredAction(MouseEvent mouseEnteredEvent) {
this.mouseEnteredEvent = mouseEnteredEvent;
}
@Override
public void actionPerformed(ActionEvent actionEvent) {
if (menu.isShowing()) {
final Point mouseLocationOnScreen = MouseInfo.getPointerInfo().getLocation();
final Rectangle menuBoundsOnScreen = new Rectangle(menu.getLocationOnScreen(), menu.getSize());
if (menuBoundsOnScreen.contains(mouseLocationOnScreen)) {
/*
* forward the mouse event only if the mouse cursor is yet
* located in the menus area.
*/
int menuDelay = menu.getDelay();
try {
/*
* Temporary remove the delay. Otherwise the delegate would wait the
* delay a second time e.g. before highlighting the menu item.
*/
menu.setDelay(0);
originalMouseEnteredStuff(mouseEnteredEvent);
} finally {
// reset the delay
menu.setDelay(menuDelay);
}
}
}
}
}