如何将 MouseListener 留在 ChildComponent 上但正确跟踪鼠标在父级上的进入和退出?
Posted
技术标签:
【中文标题】如何将 MouseListener 留在 ChildComponent 上但正确跟踪鼠标在父级上的进入和退出?【英文标题】:How to leave MouseListener on ChildComponent but correctly keep track of mouse enter and exit on parent? 【发布时间】:2020-04-28 13:11:00 【问题描述】:我有一个带有 CardLayout 和两张卡片的 JPanel。我希望布局在每次鼠标进入或退出面板时翻转卡片。 除非其中一张卡是侦听鼠标事件的组件,否则这可以正常工作。考虑以下示例:
JPanel cardLayoutPanel = new JPanel(layout);
JButton button = new JButton("listening!");
JLabel label = new JLabel("not listening.");
cardLayoutPanel.add(button);
cardLayoutPanel.add(label);
layout.last(cardLayoutPanel);
cardLayoutPanel.addMouseListener(new MouseAdapter()
@Override
public void mouseEntered(MouseEvent e)
System.out.println("entered!");
layout.next(cardLayoutPanel);
@Override
public void mouseExited(MouseEvent e)
System.out.println("exited!");
layout.next(cardLayoutPanel);
);
问题在于,如果 MouseEvent 被子组件捕获,则在与此主题相关的许多 SO 问题中,父组件不会将其处理为读取。 我尝试了不同的方法,例如重新调度事件,或者如果事件坐标仍在面板中,则忽略退出事件。
第一个解决方案根本不起作用,第二个解决方案从那时起鼠标输入事件不再发生。
我该如何解决这个问题?
我现在看到的唯一解决方案是从子组件中完全删除侦听器,并在父母鼠标侦听器中自行执行碰撞和事件处理,但这会是一团糟而不是预期的方式要做到这一点,我猜。
任何帮助或想法表示赞赏。
编辑:这是一个完整的简短可编译示例:
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class CardLayoutTest
public static void main(String[] args)
JFrame frame = new JFrame();
JPanel content = new JPanel();
content.setBorder(new EmptyBorder(new Insets(50, 50, 50, 50)));
CardLayout layout = new CardLayout();
JPanel cardLayoutPanel = new JPanel(layout);
JButton button = new JButton("listening!");
JLabel label = new JLabel("not listening.");
cardLayoutPanel.add(button);
cardLayoutPanel.add(label);
layout.last(cardLayoutPanel);
cardLayoutPanel.addMouseListener(new MouseAdapter()
@Override
public void mouseEntered(MouseEvent e)
System.out.println("entered!");
layout.next(cardLayoutPanel);
@Override
public void mouseExited(MouseEvent e)
System.out.println("exited!");
layout.next(cardLayoutPanel);
);
content.add(cardLayoutPanel);
frame.add(content);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
【问题讨论】:
鼠标事件很痛苦。只需添加鼠标侦听器即可防止事件沿组件树传播!在有 PL&F 的地方,您无法确定发生了什么。重新调度为“玻璃窗格”应该可以工作。 我知道,这正是这里的问题。 PL&F 是什么意思?您所说的“重新调度为‘玻璃窗格’”是什么意思? PL&F Plugable Look and Feel - 使 GUI 看起来像特定平台的代码,无论是 Windows、Metal、Gtk 还是其他平台。应该有很多关于玻璃窗格的示例,它是容器顶部的一个组件,可以在鼠标事件经过时拦截它们。 好的,谢谢,我看看能不能用这种方法运行 【参考方案1】:我稍微修改了您的示例并使其正常工作。关键(至少在这个例子中)是让按钮与cardLayoutPanel
共享相同的mouseListener
。我不知道这是否是一个通用的解决方案,但它确实在这里工作。我还进行了以下更改:
-
为 mouseListener 添加了一个私有内部类。
增加了 cardLayoutPanel 的大小。
为标签和按钮添加了彩色边框。
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
public class CardLayoutTest
JButton button = new JButton("listening!");
JPanel cardLayoutPanel = new JPanel();
CardLayout layout = new CardLayout();
public static void main(String[] args)
new CardLayoutTest().start();
public void start()
JFrame frame = new JFrame();
JPanel content = new JPanel();
content.setBorder(new EmptyBorder(
new Insets(50, 50, 50, 50)));
cardLayoutPanel.setLayout(layout);
cardLayoutPanel
.setPreferredSize(new Dimension(200, 200));
button.addActionListener(
ae -> System.out.println("IT WORKS!"));
button.removeMouseMotionListener(
button.getMouseMotionListeners()[0]);
button.removeMouseListener(
button.getMouseListeners()[0]);
MyMouseListener ml = new MyMouseListener();
button.addMouseListener(ml);
button.setBorder(new LineBorder(Color.red, 2));
JLabel label = new JLabel("not listening.");
label.setBorder(new LineBorder(Color.blue, 2));
cardLayoutPanel.add(button);
cardLayoutPanel.add(label);
layout.last(cardLayoutPanel);
cardLayoutPanel.addMouseListener(ml);
content.add(cardLayoutPanel);
frame.add(content);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
private class MyMouseListener extends MouseAdapter
@Override
public void mouseEntered(MouseEvent e)
System.out.println("entered!");
layout.next(cardLayoutPanel);
public void mouseClicked(MouseEvent e)
if (e.getSource() instanceof JButton)
JButton b = (JButton)(e.getSource());
b.doClick();
@Override
public void mouseExited(MouseEvent e)
System.out.println("exited!");
layout.next(cardLayoutPanel);
【讨论】:
非常感谢您的努力!我会看看是否可以让您的解决方案明天运行。 好的。一项观察。我在主程序中使用了button
实例来click
按钮。更好的方法是从MouseEvent
获取源并将其转换为JButton,然后使用它来执行doClick
。这样他们就可以共享相同的mouseListener
实际上,我只是尝试了一下以确保语法,所以我会修改我的答案。【参考方案2】:
这不是一个好的解决方案,但我设法通过在 进入 父组件时调用翻转事件来实现我想要的。 这是因为我有足够的空间在具有此属性的面板之间。
然而,我并没有按照评论中的建议用玻璃板实现这种效果。也许我做错了,但是事件没有正确到达组件,甚至一些 instanceof 检查也突然失败了。
如果有人知道如何正确执行此操作,我会很高兴看到。
【讨论】:
以上是关于如何将 MouseListener 留在 ChildComponent 上但正确跟踪鼠标在父级上的进入和退出?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 mouselistener 访问一个组件而不是另一个组件
除非与mouselistener一起使用,否则JLabel不会显示
repaint 导致 mouseListener 注册没有发生的点击
当在子组件中添加其他 MouseListener 时,父组件的 MouseListener 在子组件中不起作用