【问题标题】:Intercepting or delegating events with overlapping components拦截或委托具有重叠组件的事件
【发布时间】:2012-02-05 15:42:30
【问题描述】:

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

这是截图:

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

【问题讨论】:

    标签: java swing events paintcomponent miglayout


    【解决方案1】:

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

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

    下面是一个简单的例子——只是为了演示它的用法,逻辑显然不完整:)——用 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();
        }
    }
    

    示例用法 sn-p:

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

    【讨论】:

    • 你的意思是这个svn.java.net/svn/jxlayer~svn/trunk/src/java/org/jdesktop/… Alex,还是我错了
    • @mKorbel 是的,这就是 jdk6 的版本
    • 然后我看到(无法重新测试并且我卸载了 Java7,因为 ice_bear 与此相关)Alex 的稳定版本与 Java7 中实现的 Woodoo 之间的差异,顺便说一句,在这种情况下,Robs comp mover 和 resizer 可能是更好和最简单的选择...
    • @mKorbel 两者的api差别很小,其实例子是对JXLayer中MouseDrawingDemo的jdk7的快速调整。您当然不希望我不反对使用单个类 - 如果没有其他可用的类,它们就很好 - 与 SwingLabs 中稳定且经过测试的框架相比 :-)
    【解决方案2】:

    不要使用覆盖面板。我在您上次发帖时提出了一个建议,说明您可以如何做到这一点。

    或者,如果您确实使用了覆盖面板,那么只需将其用于绘图并让底层面板侦听鼠标事件,然后在覆盖面板上调用重新绘制。

    编辑:

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

    【讨论】:

    • 问题在于我无法从其中一个组件中拖动。让覆盖面板处理鼠标事件意味着我可以从彩色块中拖动。我会检查你的其他建议。
    • 好的,覆盖 paint 有效,但仍然:“The problem with that was that I couldn't drag from within one of the components.
    • 不要将 MouseListeners 添加到其他组件,然后鼠标事件应该传递给面板。
    • 但是如果我需要它们怎么办?我需要mouseClicked 和翻转/mouseEntered/mouseExited
    • 我可以检查是否有任何组件包含鼠标Point?
    猜你喜欢
    • 1970-01-01
    • 2015-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-11
    • 1970-01-01
    • 2017-08-22
    相关资源
    最近更新 更多