鼠标在内部组件悬停时退出
Posted
技术标签:
【中文标题】鼠标在内部组件悬停时退出【英文标题】: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 的范围内,我希望显示第二个按钮。
【问题讨论】:
【参考方案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
的常规MouseListener
s),既没有触发 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 将保持对组件的强引用,防止它被垃圾收集以上是关于鼠标在内部组件悬停时退出的主要内容,如果未能解决你的问题,请参考以下文章