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

3pmvbmvn  于 2023-01-29  发布在  Java
关注(0)|答案(4)|浏览(203)

我已经创建了一个JFrame,它带有一个包含不同组件的JPanel,例如,我希望当鼠标位于JPanel的边界内时,JPanel具有可见的边框和可见的图像。我的问题是,只要鼠标悬停在JPanel内的“可交互”组件上,它就会注册为鼠标退出JPanel。我希望它能够绘制这些内容,只要它“s在JPanel的边界内,当鼠标离开JPanel的边界时,边界和图像会“消失”。有什么方法可以实现这一点吗?
下面是一个小演示:

public class Test {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        new TestFrame();
    }

    static class TestFrame extends JFrame{
        JPanel panel;
        JButton hoverButton;
        JButton appearingButton;
        public TestFrame() {
            super();
            this.setDefaultCloseOperation(EXIT_ON_CLOSE);
            panel = new JPanel();
            panel.setBackground(Color.red);
            hoverButton = new JButton("Hover me!");
            appearingButton = new JButton("I appeared!");
            appearingButton.setVisible(false);
            panel.add(hoverButton);
            panel.add(appearingButton);
            panel.addMouseListener(new java.awt.event.MouseAdapter() {
                public void mouseEntered(java.awt.event.MouseEvent evt) {
                    System.out.println("Entered!");
                    appearingButton.setVisible(true);
                }
                public void mouseExited(java.awt.event.MouseEvent evt) {
                    System.out.println("Exited!");
                    appearingButton.setVisible(false);
                }
            });
            add(panel);
            setSize(new Dimension(200, 200));
            setVisible(true);
        }

    }

}

当鼠标位于JPanel内部(覆盖整个JFrame)时,第二个按钮将会出现。但是,将鼠标悬停在第一个按钮上会使第二个按钮消失。我希望只要您位于JPanel的边界内,第二个按钮就会显示。

jtoj6r0c

jtoj6r0c1#

这实际上比听起来要困难得多。您需要能够监视容器的子组件的所有鼠标事件。不幸的是,您要么得到一个全有或全无的解决方案。也就是说,您要么得到您现在遇到的问题,其中MouseListener在另一个组件开始捕获鼠标事件时停止报告鼠标事件(这是鼠标侦听器API的工作方式)或者可以看到系统正在处理的所有鼠标事件。
这就需要提供某种类型的过滤过程,以便可以过滤掉那些您不感兴趣的事件,例如...

Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
        @Override
        public void eventDispatched(AWTEvent event) {
            Object source = event.getSource();
            if (source instanceof JComponent) {
                JComponent comp = (JComponent) source;
                if (SwingUtilities.isDescendingFrom(parent, comp)) {
                    // The mouse is in the house...
                }
            }
        }
    }, AWTEvent.MOUSE_MOTION_EVENT_MASK);
  • (父项是您的主容器)*

这基本上是将一个AWTEventListener附加到主事件处理框架中,它将告诉您已处理的特定类型的所有事件。然后,您需要检查所讨论的事件是否确实发生在您感兴趣的上下文(您自己或它的一个子事件)中,然后再采取适当的操作...

Java 10(~ 8+?)/2018年

自从我写了最初的答案以来,事件机制的工作方式似乎发生了一些变化(我也犯了一些小错误🙄😓)
为了使AWTListener生成事件,所有“感兴趣”的组件都需要注册鼠标事件
我做了一个非常基本的测试,创建了一个普通的旧JPanel(和一个按钮),并将它们添加到父容器中,然后使用...

panel.addMouseListener(new MouseAdapter() {});
panel.addMouseMotionListener(new MouseAdapter() {});
add(panel);
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
    @Override
    public void eventDispatched(AWTEvent event) {
        Object source = event.getSource();
        if (source instanceof JComponent) {
            JComponent comp = (JComponent) source;
            System.out.println(comp);
            if (SwingUtilities.isDescendingFrom(comp, TestPane.this)) {
                // The mouse is in the house...
                System.out.println("Mouse in the house");
            }
        }
    }
}, AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK);

这将为按钮和面板生成事件

vmpqdwk3

vmpqdwk32#

