Вопрос по mouseevent, events, swing, jscrollpane – Почему JScrollPane не реагирует на события колесика мыши?

14

у меня естьJScrollPane содержащий панель сBoxLayout (СТРАНИЦА ОСЬ).

Моя проблема в том, что JScrollPane не реагирует на события колесика мыши. Чтобы сделать это с помощью колесика мыши, мне нужно быть на.JScrollBar

я нашел этонить а у меня нетMouseMotionListener или жеMouseWheelListenerтолькоMouseListener, Я думаю, что моя проблема связана с тем, что мойJScrollPane действовать наJPanel который содержит другие панели сам. Поэтому, когда мышь находится на панели внутриJScrollPane кажется, что событие используется этой панелью, которую я никогда не видел на панели прокрутки.

Есть липравильный способ сделать события, перехваченные детьми панели прокрутки, видимыми для этой панели прокрутки?

SSCCE:

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

Рамка:

public class NewJFrame extends javax.swing.JFrame {

    public NewJFrame() {
        initComponents();
        for (int i = 0; i < 50; i++) {
            jPanel1.add(new TestPanel());
        }
    }

private void initComponents() {
        jScrollPane1 = new javax.swing.JScrollPane();
        jPanel1 = new javax.swing.JPanel();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jPanel1.setLayout(new javax.swing.BoxLayout(jPanel1,    javax.swing.BoxLayout.PAGE_AXIS));
        jScrollPane1.setViewportView(jPanel1);

        getContentPane().add(jScrollPane1, java.awt.BorderLayout.CENTER);

        pack();
    }

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
           @Override
            public void run() {
                new NewJFrame().setVisible(true);
           }
        });
    }
}

ИTestPanel определение:

public class TestPanel extends javax.swing.JPanel {

    public TestPanel() {
        initComponents();
    }

    private void initComponents() {

        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        jScrollPane1 = new javax.swing.JScrollPane();
        jTextArea1 = new javax.swing.JTextArea();

        jLabel1.setText("jLabel1");

        setBackground(new java.awt.Color(255, 51, 51));
        setLayout(new java.awt.BorderLayout());

        jLabel2.setText("TEST LABEL");
        jLabel2.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        add(jLabel2, java.awt.BorderLayout.PAGE_START);

        jTextArea1.setEditable(false);
        jTextArea1.setColumns(20);
        jTextArea1.setRows(5);
        jTextArea1.setFocusable(false);
        jScrollPane1.setViewportView(jTextArea1);

        add(jScrollPane1, java.awt.BorderLayout.CENTER);
   }
}

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

Не могли бы вы опубликовать код, который выиспользуете для создания этих компонентов? Я могу'т воспроизвести вашу проблему ... 1r0n1k
Самое простое решение без влияния на другие компоненты:введите описание ссылки здесь JayC667
+1 для ближнего SSCCE (это 'не компилируется, так как вы забыли объявления полей :-) kleopatra
пожалуйста, покажите SSCCE, который демонстрирует проблему kleopatra
@kleopatra, честно говоря, это моя первая попытка написания СБСЕ. Я допустил ошибку, так как намеренно удалил полевые объявления, потому что я думал, что они будут бесполезны. Прочитав еще раз SSCCE HOWTO, я понял, что был неправ: D nathan

Ваш Ответ

2   ответа
5

ласти. Вы можете попробовать вручную передать событие на родительскую панель прокрутки следующим образом:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class TestScrollPane2 {
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                // might want to use a http://tips4java.wordpress.com/2009/12/20/scrollable-panel/
                JPanel panel = new JPanel(new GridLayout(0, 1));
                for (int i = 0; i < 10; i++) {
                    panel.add(new JScrollPane(new JTextArea(3, 40)) {
                         @Override
                        protected void processMouseWheelEvent(MouseWheelEvent e) {
                            Point oldPosition = getViewport().getViewPosition();
                            super.processMouseWheelEvent(e);

                            if(getViewport().getViewPosition().y == oldPosition.y) {
                                delegateToParent(e);
                            }
                        }

                        private void delegateToParent(MouseWheelEvent e) {
                            // even with scroll bar set to never the event doesn't reach the parent scroll frame
                            JScrollPane ancestor = (JScrollPane) SwingUtilities.getAncestorOfClass(
                                    JScrollPane.class, this);
                            if (ancestor != null) {
                                MouseWheelEvent converted = null;
                                for (MouseWheelListener listener : ancestor
                                        .getMouseWheelListeners()) {
                                    listener.mouseWheelMoved(converted != null ? converted
                                            : (converted = (MouseWheelEvent) SwingUtilities
                                                    .convertMouseEvent(this, e, ancestor)));
                                }
                            }
                        }
                    });
                }
                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                frame.getContentPane().add(new JScrollPane(panel));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}
Лично я бы невозиться с родителем сообщенийs слушателей - достаточно просто отправить сообщение (как только scrollPane решил не обрабатывать его сам) kleopatra
13

Добавляем немного деталей:

Это'верно, что JScrollPane поддерживает mouseWheelHandling. Согласно правилам диспетчеризации mouseEvent, самый верхний (в z-порядке) компонент получает событие, и это 'Панель прокрутки вокруг текстовой области. Поэтому, если прокрутка текстовой области не требуется, простое решение может состоять в том, чтобы отключить поддержку колеса в его scrollPane. И у JScrollPane даже есть API для этого:

scrollPane.setWheelScrollingEnabled(false); 

К сожалению, это нет работа. Причиныне работает то, что это свойство не имеет никакого эффекта в цепочке диспетчеризации событий, которая в конечном счете вызывает в eventTypeEnabled:

case MouseEvent.MOUSE_WHEEL:
      if ((eventMask & AWTEvent.MOUSE_WHEEL_EVENT_MASK) != 0 ||
          mouseWheelListener != null) {
          return true;
      }

Это возвращает истину, если mouseWheelListener установлен - что делается безусловно BasicScrollPaneUI, ине удаляется при изменении свойства wheelEnabled (пользовательский интерфейс недаже не слушайте это свойство ...) Плюс слушатель просто ничего не делает, если свойство ложно. По крайней мере, один из этих фактов является ошибкой, пользовательский интерфейс должен

либо удалить / добавить слушателя в зависимости от wheelEnabledили: внедрить слушателя так, чтобы он отправлял событие вверх по цепочке (как это делает Уолтер в своем примере)

Первый вариант может быть обработан кодом приложения:

scrollPane = new JScrollPane();
scrollPane.removeMouseWheelListener(scrollPane.getMouseWheelListeners()[0]);

Это'Это своего рода хак (так как обходные пути ошибок всегда есть :-), производственный код должен будет прослушивать wheelEnable для переустановки, если необходимо, а также прослушивать изменения LAF для обновления / повторного удаления слушателей, установленных пользовательским интерфейсом.

Реализация второго варианта в небольшой модификации (что касается Уолтераs диспетчеризация) путем создания подкласса JScrollPane и отправки события родительскому элементу, если wheelEnabled имеет значение false:

scrollPane = new JScrollPane() {

    @Override
    protected void processMouseWheelEvent(MouseWheelEvent e) {
        if (!isWheelScrollingEnabled()) {
            if (getParent() != null) 
                getParent().dispatchEvent(
                        SwingUtilities.convertMouseEvent(this, e, getParent()));
            return;
        }
        super.processMouseWheelEvent(e);
    }

};
scrollPane.setWheelScrollingEnabled(false); 

Похожие вопросы