为什么JScrollPane不响应鼠标滚轮事件?

nat*_*han 14 events swing mouseevent jscrollpane

我有一个JScrollPane包含BoxLayout(PAGE AXIS)的面板.

我的问题是JScrollPane不响应鼠标滚轮事件.要使用鼠标滚轮滚动我需要在JScrollBar.

我发现这个线索,我没有MouseMotionListenerMouseWheelListener只有一个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);
           }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
   }
}
Run Code Online (Sandbox Code Playgroud)

JTextArea似乎消耗,因为当鼠标光标是它里面的情况下,利用滚动轮不工作.我必须将鼠标光标放在文本区域之外,以使其再次工作.

kle*_*tra 13

沃尔特打我分析这个问题:-)

添加一些细节:

JScrollPane支持mouseWheelHandling是正确的.根据mouseEvent调度的规则,最顶层(在z顺序中)组件获取事件,这是textArea周围的scrollPane.因此,如果不需要转动textarea,一个简单的解决方案可能是在其scrollPane中禁用wheel-support.而JScrollPane甚至还有api这样做:

scrollPane.setWheelScrollingEnabled(false); 
Run Code Online (Sandbox Code Playgroud)

不幸的是,这不起作用.它不起作用的原因是该属性对最终调用eventTypeEnabled的事件调度链没有影响:

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

如果安装了mouseWheelListener,则返回true - 这是由BasicScrollPaneUI无条件完成的,并且在wheelEnabled属性更改时被删除(ui甚至不监听该属性...)如果属性是,则监听器根本不执行任何操作假.这些事实中至少有一个是错误,ui应该

  • 根据wheelEnabled删除/添加监听器
  • 或:实现侦听器,以便将事件调度到链中(正如Walter在他的示例中所做的那样)

第一个选项可以由应用程序代码处理:

scrollPane = new JScrollPane();
scrollPane.removeMouseWheelListener(scrollPane.getMouseWheelListeners()[0]);
Run Code Online (Sandbox Code Playgroud)

这是一个黑客攻击(因为bug-workarounds总是:-),如果需要,生产代码必须监听wheelEnable重新安装,并听取LAF更改以更新/重新删除由ui安装的监听器.

通过子类化JScrollPane并在wheelEnabled为false的情况下将事件分派给父实现稍微修改(对于Walter的调度)实现第二个选项:

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); 
Run Code Online (Sandbox Code Playgroud)


Wal*_*aan 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);
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 就我个人而言,我不会打扰父级侦听器的消息传递 - 简单的调度就足够了(一旦scrollPane决定不自行处理它) (2认同)