Java Swing:鼠标可以同时对两个重叠的JPanel 进行更改吗?

Java Swing: Can the mouse make changes to two overlayed JPanels at the same time?

我是 swing 新手,遇到以下问题:

我正在使用 openslide-java 库来查看整个幻灯片图像。有一个扩展 JPanel 的名为 OpenSlideView 的 class,其目的是查看图像。此 class 具有使用鼠标放大、缩小和浏览图像的功能,因为它使用自己的鼠标侦听器。我在 JFrame 中覆盖两个 OpenSlideView 对象,其中一个可见,另一个不可见,我希望能够同时对它们执行鼠标操作,即使其中一个不可见。说白了,我想要不可见的 JPanel 来追踪鼠标。

这样的事情可能吗?即使是 JPanel class 而不是它的扩展?

提前致谢。

P.S: 我附上处理 OpenSlideView 对象的鼠标事件的方法的代码,以防有帮助。

   private void registerEventHandlers() {
    // mouse wheel
    addMouseWheelListener(new MouseWheelListener() {
        public void mouseWheelMoved(MouseWheelEvent e) {
            double ds1 = zoomHelper(OpenSlideView.this, e.getX(), e.getY(),
                    e.getWheelRotation());
            double ds2 = zoomHelper(otherView, e.getX(), e.getY(), e
                    .getWheelRotation());
            zoomHelper2(OpenSlideView.this, ds1, e.getX(), e.getY());
            zoomHelper2(otherView, ds2, e.getX(), e.getY());
            zoomHelper3(OpenSlideView.this, ds1);
            zoomHelper3(otherView, ds2);
            repaintHelper(OpenSlideView.this);
            repaintHelper(otherView);
        }
    });

    // mouse drag
    MouseAdapter ma = new MouseAdapter() {
        private SelectionMode selectionMode;

        private int oldX;

        private int oldY;

        private int slideStartX;

        private int slideStartY;

        private Path2D.Double freehandPath;

        @Override
        public void mousePressed(MouseEvent e) {
            // System.out.println(e);

            requestFocusInWindow();

            if (!SwingUtilities.isLeftMouseButton(e)) {
                return;
            }

            final int ellipseMask = MouseEvent.CTRL_DOWN_MASK
                    | MouseEvent.SHIFT_DOWN_MASK;
            final int freehandMask = MouseEvent.CTRL_DOWN_MASK;
            final int rectMask = MouseEvent.SHIFT_DOWN_MASK;

            if ((e.getModifiersEx() & ellipseMask) == ellipseMask) {
                selectionMode = SelectionMode.ELLIPSE;
            } else if ((e.getModifiersEx() & freehandMask) == freehandMask) {
                selectionMode = SelectionMode.FREEHAND;
            } else if ((e.getModifiersEx() & rectMask) == rectMask) {
                selectionMode = SelectionMode.RECT;
            } else {
                selectionMode = SelectionMode.NONE;
            }

            oldX = e.getX();
            oldY = e.getY();

            double ds = getDownsample();
            slideStartX = (int) ((oldX + viewPosition.x) * ds);
            slideStartY = (int) ((oldY + viewPosition.y) * ds);
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            if (!SwingUtilities.isLeftMouseButton(e)) {
                return;
            }

            int relX = oldX - e.getX();
            int relY = oldY - e.getY();

            double ds = getDownsample();
            int dx = slideStartX;
            int dy = slideStartY;
            int dw = (int) ((e.getX() + viewPosition.x) * ds) - dx;
            int dh = (int) ((e.getY() + viewPosition.y) * ds) - dy;

            if (dw < 0) {
                dx += dw;
                dw = -dw;
            }
            if (dh < 0) {
                dy += dh;
                dh = -dh;
            }

            switch (selectionMode) {
            case NONE:
                translateHelper(OpenSlideView.this, relX, relY);
                translateHelper(otherView, relX, relY);
                repaintHelper(OpenSlideView.this);
                repaintHelper(otherView);
                break;

            case RECT:
                selectionBeingDrawn = new Rectangle(dx, dy, dw, dh);
                // System.out.println(selection);
                repaint();
                break;

            case FREEHAND:
                if (selectionBeingDrawn == null) {
                    // new selection
                    freehandPath = new Path2D.Double();
                    selectionBeingDrawn = freehandPath;

                    freehandPath.moveTo(slideStartX, slideStartY);
                }

                freehandPath.lineTo((e.getX() + viewPosition.x) * ds, (e
                        .getY() + viewPosition.y)
                        * ds);

                repaint();
                break;

            case ELLIPSE:
                selectionBeingDrawn = new Ellipse2D.Double(dx, dy, dw, dh);

                repaint();
                break;
            }
            oldX = e.getX();
            oldY = e.getY();
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            if (selectionMode == SelectionMode.FREEHAND) {
                freehandPath.closePath();
            }
            selectionMode = SelectionMode.NONE;

            if (selectionBeingDrawn != null) {
                Rectangle bb = selectionBeingDrawn.getBounds();
                if (bb.height != 0 && bb.width != 0) {
                    selections.add(new DefaultAnnotation(selectionBeingDrawn));
                    selectionBeingDrawn = null;
                }
            }
            repaint();
        }

        @Override
        public void mouseEntered(MouseEvent e) {
            selectionsVisibleHelper(OpenSlideView.this, true);
            selectionsVisibleHelper(otherView, true);
        }

        @Override
        public void mouseExited(MouseEvent e) {
            selectionsVisibleHelper(OpenSlideView.this, false);
            selectionsVisibleHelper(otherView, false);
        }
    };
    addMouseListener(ma);
    addMouseMotionListener(ma);

    // keyboard
    InputMap inputMap = new InputMap();
    ActionMap actionMap = new ActionMap();

    inputMap.put(KeyStroke.getKeyStroke("SPACE"), "center");
    actionMap.put("center", new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            centerHelper(OpenSlideView.this);
            centerHelper(otherView);
            repaintHelper(OpenSlideView.this);
            repaintHelper(otherView);
        }
    });

    inputMap.put(KeyStroke.getKeyStroke("UP"), "scroll up");
    inputMap.put(KeyStroke.getKeyStroke("W"), "scroll up");
    actionMap.put("scroll up", new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            translateHelper(OpenSlideView.this, 0, -KEYBOARD_SCROLL_AMOUNT);
            translateHelper(otherView, 0, -KEYBOARD_SCROLL_AMOUNT);
            repaintHelper(OpenSlideView.this);
            repaintHelper(otherView);
        }
    });

    inputMap.put(KeyStroke.getKeyStroke("DOWN"), "scroll down");
    inputMap.put(KeyStroke.getKeyStroke("S"), "scroll down");
    actionMap.put("scroll down", new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            translateHelper(OpenSlideView.this, 0, KEYBOARD_SCROLL_AMOUNT);
            translateHelper(otherView, 0, KEYBOARD_SCROLL_AMOUNT);
            repaintHelper(OpenSlideView.this);
            repaintHelper(otherView);
        }
    });

    inputMap.put(KeyStroke.getKeyStroke("LEFT"), "scroll left");
    inputMap.put(KeyStroke.getKeyStroke("A"), "scroll left");
    actionMap.put("scroll left", new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            translateHelper(OpenSlideView.this, -KEYBOARD_SCROLL_AMOUNT, 0);
            translateHelper(otherView, -KEYBOARD_SCROLL_AMOUNT, 0);
            repaintHelper(OpenSlideView.this);
            repaintHelper(otherView);
        }
    });

    inputMap.put(KeyStroke.getKeyStroke("RIGHT"), "scroll right");
    inputMap.put(KeyStroke.getKeyStroke("D"), "scroll right");
    actionMap.put("scroll right", new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            translateHelper(OpenSlideView.this, KEYBOARD_SCROLL_AMOUNT, 0);
            translateHelper(otherView, KEYBOARD_SCROLL_AMOUNT, 0);
            repaintHelper(OpenSlideView.this);
            repaintHelper(otherView);
        }
    });

    inputMap.put(KeyStroke.getKeyStroke("L"), "rotate left");
    actionMap.put("rotate left", new AbstractAction() {
        public void actionPerformed(ActionEvent e) {

        }
    });

    inputMap.put(KeyStroke.getKeyStroke("R"), "rotate right");
    actionMap.put("rotate right", new AbstractAction() {
        public void actionPerformed(ActionEvent e) {

        }
    });

    inputMap.put(KeyStroke.getKeyStroke("PLUS"), "zoom in");
    inputMap.put(KeyStroke.getKeyStroke("EQUALS"), "zoom in");
    actionMap.put("zoom in", new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            double d1 = zoomHelper(OpenSlideView.this, -1);
            double d2 = zoomHelper(otherView, -1);
            zoomHelper2(OpenSlideView.this, d1);
            zoomHelper2(otherView, d2);
            zoomHelper3(OpenSlideView.this, d1);
            zoomHelper3(otherView, d2);
            repaintHelper(OpenSlideView.this);
            repaintHelper(otherView);
        }
    });

    inputMap.put(KeyStroke.getKeyStroke("MINUS"), "zoom out");
    actionMap.put("zoom out", new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            double d1 = zoomHelper(OpenSlideView.this, 1);
            double d2 = zoomHelper(otherView, 1);
            zoomHelper2(OpenSlideView.this, d1);
            zoomHelper2(otherView, d2);
            zoomHelper3(OpenSlideView.this, d1);
            zoomHelper3(otherView, d2);
            repaintHelper(OpenSlideView.this);
            repaintHelper(otherView);
        }
    });

    inputMap.put(KeyStroke.getKeyStroke("Z"), "zoom to fit");
    actionMap.put("zoom to fit", new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            // System.out.println("zoom");
            zoomToFit();
            centerSlidePrivate();
            paintBackingStore();
            repaint();
        }
    });

    inputMap.put(KeyStroke.getKeyStroke("1"), "zoom to 1:1");
    actionMap.put("zoom to 1:1", new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            // System.out.println("zoom 1:1");
            double d1 = zoomHelper(OpenSlideView.this, Integer.MIN_VALUE);
            double d2 = zoomHelper(otherView, Integer.MIN_VALUE);
            zoomHelper2(OpenSlideView.this, d1);
            zoomHelper2(otherView, d2);
            zoomHelper3(OpenSlideView.this, d1);
            zoomHelper3(otherView, d2);
            repaintHelper(OpenSlideView.this);
            repaintHelper(otherView);
        }
    });

    inputMap.put(KeyStroke.getKeyStroke("BACK_QUOTE"), "toggle pins");
    actionMap.put("toggle pins", new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            selectionsAsPins = !selectionsAsPins;
            if (otherView != null) {
                otherView.selectionsAsPins = !otherView.selectionsAsPins;
            }
            repaintHelper(OpenSlideView.this);
            repaintHelper(otherView);
        }
    });

    // install as parents
    InputMap oldInputMap = getInputMap();
    ActionMap oldActionMap = getActionMap();
    inputMap.setParent(oldInputMap.getParent());
    oldInputMap.setParent(inputMap);
    actionMap.setParent(oldActionMap.getParent());
    oldActionMap.setParent(actionMap);
}

