修复 JFrame 中重叠的 MouseListeners

Fix overlapping MouseListeners in JFrame

我 运行 遇到了 MouseListener 似乎相互重叠的问题。

在我的应用程序的 GUI class 中,我添加了@camickr 的 ComponentResizer class 并注册了我的 JFrame。当没有子组件时,或者当我不向子组件添加其他 MouseListener 时,它工作正常。但是当我这样做时,子组件似乎具有侦听器优先级,并且我无法调整北边界的大小。

我尝试了 addAWTEventListener() 方法,如下所示:

Toolkit.getDefaultToolkit().addAWTEventListener(event -> {
            if (event instanceof MouseEvent) {
                MouseEvent e = (MouseEvent) event;
                if (e.getSource() == MainGUI.getMainFrame()) {
                    switch (e.getID()) {
                        case MouseEvent.MOUSE_CLICKED -> mouseClicked(e);
                        case MouseEvent.MOUSE_PRESSED -> mousePressed(e);
                        case MouseEvent.MOUSE_RELEASED -> mouseReleased(e);
                        case MouseEvent.MOUSE_ENTERED -> mouseEntered(e);
                        case MouseEvent.MOUSE_EXITED -> mouseExited(e);
                        case MouseEvent.MOUSE_DRAGGED -> mouseDragged(e);
                        case MouseEvent.MOUSE_MOVED -> mouseMoved(e);
                        default -> {}
                    }
                }
            }
        }, AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);

但是结果很奇怪,框架中的所有组件都可以同时调整大小。

这是问题的 SSCCE:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class MainGUI {

    private static JFrame mainFrame;
    private static JPanel contentPane;
    private static JPanel titleBar;

    private static ComponentResizer resizer = new ComponentResizer();

    public static void init() {
        mainFrame = new JFrame();
        contentPane = new JPanel();

        titleBar = new JPanel();
        titleBar.add(new JLabel("This is a title"));
        titleBar.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));

        // Problematic part. The resizer works correctly without it.
        titleBar.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                System.out.println("You clicked me!");
            }
        });

        contentPane.setLayout(new BorderLayout());
        contentPane.add(titleBar, BorderLayout.NORTH);

        mainFrame.setContentPane(contentPane);
        mainFrame.setPreferredSize(new Dimension(600, 400));
        mainFrame.setUndecorated(true);

        resizer.registerComponent(mainFrame);
    }

    public static void display() {
        mainFrame.pack();
        mainFrame.setLocationRelativeTo(null);
        mainFrame.setEnabled(true);
        mainFrame.setVisible(true);
        mainFrame.requestFocus();
    }

    public static JFrame getMainFrame() {
        return mainFrame;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (ClassNotFoundException | InstantiationException |
                    IllegalAccessException | UnsupportedLookAndFeelException e) {
                e.printStackTrace();
            }
            MainGUI.init();
            MainGUI.display();
        });
    }
}

这里是 ComponentResizer class:

http://www.camick.com/java/source/ComponentResizer.java

所以问题是,有没有办法让 JFrame 的监听​​器在子组件监听器的“顶部”?

is there a way to keep the JFrame's listeners "on top" of the subcomponents listeners?

据我所知没有。只有一个组件可以接收 MouseEvent。

Swing 将从底部(parent / child 组件链)到顶部搜索组件以查找具有 MouseListener 的组件并将事件传递给该组件。所以第一个发现有侦听器的组件将接收事件。

一种解决方案是为您的标题栏提供一个带有 Border.

的“包装器”面板

例如:

    //  Create wrapper panel with a border

    JPanel wrapper = new JPanel(new BorderLayout());
    wrapper.setBorder( new EmptyBorder(5, 5, 5, 5) );
    wrapper.add(titleBar);

    contentPane.add(wrapper, BorderLayout.NORTH);
   //contentPane.add(titleBar, BorderLayout.NORTH);

现在您的标题栏将不会延伸到框架的边缘,因此当鼠标位于边缘的 5 个像素以内时,MouseEvent 将被传递到框架。

如果您希望 LineBorder 完全延伸到框架的边缘,您也可以从标题栏中删除边框并在包装面板上使用 CompoundBorder

请注意,您也可能会向 BorderLayout 的 CENTER 添加组件。如果这些组件中的任何一个具有 MouseListener,您将遇到同样的问题。根据您的要求,您可能需要考虑将 EmptyBorder 添加到框架的内容窗格中。这样您就可以有效地为整个框架添加边框。