拦截或委托具有重叠组件的事件

发布于 2024-12-25 14:58:51 字数 366 浏览 1 评论 0原文

我有两个大小相等的 JPanel,一个在另一个的顶部。顶层用作拖动选择面板,另一层添加了其他组件。我的问题是这些添加的组件的鼠标事件处理程序不会被触发,因为它们是由覆盖面板处理的。我怎样才能仍然拖动这些添加的组件的顶部,但仍然为底层组件启用 mouseEnteredmouseExited

这是屏幕截图:

在此处输入图像描述

如您所见,选择矩形绘制在覆盖的 JPanel 上,但就好像我的鼠标无法穿过这个面板来查看下面的内容(寻找更好的方法来解释这一点)。

I have two JPanels equal in size, one over the top of the other. The top layer serves as a drag selection panel, and the other one has other components added to it. My problem is that the mouse event handlers of these added components aren't triggered, because they are handled by the overlaying panel instead. How can I still drag over the top of these added components, but still have mouseEntered and mouseExited enabled for the underlaying components?

Here is a screenshot:

enter image description here

As you can see, the selection rectangle is painted on the overlaying JPanel, but it's as if my mouse can't get through this panel to see what's underneath (in search of a better way to explain that).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

蓝眸 2025-01-01 14:58:52

重新发明轮子的另一种方法是使用 JLayer(jdk7 的新功能,可在 SwingLabs 子项目 JXLayer 中用于 jdk6) :

JLayer 是 Swing 组件的通用装饰器,它使您能够
实现各种高级绘画效果以及接收
在其边界内生成的所有 AWTEvent 的通知

下面是一个简单的示例 - 只是为了演示其用法,逻辑显然不完整:) - 使用 mouseEvents 跨越橡皮筋

// UI which allows to span a rubberband on top of the component
public static class RubberBandUI<V extends JComponent> extends LayerUI<V> {
    private JLayer<?> l;
    private Rectangle rubberband;
    private boolean selecting;

    @Override
    public void installUI(JComponent c) {
        super.installUI(c);
        l = (JLayer<?>) c;
        // this LayerUI will receive mouse/motion events
        l.setLayerEventMask(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
     }

     @Override
    public void uninstallUI(JComponent c) {
        super.uninstallUI(c);
        // JLayer must be returned to its initial state
        l.setLayerEventMask(0);
        l = null;
     }

    @Override
    public void paint(Graphics g, JComponent l) {
        // this paints layer as is
        super.paint(g, l);
        if (rubberband == null) return;
        Graphics2D g2 = (Graphics2D) g;
        // custom painting is here
        g2.setColor(Color.RED);
        g2.setStroke(new BasicStroke(2f));
        g2.draw(rubberband);
    }

    // intercept events as appropriate 

    @Override
    protected void processMouseMotionEvent(MouseEvent e, JLayer<? extends V> l) {
        super.processMouseMotionEvent(e, l);
        if (e.getID() == MouseEvent.MOUSE_DRAGGED && selecting) {
            Point point = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), l);
            adjustRubberband(point);
            l.repaint();
        }
    }

    @Override
    protected void processMouseEvent(MouseEvent e, JLayer<? extends V> l) {
        super.processMouseEvent(e, l);
        if (e.getID() == MouseEvent.MOUSE_RELEASED) {
            endRubberband();
        }
        if (e.getID() == MouseEvent.MOUSE_PRESSED && e.getSource() == l) {
            startRubberband(e.getPoint());
        }
    }

    // logic to start/stop/adjust the rubberband 
    private void adjustRubberband(Point point) {
        // logic to span the rubberband
        int width = point.x - rubberband.x;
        int height = point.y - rubberband.y;
        rubberband.setSize(width, height);
    }

    private void startRubberband(Point p) {
        rubberband = new Rectangle(p);
        selecting = true;
        // block events to child components while drawing
        l.getGlassPane().setVisible(true);
        l.repaint();
    }

    private void endRubberband() {
        selecting = false;
        l.getGlassPane().setVisible(false);
        l.repaint();
    }

    public void clear() {
        rubberband = null;
        l.repaint();
    }
}

示例使用片段:

JPanel panel = new JPanel();
for (int i = 0; i < 3; i++) {
    panel.add(new JButton("JButton"));
    panel.add(new JCheckBox("JCheckBox"));
    panel.add(new JTextField("JTextField"));
}
JLayer<JComponent> l = new JLayer<JComponent>(panel, new RubberBandUI<JComponent>());
frame.add(l);

