---
category: swing
folder: HandDragScrollableTable
title: JTableをスクロールバー無しのドラッグでスクロールする
tags: [JTable, JScrollPane, MouseMotionListener, Timer]
author: aterai
pubdate: 2017-12-25T14:36:51+09:00
description: JTableをスクロールバーではなく、内部の行をマウスでドラッグすることでスクロール可能になるよう設定します。
image: https://drive.google.com/uc?id=10Tv7RlmeMiqhXBuq5fgixQ3v4KR8p5_9
---
* 概要 [#summary]
`JTable`をスクロールバーではなく、内部の行をマウスでドラッグすることでスクロール可能になるよう設定します。

#download(https://drive.google.com/uc?id=10Tv7RlmeMiqhXBuq5fgixQ3v4KR8p5_9)

* サンプルコード [#sourcecode]
#code(link){{
class DragScrollingListener extends MouseAdapter {
  protected static final int VELOCITY = 5;
  protected static final int DELAY = 10;
  protected static final double GRAVITY = .95;
  protected final Cursor dc = Cursor.getDefaultCursor();
  protected final Cursor hc = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
  protected final Timer scroller;
  protected final Point startPt = new Point();
  protected final Point delta = new Point();

  protected DragScrollingListener(JComponent c) {
    super();
    this.scroller = new Timer(DELAY, e -> {
      JViewport vport = (JViewport) SwingUtilities.getUnwrappedParent(c);
      Point vp = vport.getViewPosition();
      vp.translate(-delta.x, -delta.y);
      c.scrollRectToVisible(new Rectangle(vp, vport.getSize()));
      if (Math.abs(delta.x) > 0 || Math.abs(delta.y) > 0) {
        delta.setLocation((int) (delta.x * GRAVITY), (int) (delta.y * GRAVITY));
      } else {
        ((Timer) e.getSource()).stop();
      }
    });
  }

  @Override public void mousePressed(MouseEvent e) {
    Component c = e.getComponent();
    c.setCursor(hc);
    c.setEnabled(false);
    Container p = SwingUtilities.getUnwrappedParent(c);
    if (p instanceof JViewport) {
      JViewport vport = (JViewport) p;
      Point cp = SwingUtilities.convertPoint(c, e.getPoint(), vport);
      startPt.setLocation(cp);
      scroller.stop();
    }
  }

  @Override public void mouseDragged(MouseEvent e) {
    Component c = e.getComponent();
    Container p = SwingUtilities.getUnwrappedParent(c);
    if (p instanceof JViewport) {
      JViewport vport = (JViewport) p;
      Point cp = SwingUtilities.convertPoint(c, e.getPoint(), vport);
      Point vp = vport.getViewPosition();
      vp.translate(startPt.x - cp.x, startPt.y - cp.y);
      delta.setLocation(VELOCITY * (cp.x - startPt.x), VELOCITY * (cp.y - startPt.y));
      ((JComponent) c).scrollRectToVisible(new Rectangle(vp, vport.getSize()));
      startPt.setLocation(cp);
    }
  }

  @Override public void mouseReleased(MouseEvent e) {
    Component c = e.getComponent();
    c.setCursor(dc);
    c.setEnabled(true);
    scroller.start();
  }
}
}}

* 解説 [#explanation]
- [[JTreeの余白をドラッグしてスクロール>Swing/TreeDragScroll]]の`MouseMotionListener`に[[JScrollPaneでキネティックスクロール>Swing/KineticScrolling]]の`Timer`を使った慣性処理を適用して、行のドラッグスクロールを実行
- `JScrollBar`を常に非表示にしているので、マウスホイールによるスクロールは不可
- `JScrollBar`を常に非表示にしているのでマウスホイールによるスクロールは不可
-- [[JScrollBarが非表示でもMouseWheelでScrollする>Swing/MouseWheelScroll]]で回避可能
- 行選択と干渉する場合があるので、ドラッグ中は`JTable#setEnabled(false)`で行選択などを無効化
- 行選択と干渉する場合があるのでドラッグ中は`JTable#setEnabled(false)`で行選択などを無効化
-- 複数行の選択が不可になる

* 参考リンク [#reference]
- [[JTreeの余白をドラッグしてスクロール>Swing/TreeDragScroll]]
- [[JScrollPaneでキネティックスクロール>Swing/KineticScrolling]]
- [[JScrollBarが非表示でもMouseWheelでScrollする>Swing/MouseWheelScroll]]
- [[JTableのドラッグスクロールをタッチスクリーンで実行する>Swing/TableScrollOnTouchScreen]]

* コメント [#comment]
#comment
#comment