概要

JLayerを使って、ネストするJScrollPaneへのMouseWheelEventを転送し、スクロールが継続するように設定します。

サンプルコード

class WheelScrollLayerUI extends LayerUI<JScrollPane> {
  @Override public void installUI(JComponent c) {
    super.installUI(c);
    if (c instanceof JLayer) {
      ((JLayer) c).setLayerEventMask(AWTEvent.MOUSE_WHEEL_EVENT_MASK);
    }
  }

  @Override public void uninstallUI(JComponent c) {
    if (c instanceof JLayer) {
      ((JLayer) c).setLayerEventMask(0);
    }
    super.uninstallUI(c);
  }

  @Override protected void processMouseWheelEvent(
      MouseWheelEvent e, JLayer<? extends JScrollPane> l) {
    Component c = e.getComponent();
    int dir = e.getWheelRotation();
    JScrollPane main = l.getView();
    if (c instanceof JScrollPane && !c.equals(main)) {
      JScrollPane child = (JScrollPane) c;
      BoundedRangeModel m = child.getVerticalScrollBar().getModel();
      int ext = m.getExtent();
      int min = m.getMinimum();
      int max = m.getMaximum();
      int value = m.getValue();
      if (value + ext >= max && dir > 0 || value <= min && dir < 0) {
        main.dispatchEvent(SwingUtilities.convertMouseEvent(c, e, main));
      }
    }
  }
}
View in GitHub: Java, Kotlin

解説

  • ネストしているJScrollPaneの子JScrollPane上でマウスイベントが発生した場合、MouseWheelEventなどのイベントは子JScrollPane内で消費されて親JScrollPaneには伝搬しない
  • 上記のサンプルでは、子JScrollPaneの縦スクロールバーが最下部にあるなら下方向(最上部なら上方向)のMouseWheelEventは親JScrollPaneに転送するLayerUIを作成し、これを親JScrollPaneJLayer<JScrollPane>に適用している

参考リンク

コメント