Перехват или делегирование событий с перекрывающимися компонентами - PullRequest
3 голосов
/ 09 января 2012

У меня есть два JPanels равных по размеру, один поверх другого.Верхний слой служит панелью выбора перетаскивания, а к другому добавляются другие компоненты.Моя проблема в том, что обработчики событий мыши этих добавленных компонентов не срабатывают, потому что они обрабатываются панелью наложения.Как я могу по-прежнему перетаскивать поверх этих добавленных компонентов, но при этом для лежащих в основе компонентов mouseEntered и mouseExited включены?

Вот снимок экрана:

enter image description here

Как видите, прямоугольник выделения нарисован на наложении JPanel, но моя мышь не может пройти через эту панель, чтобы увидеть, что находится под ней (в поисках лучшего способа объяснить это).

Ответы [ 2 ]

5 голосов
/ 09 января 2012

Альтернативой переизобретению колеса является использование JLayer (впервые для jdk7, доступно для jdk6 в подпроекте SwingLabs JXLayer ):

JLayer - это универсальный декоратор для компонентов Swing, который позволяет вам реализовать различные передовые эффекты рисования, а также получить уведомления обо всех событиях AWTE, сгенерированных в его границах

Ниже приведен краткий пример - просто чтобы продемонстрировать его использование, логика явно неполная :) - затягивание резинкой с помощью 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);
3 голосов
/ 09 января 2012

Не используйте наложенную панель.В вашем последнем сообщении я высказал предположение о том, как вы можете это сделать.

Или, если вы используете наложенную панель, просто используйте ее для рисования и попросите базовую панель прослушивать события мыши, а затем вызватьперекраска на накладываемой панели.

Редактировать:

Возможно, лучшим подходом является использование MouseListener для отдельных компонентов, а затем для обработки рисунка прямоугольника вы можете использовать Глобальный прослушиватель событий для прослушивания события mousePressed, mouseDragged, mouseReleased.Вам нужно будет проверить источник каждого события, чтобы увидеть, является ли источник самой панелью или компонентом на панели.Вы можете использовать SwingUtilities.isDescendingFrom(...), чтобы помочь с этим вторым тестом.

...