【问题标题】:Mouse exited on inner component hover鼠标在内部组件悬停时退出
【发布时间】:2015-04-28 00:27:19
【问题描述】:

我创建了一个 JFrame,其 JPanel 包含不同的组件,例如,当鼠标位于 JPanel 的边界内时,我希望 JPanel 具有可见边框和可见图像。我的问题是,只要鼠标悬停在 JPanel 内的“可交互”组件上,它就会在鼠标退出 JPanel 时注册。我希望它在 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 的范围内,我希望显示第二个按钮。

【问题讨论】:

    标签: java swing


    【解决方案1】:

    这实际上比听起来要困难得多。您需要能够监视容器子组件的所有鼠标事件。不幸的是,你要么得到一个全有或全无的解决方案。也就是说,你要么得到你现在遇到的问题,一旦另一个组件开始捕获它们,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);
    

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

    【讨论】:

    • 能否请您说明您必须为parent 启用鼠标事件或添加鼠标侦听器。否则不会触发鼠标事件,另见MouseEvent documentation
    • @Marcono1234 这种方法是直接监听事件调度队列 - 不需要“注册”任何其他兴趣,因为你直接在煤面 - 在调用这个监听器之后,事件将被分派给之前对事件有兴趣的组件——这就是重点
    • 你确定吗?当我实现它时(并且没有针对parent 的常规MouseListeners),既没有触发 MOUSE_EXITED 也没有触发 MOUSE_ENTERED 事件。我假设其他鼠标事件类型无关紧要,因为它们与组件没有直接关系。
    • 我认为你应该使用AWTEvent.MOUSE_EVENT_MASK 作为事件掩码。 MouseEvent.MOUSE_MOVED 似乎没有事件掩码,并导致侦听器收到各种事件的通知,包括 ComponentEvent、WindowEvent、...
    • @Marcono1234 该示例使用MouseEvent.MOUSE_MOVED掩码
    【解决方案2】:

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

    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,但是可以轻松避免由此引起的任何问题。

    【讨论】:

    • 如果父母和孩子之间没有差距,这将很遗憾。
    【解决方案3】:

    一种解决方案是为 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);
            }
        }
    
    }
    

    【讨论】:

    • 仅供参考-这可能会产生内存泄漏,如果子组件被删除,MouseListener 将保持对组件的强引用,防止它被垃圾收集
    猜你喜欢
    • 1970-01-01
    • 2019-11-01
    • 2011-05-26
    • 1970-01-01
    • 2014-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多