An alternative to re-inventing the wheel is using JLayer (new to jdk7, available for jdk6 in the SwingLabs subproject JXLayer):

JLayer is a universal decorator for Swing components which enables you
to implement various advanced painting effects as well as receive
notifications of all AWTEvents generated within its borders

Below is a quick example - just to demonstrate its usage, logic obviously incomplete :) - of spanning a rubberband with mouseEvents

// UI which allows to span a rubberband on top of the component
public static class RubberBandUI<V extends JComponent> extends LayerUI<V> {
    private JLayer<?> l;
    private Rectangle rubberband;
    private boolean selecting;

    @Override
    public void installUI(JComponent c) {
        super.installUI(c);
        l = (JLayer<?>) c;
        // this LayerUI will receive mouse/motion events
        l.setLayerEventMask(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
     }

     @Override
    public void uninstallUI(JComponent c) {
        super.uninstallUI(c);
        // JLayer must be returned to its initial state
        l.setLayerEventMask(0);
        l = null;
     }

    @Override
    public void paint(Graphics g, JComponent l) {
        // this paints layer as is
        super.paint(g, l);
        if (rubberband == null) return;
        Graphics2D g2 = (Graphics2D) g;
        // custom painting is here
        g2.setColor(Color.RED);
        g2.setStroke(new BasicStroke(2f));
        g2.draw(rubberband);
    }

    // intercept events as appropriate 

    @Override
    protected void processMouseMotionEvent(MouseEvent e, JLayer<? extends V> l) {
        super.processMouseMotionEvent(e, l);
        if (e.getID() == MouseEvent.MOUSE_DRAGGED && selecting) {
            Point point = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), l);
            adjustRubberband(point);
            l.repaint();
        }
    }

    @Override
    protected void processMouseEvent(MouseEvent e, JLayer<? extends V> l) {
        super.processMouseEvent(e, l);
        if (e.getID() == MouseEvent.MOUSE_RELEASED) {
            endRubberband();
        }
        if (e.getID() == MouseEvent.MOUSE_PRESSED && e.getSource() == l) {
            startRubberband(e.getPoint());
        }
    }

    // logic to start/stop/adjust the rubberband 
    private void adjustRubberband(Point point) {
        // logic to span the rubberband
        int width = point.x - rubberband.x;
        int height = point.y - rubberband.y;
        rubberband.setSize(width, height);
    }

    private void startRubberband(Point p) {
        rubberband = new Rectangle(p);
        selecting = true;
        // block events to child components while drawing
        l.getGlassPane().setVisible(true);
        l.repaint();
    }

    private void endRubberband() {
        selecting = false;
        l.getGlassPane().setVisible(false);
        l.repaint();
    }

    public void clear() {
        rubberband = null;
        l.repaint();
    }
}

Sample usage snippet:

JPanel panel = new JPanel();
for (int i = 0; i < 3; i++) {
    panel.add(new JButton("JButton"));
    panel.add(new JCheckBox("JCheckBox"));
    panel.add(new JTextField("JTextField"));
}
JLayer<JComponent> l = new JLayer<JComponent>(panel, new RubberBandUI<JComponent>());
frame.add(l);
嗼ふ静 2025-01-01 14:58:52

不要使用覆盖面板。我在您上一篇文章中就如何做到这一点提出了建议。

或者,如果您确实使用覆盖面板,则只需使用它进行绘图,并让底层面板侦听鼠标事件,然后调用覆盖面板上的重绘。

编辑:

也许更好的方法是在各个组件上使用 MouseListener,然后处理矩形的绘制,您可以使用 全局事件监听器 用于监听 mousePressed、mouseDragged、mouseReleased 事件。您需要检查每个事件的来源,以查看来源是否是面板本身或面板上的组件。您可以使用 SwingUtilities.isDescendingFrom(...) 来帮助完成第二个测试。

Don't use an overlaying panel. I gave a suggestion in your last posting on how you might do this.

Or, if you do use an overlaying panel, then just use it for drawing and have the underlying panel listen for the mouse events and then invoke the repainting on the overlaying panel.

Edit:

Maybe a better approach is to use a MouseListener on the individual components and then to handle the drawing of the rectangle you can use a Global Event Listener to listen for the mousePressed, mouseDragged, mouseReleased event. You would need to check the source of each event to see if the source was the panel itself oa component on the panel. You can use SwingUtilities.isDescendingFrom(...) to help with this second test.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文