在 JPanel 上的任何位置检测鼠标进入/退出事件

Posted

技术标签:

【中文标题】在 JPanel 上的任何位置检测鼠标进入/退出事件【英文标题】:Detecting mouse enter/exit events anywhere on JPanel 【发布时间】:2011-01-27 13:37:03 【问题描述】:

基本上有一个JPanel,我想知道鼠标何时进入JPanel区域并退出JPanel区域。所以我添加了一个鼠标侦听器,但是如果 JPanel 上有组件并且鼠标越过其中一个组件,即使该组件位于 JPanel 上,它也会被检测为 JPanel 上的退出。我想知道是否有人知道在不执行诸如在 JPanel 上的所有组件上添加侦听器之类的操作的情况下解决此问题的任何方法?

【问题讨论】:

本题答案适用于您的问题:***.com/questions/1882055/… 【参考方案1】:

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

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 事件发生在子元素上时,您只需忽略它。

【讨论】:

如果父母和孩子之间没有间隙,这不起作用。在这种情况下,mouseEnteredmouseExited 都不会被调用。【参考方案2】:

对于可能包含其他组件的组件,这是一种方法:

    添加全局 AWT 事件侦听器以获取所有鼠标事件。例如:

    Toolkit.getDefaultToolkit().addAWTEventListener( 
       new TargetedMouseHandler( panel ), AWTEvent.MOUSE_EVENT_MASK );
    

    实现TargetedMouseHandler 以忽略不是由面板或面板的其中一个子级发起的事件(您可以使用SwingUtilities.isDescendingFrom 对此进行测试)。

    跟踪鼠标是否已经在您的面板范围内。当您在面板或其子面板中收到 MouseEvent.MOUSE_ENTERED 事件时,请将标志设置为 true。

    当您收到MouseEvent.MOUSE_EXITED 事件时,仅当MouseEvent 中的点超出目标面板的范围时才重置标志。 SwingUtilities.convertPointComponent.getBounds().contains() 会在这里派上用场。

【讨论】:

我试图实现您的想法(在带有组件的面板上拥有一个全局右键单击处理程序(上下文菜单)),但我发现它确实是全局的,即。在整个应用程序级别,这可能会像我们一样对内部框架感到恼火,并且我们必须在关闭相应的内部框架时删除处理程序。仍然是一个有趣且内容丰富的答案,可以适用于更简单的情况。 @Ash 您能否说明您必须为panel 启用鼠标事件或添加鼠标侦听器。否则不会触发鼠标事件,另见MouseEvent documentation。【参考方案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;
                    
                
            
        
    

【讨论】:

【参考方案4】:

java 1.8+ 的简单解决方案

public class MyJPanel implements MouseListener 

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

    ...

【讨论】:

【参考方案5】:

如果您想将所有事件发送到***窗口,您可以在 JFrame 的玻璃窗格中添加一个侦听器。见getGlassPane。

【讨论】:

问题是关于 JPanel,而不是 JFrame。您可能会回答这个问题,但只是部分回答,因此该答案不适合新手。 PS.:甚至早在 2010 年,我认为它是 Java 1.6 文档的更好链接。

以上是关于在 JPanel 上的任何位置检测鼠标进入/退出事件的主要内容,如果未能解决你的问题,请参考以下文章

Java - 检测鼠标是不是在屏幕上的任何位置按下

JavaFX:如何在屏幕上的任何位置检测鼠标/键事件?

鼠标在内部组件悬停时退出

透明JPanel

需要 MenuBar 的 MouseOutEvent 来检测点击:GWT

如何在 Java 中检测 Image 对象上的鼠标单击事件?