在JPanel上检测鼠标进入/退出事件的方法

14

基本上,我有一个JPanel,在这个面板上我想知道鼠标何时进入并离开JPanel的区域。因此,我添加了一个鼠标监听器,但是如果在JPanel上有组件,并且鼠标经过其中一个组件时,即使该组件位于JPanel上,它也被检测为从JPanel退出。我想知道是否有任何方法可以解决这个问题,而不必像在所有JPanel组件上添加监听器这样做?


这个问题中的答案适用于您的问题:https://dev59.com/SUrSa4cB1Zd3GeqPY8Lb - Ash
5个回答

10

有一个非常简单的解决方案可以解决这个问题:

public class MyJPanel implements MouseListener {

    public void mouseExited(MouseEvent e) {
        java.awt.Point p = new java.awt.Point(e.getLocationOnScreen());
        SwingUtilities.convertPointFromScreen(p, e.getComponent());
        if(e.getComponent().contains(p)) {return;}
        ...//the rest of your code
    }

    ...
}

当子元素触发mouseExited事件时,你只需要忽略它即可。


如果父元素和子元素之间没有间隙,则此方法无效。在这种情况下,既不会调用mouseEntered也不会调用mouseExited - Marcono1234

9

以下是一种处理可能包含其他组件的组件的方法:

  1. Add a global AWT event listener to get all mouse events. For example:

    Toolkit.getDefaultToolkit().addAWTEventListener( 
       new TargetedMouseHandler( panel ), AWTEvent.MOUSE_EVENT_MASK );
    
  2. Implement the TargetedMouseHandler to ignore events that aren't sourced by the panel or by one of the panel's children (you can use SwingUtilities.isDescendingFrom to test for this).

  3. Keep track of whether or not the mouse is already within the bounds of your panel. When you get a MouseEvent.MOUSE_ENTERED event in your panel or one of its children, set a flag to true.

  4. When you get a MouseEvent.MOUSE_EXITED event, only reset the flag if the point in the MouseEvent is outside the bounds of your target panel. SwingUtilities.convertPoint and Component.getBounds().contains() will come in handy here.


我尝试着实现你的想法(在面板上添加全局右键处理程序(上下文菜单)),但我发现它真的是全局的,即在整个应用程序级别,这可能会对我们拥有的内部框架产生麻烦,我们必须在关闭相应的内部框架时删除处理程序。尽管如此,这仍然是一个有趣和信息丰富的答案,适用于更简单的情况。 - PhiLho
@Ash,请注意需要启用鼠标事件或向“panel”添加鼠标监听器。否则,将不会触发任何鼠标事件。请参阅MouseEvent文档 - Marcono1234

3

这是实现Ash解决方案的示例代码。对我来说,JFrame没有正确检测所有退出事件,但内部的JPanel确实检测到了,因此我传入了两个组件 - 一个用于测试后代,另一个用于测试边界。

Toolkit.getDefaultToolkit().addAWTEventListener(
        new TargetedMouseHandler(this, this.jPanel), 
        AWTEvent.MOUSE_EVENT_MASK);
}

public class TargetedMouseHandler implements AWTEventListener
{

    private Component parent;
    private Component innerBound;
    private boolean hasExited = true;

    public TargetedMouseHandler(Component p, Component p2)
    {
        parent = p;
        innerBound = p2;
    }

    @Override
    public void eventDispatched(AWTEvent e)
    {
        if (e instanceof MouseEvent)
        {
            if (SwingUtilities.isDescendingFrom(
                (Component) e.getSource(), parent))
            {
                MouseEvent m = (MouseEvent) e;
                if (m.getID() == MouseEvent.MOUSE_ENTERED)
                {
                    if (hasExited)
                    {
                        System.out.println("Entered");
                        hasExited = false;
                    }
                } else if (m.getID() == MouseEvent.MOUSE_EXITED)
                {
                    Point p = SwingUtilities.convertPoint(
                        (Component) e.getSource(),
                        m.getPoint(),
                        innerBound);
                    if (!innerBound.getBounds().contains(p))
                    {
                        System.out.println("Exited");
                        hasExited = true;
                    }
                }
            }
        }
    }
}

2

使用Java 1.8+的更简单解决方案

public class MyJPanel implements MouseListener {

    public void mouseExited(MouseEvent e) {        
        if(!this.contains(e.getPoint())) {
            ... //the rest of your code
        }
    }

    ...
}

0

如果您想获取发送到顶层窗口的所有事件,可以向JFrame的玻璃窗格添加侦听器。请参见getGlassPane


这个问题是关于 JPanel 而不是 JFrame 的。你可能会回答这个问题,但只能解决部分问题,所以对新手来说不太合适。顺便说一句:即使是在2010年,我认为最好链接到Java 1.6的文档。 - PhiLho

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接