[英]Java Swing - Add leniency when selecting items in submenus
尝试单击子菜单中的项目时,自然可以在其下方的菜单项上快速绘制鼠标。 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);
}
}
在menu
变量上调用setDelay(delay)
,其中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
而不是BasicMenuUI
中的私有handler
对象。
然后,我从GrepCode [1]中获得了handler
MouseInputListener
实现的代码,并将其复制到了我的自定义侦听器中。 我做了一个更改,在mouseEntered
中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);
}
}
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.