我需要帮助来理解Swing中的事件传播。我知道每个事件只由一个组件处理。因此,当我有一个面板outside
带有一些子面板inside
并且我将mouseListeners添加到它们时,将调用inside
之一。这很好,这是预期的行为。
但我不明白以下情况中的行为:
inside
注册一个MouseMotionListener,outside
注册一个MouseListener。我希望inside
使用所有MouseMotionEvents和outside
来接收MouseEvents,因为inside
上没有正常MouseEvents的监听器。但事实并非如此,inside
不仅以某种方式消耗所有MouseEvent,而且只消耗MouseMotionEvents。
以下代码说明了问题:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class EventTest {
public static void main(String... args) {
SwingUtilities.invokeLater(new Runnable(){
@Override
public void run() {
JComponent inside = new JPanel();
inside.setBackground(Color.red);
inside.setPreferredSize(new Dimension(200,200));
MouseMotionListener mm = new MouseMotionListener() {
@Override
public void mouseDragged(MouseEvent arg0) {
System.err.println("dragged");
}
@Override
public void mouseMoved(MouseEvent arg0) {
System.err.println("moved");
}
};
// next line disables handling of mouse clicked events in outside
inside.addMouseMotionListener(mm);
JComponent outside = new JPanel();
outside.add(inside);
outside.setPreferredSize(new Dimension(300,300));
outside.addMouseListener( new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
System.err.println("clicked");
}
});
JFrame frame = new JFrame();
frame.add(outside);
frame.pack();
frame.setVisible(true);
}
});
}
}
我可以通过在inside
上为父组件可能感兴趣的所有事件注册侦听器来解决此问题,然后调用dispatchEvent将事件转发给父级。
a)是否有人可以指向某些文档,其中描述了这种行为? MouseEvent的javadoc让我觉得我的期望是正确的。所以,我需要一个不同的描述来理解它。
b)有没有比上面描述的解决方案更好的解决方案?
谢谢, 席
编辑:目前还不清楚,为什么Swing会这样做。但是看起来,让这些东西工作的唯一方法就是手动转发事件,我会这样做。
答案 0 :(得分:8)
a)通过design,Java鼠标事件"冒泡"只有在子组件上没有鼠标侦听器时才会这样。
b)您可以转发事件到另一个组件,如here及以下所示。
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class EventTest {
public static void main(String... args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
final JComponent outside = new JPanel();
JComponent inside = new JPanel();
inside.setBackground(Color.red);
inside.setPreferredSize(new Dimension(200, 200));
inside.addMouseMotionListener(new MouseAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
System.err.println("dragged");
}
@Override
public void mouseMoved(MouseEvent e) {
System.err.println("moved inside");
outside.dispatchEvent(e);
}
});
outside.add(inside);
outside.setPreferredSize(new Dimension(300, 300));
outside.addMouseMotionListener(new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent arg0) {
System.err.println("moved outside");
}
});
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(outside);
frame.pack();
frame.setVisible(true);
}
});
}
}
答案 1 :(得分:1)
与trashgod的答案非常相似 - 您可以使用MouseAdapter
作为动作监听器,并覆盖它以转发您希望由父级处理的任何事件。这应该只为您的代码添加最少量。
MouseAdapter mm = new MouseAdapter() {
@Override
public void mouseDragged(MouseEvent arg0) {
System.err.println("dragged");
}
@Override
public void mouseMoved(MouseEvent arg0) {
System.err.println("moved");
}
@Override
public void mouseClicked(MouseEvent e) {
outside.dispatchEvent(e);
}
};
// For forwarding events
inside.addMouseListener(mm);
// For consuming events you care about
inside.addMouseMotionListener(mm);
我也找不到使用dispatchEvent(e)
方法的方法。我认为你坚持这条路。
答案 2 :(得分:0)
这对我有用:
Ellipse2D mCircle = new Ellipse2D.Double(x,y,size,size);
void PassMouseEvent(MouseEvent e) {
getParent().dispatchEvent(e);
}
public void mousePressed(MouseEvent arg0) {
if(mCircle.contains(arg0.getX(), arg0.getY())) {
// Do stuff if we click on this object
} else {
// Pass to the underlying object to deal with the mouse event
PassMouseEvent(arg0);
}
}