• title: JScrollPane内にある複数Componentを配置したJPanelをJLayerを使ってドラッグスクロール tags: [JScrollPane, JPanel, JLayer] author: aterai pubdate: 2013-08-05T11:49:22+09:00 description: 複数のネストしたComponentを配置したJPanelのドラッグイベントを、JScrollPaneをラップしたJLayerで受け取ってスクロール可能にします。

概要

複数のネストしたComponentを配置したJPanelのドラッグイベントを、JScrollPaneをラップしたJLayerで受け取ってスクロール可能にします。

サンプルコード

class DragScrollLayerUI extends LayerUI<JScrollPane> {
  private final Point pp = new Point();
  @Override public void installUI(JComponent c) {
    super.installUI(c);
    JLayer jlayer = (JLayer)c;
    jlayer.setLayerEventMask(
        AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
  }
  @Override public void uninstallUI(JComponent c) {
    JLayer jlayer = (JLayer)c;
    jlayer.setLayerEventMask(0);
    super.uninstallUI(c);
  }
  @Override protected void processMouseEvent(
      MouseEvent e, JLayer<? extends JScrollPane> l) {
    Component c = e.getComponent();
    if(c instanceof JScrollBar || c instanceof JSlider) {
      return;
    }
    if(e.getID()==MouseEvent.MOUSE_PRESSED) {
      JViewport vport = l.getView().getViewport();
      Point cp = SwingUtilities.convertPoint(c, e.getPoint(), vport);
      pp.setLocation(cp);
    }
  }
  @Override protected void processMouseMotionEvent(
      MouseEvent e, JLayer<? extends JScrollPane> l) {
    Component c = e.getComponent();
    if(c instanceof JScrollBar || c instanceof JSlider) {
      return;
    }
    if(e.getID()==MouseEvent.MOUSE_DRAGGED) {
      JViewport vport = l.getView().getViewport();
      JComponent cmp = (JComponent)vport.getView();
      Point cp = SwingUtilities.convertPoint(c, e.getPoint(), vport);
      Point vp = vport.getViewPosition();
      vp.translate(pp.x-cp.x, pp.y-cp.y);
      cmp.scrollRectToVisible(new Rectangle(vp, vport.getSize()));
      pp.setLocation(cp);
    }
  }
}
View in GitHub: Java, Kotlin

解説

  • 左: DragScrollListener
    • JTreeの余白をドラッグしてスクロール
    • このリスナーを追加した一つのComponentをドラッグしてスクロールが可能
    • JPanelを掴んでスクロールすることができるが、その子コンポーネントのJTabbedPaneなどをドラッグしてもスクロールはできない
  • 右: DragScrollLayer
    • すべての下位コンポーネントのMouseEventをキャッチするようJLayer#setLayerEventMask(...)と設定したJLayerを作成
    • このJLayerJScrollPaneをラップし、子にマウスイベントを消費するコンポーネントがあっても、ビューであるJPanelをドラッグに応じてスクロール
    • JScrollBarJSliderのように、ノブのドラッグを使用するコンポーネントは二重にスクロールしてしまうので除外

参考リンク

コメント