i want to be able to perform mouse actions on both of them at the same time

没有任何意义。如果一个组件不可见,你为什么要这样做。也许您需要让您的组件保持其当前状态。因此,当您更改顶级组件的状态时,您会更新隐藏组件的状态。如果隐藏的组件变得可见,它将自动以当前状态绘制。

Plainly put, i want the not-visible JPanel to track down the mouse.

一个事件只会分派给一个组件。

但是,您可以使用 Component.dispatchEvent(...) 方法将新事件分派到单独的组件。

是的,您可以这样做,但您需要稍微不同的设计。

首先注意the API docs's characterization of MouseEvents:

An event which indicates that a mouse action occurred in a component. A mouse action is considered to occur in a particular component if and only if the mouse cursor is over the unobscured part of the component's bounds when the action happens. For lightweight components, such as Swing's components, mouse events are only dispatched to the component if the mouse event type has been enabled on the component. [...] If the mouse event type has not been enabled on the component, the corresponding mouse events are dispatched to the first ancestor that has enabled the mouse event type.

(强调已添加。)

因此,当您的一个面板在另一个面板后面时——因此被遮挡了——鼠标事件不会发生在被遮挡的部分。此外,尽管文档没有用那么多的话来说明,但您应该从中得到的是 MouseEvents 被传递给它们本身出现的组件,或者传递给它在接受鼠标的包含树中最近的祖先事件。 Swing 不会将事件分派给不是事件发生所在组件的包含祖先的组件,例如被遮挡的面板。

那么,如何才能使同级或近亲组件响应相同的鼠标事件?至少有两种选择:

  1. 在最近的共享祖先容器上注册各种鼠标侦听器,而不是在面板本身上。确保面板配置为自己接收鼠标事件。让在祖先上注册的侦听器在事件发生时通知面板。

  2. 使用了解所有面板的单个 侦听器对象。在每个面板上注册一个对象,并让它对每个面板采取适当的操作。

您会注意到它们实际上并没有太大区别。共同的主题是只有一个侦听器将从 Swing 接收事件,并且它将指示适当的面板做出所需的响应。