一种解决方案是向JPanel中的每个子组件添加鼠标侦听器。
使用您的代码,这里有一种方法:

package com.ggl.testing;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class HoverTest {

    /**
     * @param args
     *            the command line arguments
     */
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                HoverTest hoverTest = new HoverTest();
                hoverTest.new TestFrame();
            }
        });
    }

    public class TestFrame extends JFrame {
        private static final long serialVersionUID = 6304847277329579360L;

        JPanel panel;

        JButton hoverButton;
        JButton appearingButton;

        public TestFrame() {
            super();
            this.setDefaultCloseOperation(EXIT_ON_CLOSE);
            panel = new JPanel();
            panel.setBackground(Color.red);

            hoverButton = new JButton("Hover me!");

            appearingButton = new JButton("I appeared!");
            appearingButton.setVisible(false);

            ButtonListener listener = new ButtonListener(appearingButton);

            panel.add(hoverButton);
            panel.add(appearingButton);
            panel.addMouseListener(listener);

            hoverButton.addMouseListener(listener);
            appearingButton.addMouseListener(listener);

            add(panel);

            setSize(new Dimension(200, 200));
            setVisible(true);
        }

    }

    private class ButtonListener extends MouseAdapter {

        private JButton appearingButton;

        public ButtonListener(JButton appearingButton) {
            this.appearingButton = appearingButton;
        }

        @Override
        public void mouseEntered(java.awt.event.MouseEvent evt) {
            System.out.println("Entered!");
            appearingButton.setVisible(true);
        }

        @Override
        public void mouseExited(java.awt.event.MouseEvent evt) {
            System.out.println("Exited!");
            appearingButton.setVisible(false);
        }
    }

}
ne5o7dgx

ne5o7dgx3#

这实际上比你想象的要容易得多:

panel.addMouseListener(new java.awt.event.MouseAdapter() {
     public void mouseEntered(java.awt.event.MouseEvent evt) {
          System.out.println("Entered!");
          appearingButton.setVisible(true);
      }
      public void mouseExited(java.awt.event.MouseEvent evt) {
           if( ! panel.contains( evt.getPoint() ) ){
                System.out.println("Exited!");
                appearingButton.setVisible(false);
           }
      }
});

***if***语句将过滤掉鼠标仍在面板中时发生的所有***mouseExited***事件。

一个后果是,您最终将收到多个***mouseEntered***事件,而这些事件之间没有***mouseExited***,但由此导致的任何问题都可以轻松避免。

hgb9j2n6

hgb9j2n64#

当编辑单元格时,我发现自己在使用JTable时遇到了类似的情况。
JTable有一个鼠标监听器,它可以跟踪鼠标的位置,但是一旦开始编辑单元格,表格就不会接收到mouseExited/MOUSE_EXITED事件。

@Override
public void mouseExited(MouseEvent e) {
    var lastestLocation = MouseInfo.getPointerInfo().getLocation();
    SwingUtilities.convertPointFromScreen(location, component);

    if (table.contains(latestLocation)) return; // bail out

    // do something with the table row
}

当收到 * 鼠标退出 * 事件时,它正在工作,但我发现有时当指针移动有点不同时,通常太快,则 * 鼠标退出 * 事件未触发,即使指针实际移出JTable
通过尝试MadProgrammer's solution,问题消失了。这里有一个小小的修改,使用边界执行检查,而不是遍历组件树。另外,我想发出与表相关的事件,而不是实际的源。

Toolkit.getDefaultToolkit().addAWTEventListener(awtEvent -> {
    var source = awtEvent.getSource();
    if (source instanceof JComponent) {
        var comp = (JComponent) source;
        // The actual received event may come from a different component than the table
        var tableMouseEvent = SwingUtilities.convertMouseEvent(
                comp,
                (MouseEvent) awtEvent,
                interactiveTable
        );
        if (interactiveTable.contains(tableMouseEvent.getPoint())) {
            // The mouse is in the house...
            listeners.forEach(l -> l.mouseMoveWithin(tableMouseEvent));
        } else {
            // Mouse is outside
            listeners.forEach(l -> l.mouseOut(tableMouseEvent));
        }
    }
}, AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK);

11.0.17+8-LTS基础数据库-aarch 64宏操作系统13.1

相关问题