JTree間でのドラッグ&ドロップによるノードの移動
Total: 5390
, Today: 2
, Yesterday: 2
Posted by aterai at
Last-modified:
概要
JTree
間でのドラッグ&ドロップによるノードの移動を行います。
Screenshot
Advertisement
サンプルコード
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());
}
}
}
}
View in GitHub: Java, Kotlin解説
上記のサンプルでは、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)
で非表示にして移動禁止
- 同一