JFrame Glasspane 也在 JDialog 之上,但不应该

2022-01-24 00:00:00 mouseevent java jframe jdialog glasspane

我有一个带有 Glasspane 的 JFrame(未装饰).此框架打开一个 JDialog(也未装饰,也有一个 glassPane)并隐藏自身(setVisible(false)).Glasspanes 使用 .setGlassPane() 设置.对话框以 Frame 作为所有者打开.

I have a JFrame (undecorated) with a Glasspane. This Frame opens a JDialog (also undecorated and has also a glassPane) and hides itself (setVisible(false)). The Glasspanes are set with .setGlassPane(). The Dialog is opened with the Frame as owner.

GlassPane 扩展了 JPanel 并实现了 AWTEventListener.我用它来调整框架和对话框的大小,所以它知道它是父级(框架/对话框)——这被称为目标".

The GlassPane extends a JPanel and implements AWTEventListener. I use it for resizing the Frames and Dialogs, so it knows it's parent (the Frame/Dialog) - this is called "target".

GlassPane 中的事件是这样处理的:

The Events inside the GlassPane are handled like this:

public void eventDispatched(AWTEvent event) {

  if (target instanceof JFrame) {
     e = SwingUtilities.convertMouseEvent(
     ((MouseEvent) event).getComponent(),
     (MouseEvent) event, ((JFrame) target).getGlassPane());
  } else if (target instanceof JDialog) {
     e = SwingUtilities.convertMouseEvent(
     ((MouseEvent) event).getComponent(),
     (MouseEvent) event, this);
  }


  if (e.getID() == MouseEvent.MOUSE_PRESSED) {
    this.startPos = target.getLocationOnScreen();
  }
}

在target.getLocationOnScree"处,当 JFrame 被隐藏并单击 JDialog 时,我得到一个 IllegalComponentStateException.它说组件必须显示在屏幕上才能确定其位置".这是因为 JFrame 的 GlassPane 获取了事件.但是 JDialog 的 Glasspane 应该得到它.我认为,JFrame 的 Glasspane 在 JDialog 前面.但为什么呢?

At "target.getLocationOnScree" I get an IllegalComponentStateException, when the JFrame is hidden and I click on the JDialog. It says "component must be showing on the screen to determine its location". This is because the GlassPane of the JFrame gets the event. But the Glasspane of the JDialog should get it. I think, the Glasspane of the JFrame is in front of the JDialog. But why?

感谢您的帮助!

这是一个例子:

import java.awt.AWTEvent;
import java.awt.Frame;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;

import javax.swing.JDialog;
import javax.swing.JFrame;



public class Main {




    static JFrame frame;
static JDialog dialog;


public static void main(String[] args) {

    frame = new JFrame();
     frame.setSize(600,600);
    GlassPane frameGlas = new GlassPane(frame);
    frame.setGlassPane(frameGlas);
    frame.setVisible(true);

    frameGlas.setVisible(true);
    dialog = new JDialog(frame);

    dialog.setSize(100, 100);
    GlassPane dialogGlas = new GlassPane(dialog);

    dialog.setGlassPane(dialogGlas);
    AWTEventListener al = (AWTEventListener) frameGlas;
    Toolkit.getDefaultToolkit().addAWTEventListener(
            al,
            AWTEvent.MOUSE_MOTION_EVENT_MASK
                    | AWTEvent.MOUSE_EVENT_MASK);
    dialogGlas.setVisible(true);
    dialog.setVisible(true);
}


}


import java.awt.AWTEvent;
import java.awt.Point;
import java.awt.Window;
import java.awt.event.AWTEventListener;
import java.awt.event.MouseEvent;

