• 追加された行はこの色です。
  • 削除された行はこの色です。
---
category: swing
folder: DnDTable
title: JTableの行をドラッグ&ドロップ
tags: [JTable, DragAndDrop]
author: aterai
pubdate: 2007-04-02T16:29:09+09:00
description: JTableの行を選択し、その順序をドラッグ&ドロップで入れ替えます。
image: https://lh4.googleusercontent.com/_9Z4BYR88imo/TQTLl0NAFoI/AAAAAAAAAX0/su1r-c7f4V0/s800/DnDTable.png
---
* 概要 [#summary]
`JTable`の行を選択し、その順序をドラッグ&ドロップで入れ替えます。

#download(https://lh4.googleusercontent.com/_9Z4BYR88imo/TQTLl0NAFoI/AAAAAAAAAX0/su1r-c7f4V0/s800/DnDTable.png)

* サンプルコード [#sourcecode]
#code(link){{
class DnDTable extends JTable implements DragGestureListener, Transferable {
  private static final String NAME = "test";
  private static final DataFlavor FLAVOR =
    new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType, NAME);
  private static final Color LINE_COLOR = new Color(255, 100, 100);
  private final Rectangle2D targetLine = new Rectangle2D.Float();
  private int draggedIndex = -1;
  private int targetIndex  = -1;

  public DnDTable(TableModel model) {
    super(model);
    //DropTarget dropTarget =
    new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE,
                   new CDropTargetListener(), true);
    //DragSource dragSource = new DragSource();
    new DragSource().createDefaultDragGestureRecognizer(
        (Component) this, DnDConstants.ACTION_COPY_OR_MOVE,
        (DragGestureListener) this);
  }
  @Override protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    if (targetIndex >= 0) {
      Graphics2D g2 = (Graphics2D) g.create();
      g2.setPaint(LINE_COLOR);
      g2.fill(targetLine);
      g2.dispose();
    }
  }

  private void initTargetLine(Point p) {
    Rectangle2D testArea = new Rectangle2D.Float();
    int cellHeight = getRowHeight();
    int lineWidth  = getWidth();
    int lineHeight = 2;
    int modelSize  = getRowCount();
    targetIndex = -1;
    for (int i = 0; i < modelSize; i++) {
      testArea.setBounds(
          0, cellHeight * i - cellHeight / 2, lineWidth, cellHeight);
      if (testArea.contains(p)) {
        targetIndex = i;
        targetLine.setBounds(0, i * cellHeight, lineWidth, lineHeight);
        break;
      }
    }
    if (targetIndex < 0) {
      targetIndex = modelSize;
      targetLine.setBounds(
          0, targetIndex * cellHeight - lineHeight, lineWidth, lineHeight);
    }
  }

  // Interface: DragGestureListener
  @Override public void dragGestureRecognized(DragGestureEvent e) {
    if (getSelectedRowCount() > 1) {
      return;
    }
    draggedIndex = rowAtPoint(e.getDragOrigin());
    if (draggedIndex < 0) {
      return;
    }
    try {
      e.startDrag(DragSource.DefaultMoveDrop, (Transferable) this,
                  new TableDragSourceListener());
    } catch (InvalidDnDOperationException idoe) {
      idoe.printStackTrace();
    }
  }

  // Interface: Transferable
  @Override public Object getTransferData(DataFlavor flavor) {
    return this;
  }

  @Override public DataFlavor[] getTransferDataFlavors() {
    DataFlavor[] f = new DataFlavor[1];
    f[0] = this.FLAVOR;
    return f;
    return new DataFlavor[] {FLAVOR};
  }

  @Override public boolean isDataFlavorSupported(DataFlavor flavor) {
    return flavor.getHumanPresentableName().equals(NAME);
  }

  class CDropTargetListener implements DropTargetListener {
    @Override public void dragExit(DropTargetEvent e) {
      targetIndex = -1;
      repaint();
    }

    @Override public void dragEnter(DropTargetDragEvent e) {
      if (isDragAcceptable(e)) {
        e.acceptDrag(e.getDropAction());
      } else {
        e.rejectDrag();
      }
    }

    @Override public void dragOver(final DropTargetDragEvent e) {
      if (isDragAcceptable(e)) {
        e.acceptDrag(e.getDropAction());
        setCursor(DragSource.DefaultMoveDrop);
      } else {
        e.rejectDrag();
        setCursor(DragSource.DefaultMoveNoDrop);
        return;
      }
      initTargetLine(e.getLocation());
      repaint();
    }

    @Override public void dropActionChanged(DropTargetDragEvent e) {
      // if (isDragAcceptable(e)) { e.acceptDrag(e.getDropAction()); }
      // else e.rejectDrag();
    }

    @Override public void drop(DropTargetDropEvent e) {
      DefaultTableModel model = (DefaultTableModel) getModel();
      if (isDropAcceptable(e)) {
        if (targetIndex == draggedIndex) {
          setRowSelectionInterval(targetIndex, targetIndex);
        } else {
          int tg = targetIndex < draggedIndex ? targetIndex : targetIndex - 1;
          model.moveRow(draggedIndex, draggedIndex, tg);
          setRowSelectionInterval(tg, tg);
        }
        e.dropComplete(true);
      } else {
        e.dropComplete(false);
      }
      e.dropComplete(false);
      setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      setCursor(Cursor.getDefaultCursor());
      targetIndex = -1;
      repaint();
    }
    private boolean isDragAcceptable(DropTargetDragEvent e) {
      DataFlavor[] f = e.getCurrentDataFlavors();
      return isDataFlavorSupported(f[0]);
    }
    private boolean isDropAcceptable(DropTargetDropEvent e) {
      Transferable t = e.getTransferable();
      DataFlavor[] f = t.getTransferDataFlavors();
      return isDataFlavorSupported(f[0]);
    }
  }
}
}}

* 解説 [#explanation]
ドラッグソースとドラッグターゲットをどちらも`JTable`にして、行の入れ替えがドラッグ&ドロップで出来るようになっています。
ドラッグソースとドラッグターゲットをどちらも`JTable`にして、行の入れ替えがドラッグ&ドロップで可能になっています。

このサンプルのドラッグ&ドロップの手順は、行の入れ替え処理などを除けば、[[JListの項目をドラッグ&ドロップ>Swing/DnDList]]とほぼ同一です。
このサンプルのドラッグ&ドロップの手順は、行の入れ替え処理などを除けば[[JListの項目をドラッグ&ドロップ>Swing/DnDList]]と同等です。

* 参考リンク [#reference]
- [[JListの項目をドラッグ&ドロップ>Swing/DnDList]]
- [[JTable自体の高さを拡張>Swing/FillsViewportHeight]]
- [[TransferHandlerを使ってJTableの行をドラッグ&ドロップ、並べ替え>Swing/DnDReorderTable]]
-- `Java 1.6.0`以上で複数行を選択して移動を行う場合のサンプル
- [[JTableの行を別のJTableにドラッグして移動>Swing/DragRowsAnotherTable]]
- [[JTree間でのドラッグ&ドロップによるノードの移動>Swing/DnDBetweenTrees]]

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