为什么 JScrollPane 对鼠标滚轮事件没有反应? [英] Why JScrollPane does not react to mouse wheel events?

查看:52
本文介绍了为什么 JScrollPane 对鼠标滚轮事件没有反应?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a JScrollPane containing a panel with a BoxLayout (PAGE AXIS).

我的问题是 JScrollPane 对鼠标滚轮事件没有反应.要使用鼠标滚轮滚动,我需要在 JScrollBar 上.

My problem is that the JScrollPane does not react to mouse wheel events. To make it scroll using the mouse wheel i need to be on the JScrollBar.

我发现了这个 线程 并且我没有 MouseMotionListenerMouseWheelListener,只有一个MouseListener.我认为我的问题来自这样一个事实,即我的 JScrollPane 作用于包含其他面板本身的 JPanel.因此,当鼠标位于 JScrollPane 内的面板上时,似乎该面板消耗了该面板,我从未在滚动窗格中看到过.

I found this thread and i have no MouseMotionListener or MouseWheelListener, only a MouseListener. I think my problem come from the fact that my JScrollPane act on a JPanel that contains other panels itself. So when the mouse is on a panel within the JScrollPane it seems that the event is consumed by this panel i never seen by the scroll pane.

是否有一种正确方法可以使滚动窗格的子级捕获的事件对该滚动窗格可见?

Is there a correct way to make the events caught by the children of the scroll pane visible to this scroll pane?

SSCCE:

这是一个简单的测试用例,试图显示我在 Swing 应用程序中尝试执行的操作.

Here a simple test case trying to show when i try to do in my Swing application.

框架:

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 似乎消耗了该事件,因为当鼠标光标位于其中时,使用滚轮的滚动不起作用.我必须将鼠标光标放在文本区域之外才能使其再次工作.

The JTextArea seems to consume the event since when the mouse cursor is inside it, the scrolling using wheel does not work. I have to put the mouse cursor outside the text area to make it works again.

推荐答案

Walter 击败了我来分析问题 :-)

Walter beat me to analysing the issue :-)

补充一点细节:

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

It's correct that a JScrollPane supports mouseWheelHandling. According to the rules of mouseEvent dispatching, the top-most (in z-order) component gets the event, and that's the scrollPane around the textArea. So if wheeling the textarea is not required, a simple solution might be to disable the wheel-support in its scrollPane. And JScrollPane even has api for doing it:

scrollPane.setWheelScrollingEnabled(false); 

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

Unfortunately, that doesn't work. Reason it's not working is that this property has no effect in the event dispatch chain which ultimately calls into eventTypeEnabled:

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

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

This returns true if a mouseWheelListener is installed - which is done unconditionally by BasicScrollPaneUI, and not removed when the wheelEnabled property is changed (the ui doesn't even listen to that property ...) Plus the listener simply does nothing if the property is false. At least one of those facts is a bug, the ui should

  • 根据wheelEnabled删除/添加监听器
  • 或:实现侦听器,使其将事件向上分派(就像 Walter 在他的示例中所做的那样)

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

The first option can be handled by application code:

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

这有点麻烦(因为错误解决方法总是 :-),生产代码必须监听 wheelEnable 以在需要时重新安装,并监听 LAF 更改以更新/重新删除安装的监听器用户界面.

it's a bit of a hack (as bug-workarounds always are :-), production code would have to listen to the wheelEnable to re-install if needed plus listen to LAF changes to update/re-remove the listeners installed by the ui.

通过对 JScrollPane 进行子类化并在 wheelEnabled 为 false 时将事件分派给父级,稍微修改(关于 Walter 的分派)实现第二个选项:

Implementing the second option in slight modification (as to Walter's dispatching) by subclassing the JScrollPane and dispatch the event to parent if the wheelEnabled is 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); 

这篇关于为什么 JScrollPane 对鼠标滚轮事件没有反应?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