import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class GlassPane extends JPanel implements AWTEventListener {

    /**
     * 
     */
    private static final long serialVersionUID = 5110857185182004819L;

    private final Window target;


    public GlassPane(Window target) {
        super(null);
        this.target = target;

    }



    public void eventDispatched(AWTEvent event) {
        if (event instanceof MouseEvent) {

            MouseEvent originalEvent = (MouseEvent) event;

            MouseEvent e = originalEvent;
        if (target instanceof JDialog) {
                e = SwingUtilities.convertMouseEvent(
                        ((MouseEvent) event).getComponent(),
                        (MouseEvent) event, this);
            }


            if (e.getID() == MouseEvent.MOUSE_PRESSED) {

                Point p  = target.getLocationOnScreen();
                System.out.println(p.getX());
            }
        }

        }



}

推荐答案

查看源代码,您只需要将 frame 的玻璃窗格注册到 AWTListener.现在,从表面上看,这似乎不是一件坏事.AWTListener 将被通知系统中的所有鼠标事件,但实际接收事件的 GlassPane 实例只会知道 frame...

Looking at you source code, you only ever register the frame's glass pane to the AWTListener. Now, on the surface, this doesn't seem like a bad thing. The AWTListener will be notified of ALL the mouse events in the system, but the instance of GlassPane that is actually receiving the events will only know about the frame...

基本上,这意味着 dialogGlas 永远不会收到任何事件,因为它没有注册.

Basically, this means that the dialogGlas will never receive any events, as it's not registered.

首先,您需要将 frameGlasdialogGlas 都注册为侦听器.

First, you need to register both the frameGlas and dialogGlas as listeners.

其次,您不应该试图猜测"目标.MouseEvent(实际上是所有事件)都有一个来源.您应该将源代码与 target 进行比较,以便仅当事件发生在您感兴趣的组件上时才能对事件做出反应......

Second, you shouldn't be trying to "guess" the target. MouseEvent (in fact all events) have a source. You should be comparing the source with the target so you can react to events only when they occur on components you're interested in...

import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.AWTEventListener;
import java.awt.event.MouseEvent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Main {

    static JFrame frame;
    static JDialog dialog;

    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }


                frame = new JFrame();
                frame.setSize(600, 600);
                GlassPane frameGlas = new GlassPane(frame);
                frame.setGlassPane(frameGlas);
                frame.setVisible(true);

                frameGlas.setVisible(true);
                dialog = new JDialog(frame);

                dialog.setSize(100, 100);
                GlassPane dialogGlas = new GlassPane(dialog);
                dialog.setGlassPane(dialogGlas);
                dialogGlas.setVisible(true);
                dialog.setVisible(true);

                // Register a listener for the frameGlas
                Toolkit.getDefaultToolkit().addAWTEventListener(
                        frameGlas,
                        AWTEvent.MOUSE_MOTION_EVENT_MASK
                        | AWTEvent.MOUSE_EVENT_MASK);
                // Register a listener for the dialogGlas
                Toolkit.getDefaultToolkit().addAWTEventListener(
                        dialogGlas,
                        AWTEvent.MOUSE_MOTION_EVENT_MASK
                        | AWTEvent.MOUSE_EVENT_MASK);
            }
        });
    }

    public class GlassPane extends JPanel implements AWTEventListener {

        private static final long serialVersionUID = 5110857185182004819L;
        private final Window target;

        public GlassPane(Window target) {
            super(null);
            this.target = target;

        }

        @Override
        public void eventDispatched(AWTEvent event) {
            if (event instanceof MouseEvent) {

                MouseEvent originalEvent = (MouseEvent) event;

                MouseEvent e = originalEvent;
                Component source = e.getComponent();
                System.out.println("Source: " + source);
                System.out.println("Target: " + target);
                if (target != null && target.equals(source)) {
                    e = SwingUtilities.convertMouseEvent(
                            ((MouseEvent) event).getComponent(),
                            (MouseEvent) event, this);

                    if (e.getID() == MouseEvent.MOUSE_PRESSED) {

                        Point p = target.getLocationOnScreen();
                        System.out.println(p.getX());
                    }
                }
            }
        }
    }
}

现在,在我的脑海中,MouseListener 的问题是它们是贪婪的,它们阻止事件级联超出它们注册的组件.

Now, off the top of my head, the problem you're having with MouseListener is that they are greedy, they prevent the events from cascading beyond the component they are registered on.

相关文章