尝试单击子菜单中的某个项目时,很自然地会在其下方的菜单项中快速绘制鼠标。 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);
}
}
答案 0 :(得分:2)
在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);
}
答案 1 :(得分:0)
我想出了一个非常讨厌的解决方案。
我创建了一个扩展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毫秒后弹出。
如果有人发现了更好的解决方案,请告诉我。
答案 2 :(得分:0)
非常感谢,您拯救了我的一天!该解决方案可以按预期工作,但是我建议使用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);
}
}
}
}
}