Swing/DnDBetweenTrees の変更点
- 追加された行はこの色です。
- 削除された行はこの色です。
- Swing/DnDBetweenTrees へ行く。
- Swing/DnDBetweenTrees の差分を削除
--- 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 --- * Summary [#summary] `JTree`間でのドラッグ&ドロップによるノードの移動を行います。 #download(https://lh3.googleusercontent.com/-orv222AWr0E/VsnVITv64uI/AAAAAAAAOPE/vGmbPDCu4nY/s800-Ic42/DnDBetweenTrees.png) * Source Code Examples [#sourcecode] #code(link){{ class TreeTransferHandler extends TransferHandler { private static final DataFlavor FLAVOR = new DataFlavor( DefaultMutableTreeNode[].class, "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 Transferable() { @Override public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[] {FLAVOR}; } @Override public boolean isDataFlavorSupported(DataFlavor flavor) { return Objects.equals(FLAVOR, flavor); } @Override public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { if (isDataFlavorSupported(flavor)) { return nodes; } else { throw new UnsupportedFlavorException(flavor); } } }; } @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) { 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 == TransferHandler.MOVE) { JTree tree = (JTree) source; DefaultTreeModel model = (DefaultTreeModel) tree.getModel(); for (TreePath path : tree.getSelectionPaths()) { model.removeNodeFromParent((MutableTreeNode) path.getLastPathComponent()); } } } } }} * Explanation [#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)`で非表示にして移動禁止 * Reference [#reference] - [[JTreeのノードをドラッグ&ドロップ>Swing/DnDTree]] - [[JTreeのノードをドラッグ&ドロップで並べ替える>Swing/DnDReorderTree]] * コメント [#comment] * Comment [#comment] #comment #comment