Java Swing: может ли мышь вносить изменения в два наложенных JPanels одновременно?

Я новичок в качелях, и у меня есть следующая проблема:

Я использую библиотеку openslide-java для просмотра целых изображений слайдов. Существует класс OpenSlideView, который расширяет JPanel, и его целью является просмотр изображений. Этот класс имеет возможность увеличения, уменьшения и навигации по изображению с помощью мыши, так как он использует свой собственный слушатель мыши. Я накладываю два объекта OpenSlideView в JFrame, где один виден, а другой нет, и я хочу иметь возможность выполнять действия мыши на обоих из них одновременно, даже если один из них не виден. Проще говоря, я хочу, чтобы невидимая JPanel выслеживала мышь.

Возможно ли что-то подобное? Даже для класса JPanel, а не для его расширения?

Заранее спасибо.

PS: я прилагаю код метода, который обрабатывает события мыши для объектов 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);
}

2 ответа

Решение

я хочу иметь возможность выполнять действия мыши на них обоих одновременно

Не имеет никакого смысла. Если компонент не виден, зачем вам это делать? Может быть, вам нужно, чтобы ваш компонент сохранил свое текущее состояние. Поэтому, когда вы меняете состояние верхнего компонента, вы обновляете состояние скрытого компонента. Если скрытый компонент когда-либо станет видимым, он будет автоматически закрашен в своем текущем состоянии.

Проще говоря, я хочу, чтобы невидимая JPanel выслеживала мышь.

Событие отправляется только одному компоненту.

Тем не менее, вы можете использовать Component.dispatchEvent(...) метод для отправки нового события в отдельный компонент.

Да, вы можете сделать это, но вам понадобится несколько иной дизайн.

Во-первых, обратите внимание на характеристику API документации MouseEvent s:

Событие, которое указывает, что действие мыши произошло в компоненте. Считается, что действие мыши происходит в конкретном компоненте тогда и только тогда, когда курсор мыши находится над незатененной частью границ компонента, когда происходит действие. Для облегченных компонентов, таких как компоненты Swing, события мыши отправляются компоненту только в том случае, если для компонента включен тип события мыши. [...] Если тип события мыши не был включен в компоненте, соответствующие события мыши отправляются первому предку, который активировал тип события мыши.

(Акцент добавлен.)

Таким образом, когда одна из ваших панелей находится позади другой и, следовательно, скрыта, события мыши не могут происходить в скрытой части. Кроме того, хотя в документах не говорится об этом так много слов, из этого следует, что MouseEvents доставляются компоненту, в котором они происходят, или его ближайшему предку в дереве содержания, который принимает события мыши. Swing не будет отправлять события компонентам, таким как скрытая панель, которые не являются предками сдерживания того, в котором произошло событие.

Как же тогда вы можете заставить компоненты братьев и сестер реагировать на одни и те же события мыши? Есть как минимум две альтернативы:

  1. Зарегистрируйте различных слушателей мыши на ближайшем контейнере общего предка, а не на самих панелях. Убедитесь, что панели настроены на то, чтобы сами не получать события мыши. Пусть слушатель, зарегистрированный на предке, уведомит панели о событиях.

  2. Используйте один объект слушателя, который знает обо всех панелях. Зарегистрируйте этот один объект на каждой панели и попросите его выполнить соответствующие действия для каждой панели.

Вы заметите, что на самом деле они не сильно отличаются. Общая тема заключается в том, что ровно один слушатель получит событие от Swing, и он направит соответствующие панели для получения необходимого ответа.

Другие вопросы по тегам