概要

JLayerを使用してJTableの行の高さをマウスでドラッグして変更可能にします。

サンプルコード

class RowHeightResizeLayer extends LayerUI<JScrollPane> {
  private static final int MIN_ROW_HEIGHT = 16;
  private static final Cursor RESIZE_CURSOR = Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR);
  private int mouseYOffset;
  private int resizingRow = -1;
  private Cursor otherCursor = RESIZE_CURSOR;

  @Override public void installUI(JComponent c) {
    super.installUI(c);
    if (c instanceof JLayer) {
      ((JLayer<?>) c).setLayerEventMask(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
    }
  }

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

  @Override protected void processMouseEvent(MouseEvent e, JLayer<? extends JScrollPane> l) {
    Component c = e.getComponent();
    if (c instanceof JTable) {
      JTable table = (JTable) c;
      resizingRow = getResizeTargetRow(table, e.getPoint());
      if (e.getID() == MouseEvent.MOUSE_PRESSED && resizingRow >= 0) {
        mouseYOffset = e.getY() - table.getRowHeight(resizingRow);
        e.consume();
      }
    }
  }

  @Override protected void processMouseMotionEvent(MouseEvent e, JLayer<? extends JScrollPane> l) {
    Component c = e.getComponent();
    if (!(c instanceof JTable)) {
      return;
    }
    JTable table = (JTable) c;
    if (e.getID() == MouseEvent.MOUSE_MOVED) {
      boolean isResizing = RESIZE_CURSOR.equals(table.getCursor());
      int row = getResizeTargetRow(table, e.getPoint());
      if (row >= 0 != isResizing) {
        Cursor tmp = table.getCursor();
        table.setCursor(otherCursor);
        otherCursor = tmp;
      }
    } else if (e.getID() == MouseEvent.MOUSE_DRAGGED && resizingRow >= 0) {
      int newHeight = e.getY() - mouseYOffset;
      if (newHeight > MIN_ROW_HEIGHT) {
        table.setRowHeight(resizingRow, newHeight);
      }
      e.consume();
    }
  }

  private int getResizeTargetRow(JTable table, Point p) {
    int row = table.rowAtPoint(p);
    int col = table.columnAtPoint(p);
    Rectangle r = table.getCellRect(row, col, false);
    r.grow(0, -2);
    if (r.contains(p)) {
      return -1;
    }
    return p.y < r.getCenterY() ? row - 1 : row;
  }
}
View in GitHub: Java, Kotlin

解説

  • JTableの各行の高さはJTable#setRowHeight(row, height)で設定可能
  • JTableに直接MouseMotionListenerMouseListenerを追加して行の高さを変更する方法もある
  • JTableの親JScrollPaneJLayerを設定してJTableへのマウスイベントを取得し、高さ変更のドラッグ中はMouseEvent#consume()を実行してJTableへのマウスイベントを消費し、セルの選択状態が変化しないよう設定
    • JTableHeaderの高さも同様のJLayerでリサイズ可能だが拡大するときにフォーカスの描画が乱れる場合がある
    • Excel風に現在の行の高さを表示したい場合はJToolTipTableColumnのリサイズ中にその幅をJToolTipで表示するのように使用可能
    • 列幅のリサイズと同様にJTable外のJTableHeader領域などにドラッグしてもリサイズを継続するよう修正

参考リンク

コメント