---
category: swing
folder: TableScrollOnTouchScreen
title: JTableのドラッグスクロールをタッチスクリーンで実行する
tags: [JTable, JScrollPane, MouseMotionListener, Timer, ListSelectionListener]
author: aterai
pubdate: 2019-07-15T19:32:33.964+09:00
description: JTableのドラッグスクロールをタッチスクリーンで実行可能にするためのリスナーを作成します。
image: https://drive.google.com/uc?id=1pV73QkefF37JYOx942N2Vr8Yq7FsoJHb
---
* 概要 [#summary]
`JTable`のドラッグスクロールをタッチスクリーンで実行可能にするためのリスナーを作成します。

#download(https://drive.google.com/uc?id=1pV73QkefF37JYOx942N2Vr8Yq7FsoJHb)

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

  protected TableTouchScreenHandler(JTable table) {
    super();
    this.scroller = new Timer(DELAY, e -> {
      System.out.print(".");
      JViewport vport = (JViewport) SwingUtilities.getUnwrappedParent(table);
      Point vp = vport.getViewPosition();
      vp.translate(-delta.x, -delta.y);
      table.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) {
    System.out.println("mousePressed: " + delta);
    Component c = e.getComponent();
    c.setCursor(hc);
    // c.setEnabled(false);
    Container p = SwingUtilities.getUnwrappedParent(c);
    if (p instanceof JViewport) {
      startPt.setLocation(SwingUtilities.convertPoint(c, e.getPoint(), p));
      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) {
    System.out.println("mouseReleased: " + delta);
    JTable c = (JTable) e.getComponent();
    c.setCursor(dc);
    // c.setEnabled(true);
    if (c.isEditing()) {
      delta.setLocation(0, 0);
    } else {
      scroller.start();
    }
  }

  @Override public void valueChanged(ListSelectionEvent e) {
    System.out.println("\nvalueChanged: " + e.getValueIsAdjusting());
    if (scroller.isRunning()) {
      System.out.println("isRunning");
      delta.setLocation(0, 0);
    }
    scroller.stop();
  }
}
}}

* 解説 [#explanation]
上記のサンプルでは、ドラッグによるスクロールは[[JTableをスクロールバー無しのドラッグでスクロールする>Swing/HandDragScrollableTable]]と同じマウスリスナーを使用していますが、タッチスクリーン上で実行するために以下の変更を追加しています。

- タッチした時点ではそのタッチイベントが取得できない?ため、`ListSelectionListener`を追加して行が選択されたらスクロールを中断する
- タッチがドラッグではなくセル編集開始の場合はスクロール開始を実行しない
-- 編集終了のタッチの場合でもスクロールが開始されないようにするため、編集開始時点で移動量を`0`に設定する必要がある
-- この設定をしない場合、例えば`0`列`0`行目のセルを編集状態にしたあと`2`列`10`行目のチェックボックスをタッチすると自動スクロールが開始されてしまう
- 以上、`Surface Pro 3`で確認

* 参考リンク [#reference]
- [[JTableをスクロールバー無しのドラッグでスクロールする>Swing/HandDragScrollableTable]]
-- こちらのサンプルではタッチスクリーンを指で操作した場合、セル編集やスクロールの停止に不具合がある

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