Почему JScrollPane не реагирует на события колесика мыши?

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

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

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

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

SSCCE:

enter image description here

здесь простой тестовый пример пытается показать, когда я пытаюсь сделать в своем приложении 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 кажется, используют событие когда курсор мыши находится внутри него, прокрутка колесиком не работает. Я должен поместить курсор мыши вне текстовой области, чтобы он снова работал.

2 ответов


Уолтер опередил меня в анализе вопроса: -)

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

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

scrollPane.setWheelScrollingEnabled(false); 

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

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

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

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

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

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

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

реализация второго варианта в небольшой модификации (относительно диспетчеризации Уолтера) путем подкласса 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); 

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

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