15

(PAGE AXIS)をJScrollPane含むパネルがあります。BoxLayout

私の問題は、JScrollPaneがマウスホイールイベントに反応しないことです。マウスホイールを使用してスクロールさせるには、上にいる必要がありますJScrollBar

私はこのスレッドを見つけましたが、私にはありませMouseMotionListenerん。私の問題は、他のパネル自体を含むに私の行動があったという事実から来ていると思います。したがって、マウスがその内のパネル上にある場合、イベントはこのパネルによって消費されているように見えます。スクロールペインには表示されません。MouseWheelListenerMouseListenerJScrollPaneJPanelJScrollPane

スクロールペインの子によってキャッチされたイベントをこのスクロールペインに表示する正しい方法はありますか?

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中にあるとき、ホイールを使用したスクロールが機能しないため、はイベントを消費しているようです。再び機能させるには、テキスト領域の外側にマウスカーソルを置く必要があります。

4

2 に答える 2

15

ウォルターは問題を分析するために私を殴りました:-)

少し詳細を追加します。

JScrollPaneがmouseWheelHandlingをサポートしているのは正しいことです。mouseEventディスパッチのルールに従って、最上位(zオーダー)のコンポーネントがイベントを取得します。これは、textAreaの周りのscrollPaneです。したがって、textareaをホイールする必要がない場合、簡単な解決策は、scrollPaneでホイールサポートを無効にすることです。そして、JScrollPaneにはそれを行うためのAPIさえあります:

scrollPane.setWheelScrollingEnabled(false); 

残念ながら、それは機能しません。動作しない理由は、このプロパティが、最終的にeventTypeEnabledを呼び出すイベントディスパッチチェーンに影響を与えないためです。

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

これは、mouseWheelListenerがインストールされている場合にtrueを返します。これは、BasicScrollPaneUIによって無条件に実行され、wheelEnabledプロパティが変更されても削除されません(UIはそのプロパティをリッスンしません...)。さらに、プロパティfalse。それらの事実の少なくとも1つはバグであり、UIは

  • WheelEnabledに応じて、リスナーを削除/追加します
  • または:リスナーを実装して、イベントをチェーンの上位にディスパッチします(Walterが彼の例で行っているように)

最初のオプションは、アプリケーションコードで処理できます。

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

これはちょっとしたハックです(バグ回避策は常に:-)ので、本番コードは必要に応じてwheelEnableをリッスンして再インストールし、さらにLAFの変更をリッスンしてUIによってインストールされたリスナーを更新/再削除する必要があります。

JScrollPaneをサブクラス化し、wheelEnabledがfalseの場合にイベントを親にディスパッチすることにより、わずかな変更(Walterのディスパッチに関して)で2番目のオプションを実装します。

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); 
于 2012-10-16T11:57:56.540 に答える
7

マウスホイールイベントは、テキスト領域の周りのスクロールペインによって消費されます。次のように、イベントを親スクロールペインに手動で渡すことができます。

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);
            }
        });
    }
}
于 2012-10-16T11:00:56.240 に答える