• 追加された行はこの色です。
  • 削除された行はこの色です。
---
category: swing
folder: DnDBetweenTrees
title: JTree間でのドラッグ&ドロップによるノードの移動
tags: [JTree, DragAndDrop, TransferHandler]
author: aterai
pubdate: 2016-02-22T00:58:53+09:00
description: JTree間でのドラッグ&ドロップによるノードの移動を行います。
image: https://lh3.googleusercontent.com/-orv222AWr0E/VsnVITv64uI/AAAAAAAAOPE/vGmbPDCu4nY/s800-Ic42/DnDBetweenTrees.png
---
* 概要 [#u3c3752d]
* 概要 [#summary]
`JTree`間でのドラッグ&ドロップによるノードの移動を行います。

#download(https://lh3.googleusercontent.com/-orv222AWr0E/VsnVITv64uI/AAAAAAAAOPE/vGmbPDCu4nY/s800-Ic42/DnDBetweenTrees.png)

* サンプルコード [#jcf87e46]
* サンプルコード [#sourcecode]
#code(link){{
class TreeTransferHandler extends TransferHandler {
  private static final DataFlavor FLAVOR = new ActivationDataFlavor(
      DefaultMutableTreeNode[].class,
      DataFlavor.javaJVMLocalObjectMimeType,
      "Array of DefaultMutableTreeNode");
  private JTree source;

  @Override protected Transferable createTransferable(JComponent c) {
    source = (JTree) c;
    TreePath[] paths = source.getSelectionPaths();
    DefaultMutableTreeNode[] nodes = new DefaultMutableTreeNode[paths.length];
    for (int i = 0; i < paths.length; i++) {
      nodes[i] = (DefaultMutableTreeNode) paths[i].getLastPathComponent();
    }
    return new DataHandler(nodes, FLAVOR.getMimeType());
  }

  @Override public int getSourceActions(JComponent c) {
    return MOVE;
  }

  @Override public boolean canImport(TransferHandler.TransferSupport support) {
    if (!support.isDrop()) {
      return false;
    }
    if (!support.isDataFlavorSupported(FLAVOR)) {
      return false;
    }
    JTree tree = (JTree) support.getComponent();
    return !tree.equals(source);
  }

  @Override public boolean importData(TransferHandler.TransferSupport support) {
    if (!canImport(support)) {
      return false;
    }
    DefaultMutableTreeNode[] nodes = null;
    try {
      Transferable t = support.getTransferable();
      nodes = (DefaultMutableTreeNode[]) t.getTransferData(FLAVOR);
    } catch (UnsupportedFlavorException | IOException ex) {
      ex.printStackTrace();
    }
    TransferHandler.DropLocation tdl = support.getDropLocation();
    if (tdl instanceof JTree.DropLocation) {
      JTree.DropLocation dl = (JTree.DropLocation) tdl;
      int childIndex = dl.getChildIndex();
      TreePath dest = dl.getPath();
      DefaultMutableTreeNode parent =
        (DefaultMutableTreeNode) dest.getLastPathComponent();
      JTree tree = (JTree) support.getComponent();
      DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
      int idx = childIndex < 0 ? parent.getChildCount() : childIndex;
      //DefaultTreeModel sm = (DefaultTreeModel) source.getModel();
      for (DefaultMutableTreeNode node : nodes) {
        //sm.removeNodeFromParent(node);
        //model.insertNodeInto(node, parent, idx++);
        DefaultMutableTreeNode clone = new DefaultMutableTreeNode(node.getUserObject());
        model.insertNodeInto(deepCopyTreeNode(node, clone), parent, idx++);
      }
      return true;
    }
    return false;
  }
  private static DefaultMutableTreeNode deepCopyTreeNode(
      DefaultMutableTreeNode src, DefaultMutableTreeNode tgt) {
    for (int i = 0; i < src.getChildCount(); i++) {
      DefaultMutableTreeNode node  = (DefaultMutableTreeNode) src.getChildAt(i);
      DefaultMutableTreeNode clone = new DefaultMutableTreeNode(node.getUserObject());
      tgt.add(clone);
      if (!node.isLeaf()) {
        deepCopyTreeNode(node, clone);
      }
    }
    return tgt;
  }
  @Override protected void exportDone(
      JComponent source, Transferable data, int action) {
    if (action == MOVE) {
      JTree tree = (JTree) source;
      DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
      for (TreePath path : tree.getSelectionPaths()) {
        model.removeNodeFromParent((MutableTreeNode) path.getLastPathComponent());
      }
    }
  }
}
}}

* 解説 [#tcbf3f3a]
上記のサンプルでは、`JTree`間でのノード移動を可能にする`TransferHanlder`を作成して、`JTree#setTransferHandler(...)`で設定しています。
* 解説 [#explanation]
上記のサンプルでは、`JTree`間でのノード移動を可能にする`TransferHandler`を作成して、`JTree#setTransferHandler(...)`で設定しています。

`TransferHandler#importData(...)`メソッド内で、ドラッグして移動する`DefaultMutableTreeNode`をドラッグ元の`DefaultTreeModel`から削除せずに、`DefaultTreeModel#insertNodeInto(...)`メソッドでドロップ先の`JTree`の`DefaultTreeModel`に挿入すると、ノードの親子関係がおかしくなって、ドラッグ元の`JTree`から`DefaultTreeModel#removeNodeFromParent(...)`メソッドでノードを削除できなくなります(`DefaultTreeModel.reload()`ですべて再評価すると正常に表示されるが、ノードがすべて折り畳まれてしまう)。これを避けるために、移動する`DefaultMutableTreeNode`のクローン(親ノードは未設定)を作成してドロップ元に追加し、ドラッグ元からの削除は`TransferHandler#exportDone(...)`で実行するようにしています。

- 制限:
-- 同一`JTree`内でのノード入れ替えには未対応
-- `TreeSelectionModel.SINGLE_TREE_SELECTION`で選択、移動できるのは`1`ノードのみに制限
-- `TransferHandler.MOVE`で移動のみに対応
-- ルートノードは、`JTree#setRootVisible(false)`で非表示にして移動禁止

//* 参考リンク
* コメント [#i9ecc5ec]
//* 参考リンク [#reference]
* コメント [#comment]
#comment
#comment