子组件中的MouseMotionListener会禁用父组件中的MouseListener。

9
我需要帮助了解Swing中的事件传播。我知道每个事件只会由一个组件处理。因此,当我有一个面板 outside,它有一些子面板 inside,并且我为它们两个都添加了mouseListeners时,inside 的监听器将被调用。这很好,也是预期的行为。
但是,我不理解以下情况的行为: inside 注册了一个MouseMotionListener,而outside注册了一个MouseListener。我期望inside消耗所有MouseMotionEvents,而outside接收MouseEvents,因为在inside上没有普通的MouseEvents监听器。但事实并非如此,inside以某种方式消耗了所有MouseEvents,而不仅仅是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的javadocs让我认为我的期望是正确的。因此,我需要一个不同的描述来理解它。

b) 是否有比上面草拟的解决方案更好的解决方案?

谢谢, 卡瑟琳

编辑:仍然不清楚Swing为什么会表现出这种方式。但看起来,使事情正常工作的唯一方法是手动转发事件,所以我会这样做。


+1 for sscce - trashgod
3个回答

8

a) 根据设计,Java鼠标事件只有在子组件上没有鼠标监听器时才会“冒泡”。

b) 您可以像这里和下面所示的那样将事件“转发”到另一个组件。

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
非常类似于trashgod的答案-您可以使用MouseAdapter作为您的motion listener,并覆盖它以转发任何事件,您希望父级处理这些事件。这只应在您的代码中添加最少的内容。
        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)方法的方法。我认为你只能坚持这条路线。


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);
    }
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接