繁体   English   中英

Java Swing-在子菜单中选择项目时增加宽大处理

[英]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实现的代码,并将其复制到了我的自定义侦听器中。 我做了一个更改,在mouseEnteredmouseEntered了一个计时器。 我对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毫秒后弹出鼠标的所有菜单。

请让我知道是否有人找到了更好的解决方案。

[1] http://www.grepcode.com/file_/repository.grepcode.com/java/root/jdk/openjdk/7-b147/javax/swing/plaf/basic/BasicMenuUI.java/?v=source

非常感谢您,您拯救了我的一天! 该解决方案按预期工作,但我建议使用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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM