【问题标题】:Drag and Drop nodes in JTree在 JTree 中拖放节点
【发布时间】:2011-06-03 01:16:04
【问题描述】:

我很难创建一个允许通过将节点拖放到 JTree 中来重新组织节点的 JTree。这似乎应该相对简单。我看过网上的例子,但我似乎无法在我自己的代码中实现它。

例如,sun 提供的this 允许在不同组件之间拖动到树中,但不能从树本身中拖动。

我还发现这个允许您将文本拖到 JTree 中,但不能拖到树中。

import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javax.swing.tree.*;

public class DndTree {

    public static void main(String args[]) {
        Runnable runner = new Runnable()  {

            public void run() {
                JFrame f = new JFrame("D-n-D JTree");
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                JPanel top = new JPanel(new BorderLayout());
                JLabel dragLabel = new JLabel("Drag me:");
                JTextField text = new JTextField();
                text.setDragEnabled(true);
                top.add(dragLabel, BorderLayout.WEST);
                top.add(text, BorderLayout.CENTER);
                f.add(top, BorderLayout.NORTH);

                final JTree tree = new JTree();
                final DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
                tree.setTransferHandler(new TransferHandler()  {

                    public boolean canImport(TransferHandler.TransferSupport support) {
                        if (!support.isDataFlavorSupported(DataFlavor.stringFlavor)
                                || !support.isDrop()) {
                            return false;
                        }

                        JTree.DropLocation dropLocation =
                                (JTree.DropLocation) support.getDropLocation();

                        return dropLocation.getPath() != null;
                    }

                    public boolean importData(TransferHandler.TransferSupport support) {
                        if (!canImport(support)) {
                            return false;
                        }

                        JTree.DropLocation dropLocation =
                                (JTree.DropLocation) support.getDropLocation();

                        TreePath path = dropLocation.getPath();

                        Transferable transferable = support.getTransferable();

                        String transferData;
                        try {
                            transferData = (String) transferable.getTransferData(
                                    DataFlavor.stringFlavor);
                        } catch (IOException e) {
                            return false;
                        } catch (UnsupportedFlavorException e) {
                            return false;
                        }

                        int childIndex = dropLocation.getChildIndex();
                        if (childIndex == -1) {
                            childIndex = model.getChildCount(path.getLastPathComponent());
                        }

                        DefaultMutableTreeNode newNode =
                                new DefaultMutableTreeNode(transferData);
                        DefaultMutableTreeNode parentNode =
                                (DefaultMutableTreeNode) path.getLastPathComponent();
                        model.insertNodeInto(newNode, parentNode, childIndex);

                        TreePath newPath = path.pathByAddingChild(newNode);
                        tree.makeVisible(newPath);
                        tree.scrollRectToVisible(tree.getPathBounds(newPath));

                        return true;
                    }
                });

                JScrollPane pane = new JScrollPane(tree);
                f.add(pane, BorderLayout.CENTER);

                JPanel bottom = new JPanel();
                JLabel comboLabel = new JLabel("DropMode");
                String options[] = {"USE_SELECTION",
                    "ON", "INSERT", "ON_OR_INSERT"
                };
                final DropMode mode[] = {DropMode.USE_SELECTION,
                    DropMode.ON, DropMode.INSERT, DropMode.ON_OR_INSERT};
                final JComboBox combo = new JComboBox(options);
                combo.addActionListener(new ActionListener()  {

                    public void actionPerformed(ActionEvent e) {
                        int selectedIndex = combo.getSelectedIndex();
                        tree.setDropMode(mode[selectedIndex]);
                    }
                });
                bottom.add(comboLabel);
                bottom.add(combo);
                f.add(bottom, BorderLayout.SOUTH);
                f.setSize(300, 400);
                f.setVisible(true);
            }
        };
        EventQueue.invokeLater(runner);
    }
}

任何参考或建议都会很棒。谢谢

【问题讨论】:

    标签: java swing drag-and-drop jtree


    【解决方案1】:

    以前没有这样做过,但是快速的谷歌搜索在这里发现了同样的问题:http://www.coderanch.com/t/346509/GUI/java/JTree-drag-drop-inside-one 它有一个你可以查看的工作实现。

    这是 Craig Wood 发布的相关代码:

    import java.awt.*;
    import java.awt.datatransfer.*;
    import java.awt.dnd.*;
    import java.util.*;
    import java.util.List;
    import javax.swing.*;
    import javax.swing.tree.*;
    
    public class TreeDragAndDrop {
        private JScrollPane getContent() {
            JTree tree = new JTree();
            tree.setDragEnabled(true);
            tree.setDropMode(DropMode.ON_OR_INSERT);
            tree.setTransferHandler(new TreeTransferHandler());
            tree.getSelectionModel().setSelectionMode(
                    TreeSelectionModel.CONTIGUOUS_TREE_SELECTION);
            expandTree(tree);
            return new JScrollPane(tree);
        }
    
        private void expandTree(JTree tree) {
            DefaultMutableTreeNode root =
                (DefaultMutableTreeNode)tree.getModel().getRoot();
            Enumeration e = root.breadthFirstEnumeration();
            while(e.hasMoreElements()) {
                DefaultMutableTreeNode node =
                    (DefaultMutableTreeNode)e.nextElement();
                if(node.isLeaf()) continue;
                int row = tree.getRowForPath(new TreePath(node.getPath()));
                tree.expandRow(row);
            }
        }
    
        public static void main(String[] args) {
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.add(new TreeDragAndDrop().getContent());
            f.setSize(400,400);
            f.setLocation(200,200);
            f.setVisible(true);
        }
    }
    
    class TreeTransferHandler extends TransferHandler {
        DataFlavor nodesFlavor;
        DataFlavor[] flavors = new DataFlavor[1];
        DefaultMutableTreeNode[] nodesToRemove;
    
        public TreeTransferHandler() {
            try {
                String mimeType = DataFlavor.javaJVMLocalObjectMimeType +
                                  ";class=\"" +
                    javax.swing.tree.DefaultMutableTreeNode[].class.getName() +
                                  "\"";
                nodesFlavor = new DataFlavor(mimeType);
                flavors[0] = nodesFlavor;
            } catch(ClassNotFoundException e) {
                System.out.println("ClassNotFound: " + e.getMessage());
            }
        }
    
        public boolean canImport(TransferHandler.TransferSupport support) {
            if(!support.isDrop()) {
                return false;
            }
            support.setShowDropLocation(true);
            if(!support.isDataFlavorSupported(nodesFlavor)) {
                return false;
            }
            // Do not allow a drop on the drag source selections.
            JTree.DropLocation dl =
                    (JTree.DropLocation)support.getDropLocation();
            JTree tree = (JTree)support.getComponent();
            int dropRow = tree.getRowForPath(dl.getPath());
            int[] selRows = tree.getSelectionRows();
            for(int i = 0; i < selRows.length; i++) {
                if(selRows[i] == dropRow) {
                    return false;
                }
            }
            // Do not allow MOVE-action drops if a non-leaf node is
            // selected unless all of its children are also selected.
            int action = support.getDropAction();
            if(action == MOVE) {
                return haveCompleteNode(tree);
            }
            // Do not allow a non-leaf node to be copied to a level
            // which is less than its source level.
            TreePath dest = dl.getPath();
            DefaultMutableTreeNode target =
                (DefaultMutableTreeNode)dest.getLastPathComponent();
            TreePath path = tree.getPathForRow(selRows[0]);
            DefaultMutableTreeNode firstNode =
                (DefaultMutableTreeNode)path.getLastPathComponent();
            if(firstNode.getChildCount() > 0 &&
                   target.getLevel() < firstNode.getLevel()) {
                return false;
            }
            return true;
        }
    
        private boolean haveCompleteNode(JTree tree) {
            int[] selRows = tree.getSelectionRows();
            TreePath path = tree.getPathForRow(selRows[0]);
            DefaultMutableTreeNode first =
                (DefaultMutableTreeNode)path.getLastPathComponent();
            int childCount = first.getChildCount();
            // first has children and no children are selected.
            if(childCount > 0 && selRows.length == 1)
                return false;
            // first may have children.
            for(int i = 1; i < selRows.length; i++) {
                path = tree.getPathForRow(selRows[i]);
                DefaultMutableTreeNode next =
                    (DefaultMutableTreeNode)path.getLastPathComponent();
                if(first.isNodeChild(next)) {
                    // Found a child of first.
                    if(childCount > selRows.length-1) {
                        // Not all children of first are selected.
                        return false;
                    }
                }
            }
            return true;
        }
    
        protected Transferable createTransferable(JComponent c) {
            JTree tree = (JTree)c;
            TreePath[] paths = tree.getSelectionPaths();
            if(paths != null) {
                // Make up a node array of copies for transfer and
                // another for/of the nodes that will be removed in
                // exportDone after a successful drop.
                List<DefaultMutableTreeNode> copies =
                    new ArrayList<DefaultMutableTreeNode>();
                List<DefaultMutableTreeNode> toRemove =
                    new ArrayList<DefaultMutableTreeNode>();
                DefaultMutableTreeNode node =
                    (DefaultMutableTreeNode)paths[0].getLastPathComponent();
                DefaultMutableTreeNode copy = copy(node);
                copies.add(copy);
                toRemove.add(node);
                for(int i = 1; i < paths.length; i++) {
                    DefaultMutableTreeNode next =
                        (DefaultMutableTreeNode)paths[i].getLastPathComponent();
                    // Do not allow higher level nodes to be added to list.
                    if(next.getLevel() < node.getLevel()) {
                        break;
                    } else if(next.getLevel() > node.getLevel()) {  // child node
                        copy.add(copy(next));
                        // node already contains child
                    } else {                                        // sibling
                        copies.add(copy(next));
                        toRemove.add(next);
                    }
                }
                DefaultMutableTreeNode[] nodes =
                    copies.toArray(new DefaultMutableTreeNode[copies.size()]);
                nodesToRemove =
                    toRemove.toArray(new DefaultMutableTreeNode[toRemove.size()]);
                return new NodesTransferable(nodes);
            }
            return null;
        }
    
        /** Defensive copy used in createTransferable. */
        private DefaultMutableTreeNode copy(TreeNode node) {
            return new DefaultMutableTreeNode(node);
        }
    
        protected void exportDone(JComponent source, Transferable data, int action) {
            if((action & MOVE) == MOVE) {
                JTree tree = (JTree)source;
                DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
                // Remove nodes saved in nodesToRemove in createTransferable.
                for(int i = 0; i < nodesToRemove.length; i++) {
                    model.removeNodeFromParent(nodesToRemove[i]);
                }
            }
        }
    
        public int getSourceActions(JComponent c) {
            return COPY_OR_MOVE;
        }
    
        public boolean importData(TransferHandler.TransferSupport support) {
            if(!canImport(support)) {
                return false;
            }
            // Extract transfer data.
            DefaultMutableTreeNode[] nodes = null;
            try {
                Transferable t = support.getTransferable();
                nodes = (DefaultMutableTreeNode[])t.getTransferData(nodesFlavor);
            } catch(UnsupportedFlavorException ufe) {
                System.out.println("UnsupportedFlavor: " + ufe.getMessage());
            } catch(java.io.IOException ioe) {
                System.out.println("I/O error: " + ioe.getMessage());
            }
            // Get drop location info.
            JTree.DropLocation dl =
                    (JTree.DropLocation)support.getDropLocation();
            int childIndex = dl.getChildIndex();
            TreePath dest = dl.getPath();
            DefaultMutableTreeNode parent =
                (DefaultMutableTreeNode)dest.getLastPathComponent();
            JTree tree = (JTree)support.getComponent();
            DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
            // Configure for drop mode.
            int index = childIndex;    // DropMode.INSERT
            if(childIndex == -1) {     // DropMode.ON
                index = parent.getChildCount();
            }
            // Add data to model.
            for(int i = 0; i < nodes.length; i++) {
                model.insertNodeInto(nodes[i], parent, index++);
            }
            return true;
        }
    
        public String toString() {
            return getClass().getName();
        }
    
        public class NodesTransferable implements Transferable {
            DefaultMutableTreeNode[] nodes;
    
            public NodesTransferable(DefaultMutableTreeNode[] nodes) {
                this.nodes = nodes;
             }
    
            public Object getTransferData(DataFlavor flavor)
                                     throws UnsupportedFlavorException {
                if(!isDataFlavorSupported(flavor))
                    throw new UnsupportedFlavorException(flavor);
                return nodes;
            }
    
            public DataFlavor[] getTransferDataFlavors() {
                return flavors;
            }
    
            public boolean isDataFlavorSupported(DataFlavor flavor) {
                return nodesFlavor.equals(flavor);
            }
        }
    }
    

    【讨论】:

    • 在线程中进一步有人指出了一个错误(在您发布答案之后)。 copy(TreeNode) 方法应该是 private DefaultMutableTreeNode copy(TreeNode node) {return node.clone();}
    • 有点问题。我们不能在叶子的最后一个节点之后移动一个节点。
    • 后面的帖子指出Paul贴的方法没有编译通过,应该是:private DefaultMutableTreeNode copy(TreeNode node) { DefaultMutableTreeNode n=(DefaultMutableTreeNode)node; return (DefaultMutableTreeNode) n.clone(); }
    • 我不同意应该调用 node.clone。它已经通过将 Cloneable 接口传递给构造函数来使用它。也许他们使用的是较旧的 Java 版本?
    【解决方案2】:

    @jzd 提到的 Craig Wood 的原始代码包含一个错误,其功能可以改进。我为它创建了一个 repo 并解释了所有内容:https://gitlab.com/alberthendriks/jtree-drag-drop。我改进的代码是:

    /**
     * Edited from https://coderanch.com/t/346509/java/JTree-drag-drop-tree-Java
     * by Craig Wood and mentioned on
     * https://stackoverflow.com/questions/4588109/drag-and-drop-nodes-in-jtree
     */
    
    import java.awt.datatransfer.*;
    import java.util.*;
    import java.util.List;
    import javax.swing.*;
    import javax.swing.tree.*;
    
    public class TreeDragAndDrop {
        private JScrollPane getContent() {
            JTree tree = new JTree();
            tree.setDragEnabled(true);
            tree.setDropMode(DropMode.ON_OR_INSERT);
            tree.setTransferHandler(new TreeTransferHandler());
            tree.getSelectionModel().setSelectionMode(
                    TreeSelectionModel.CONTIGUOUS_TREE_SELECTION);
            expandTree(tree);
            return new JScrollPane(tree);
        }
    
        private void expandTree(JTree tree) {
            DefaultMutableTreeNode root =
                    (DefaultMutableTreeNode)tree.getModel().getRoot();
            Enumeration e = root.breadthFirstEnumeration();
            while(e.hasMoreElements()) {
                DefaultMutableTreeNode node =
                        (DefaultMutableTreeNode)e.nextElement();
                if(node.isLeaf()) continue;
                int row = tree.getRowForPath(new TreePath(node.getPath()));
                tree.expandRow(row);
            }
        }
    
        public static void main(String[] args) {
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.add(new TreeDragAndDrop().getContent());
            f.setSize(400,400);
            f.setLocation(200,200);
            f.setVisible(true);
        }
    }
    
    class TreeTransferHandler extends TransferHandler {
        DataFlavor nodesFlavor;
        DataFlavor[] flavors = new DataFlavor[1];
        DefaultMutableTreeNode[] nodesToRemove;
    
        public TreeTransferHandler() {
            try {
                String mimeType = DataFlavor.javaJVMLocalObjectMimeType +
                        ";class=\"" +
                        javax.swing.tree.DefaultMutableTreeNode[].class.getName() +
                        "\"";
                nodesFlavor = new DataFlavor(mimeType);
                flavors[0] = nodesFlavor;
            } catch(ClassNotFoundException e) {
                System.out.println("ClassNotFound: " + e.getMessage());
            }
        }
    
        public boolean canImport(TransferHandler.TransferSupport support) {
            if(!support.isDrop()) {
                return false;
            }
            support.setShowDropLocation(true);
            if(!support.isDataFlavorSupported(nodesFlavor)) {
                return false;
            }
            // Do not allow a drop on the drag source selections.
            JTree.DropLocation dl =
                    (JTree.DropLocation)support.getDropLocation();
            JTree tree = (JTree)support.getComponent();
            int dropRow = tree.getRowForPath(dl.getPath());
            int[] selRows = tree.getSelectionRows();
            for(int i = 0; i < selRows.length; i++) {
                if(selRows[i] == dropRow) {
                    return false;
                }
                DefaultMutableTreeNode treeNode =
                        (DefaultMutableTreeNode)tree.getPathForRow(selRows[i]).getLastPathComponent();
                for (TreeNode offspring: Collections.list(treeNode.depthFirstEnumeration())) {
                    if (tree.getRowForPath(new TreePath(((DefaultMutableTreeNode)offspring).getPath())) == dropRow) {
                        return false;
                    }
                }
            }
            return true;
        }
    
        protected Transferable createTransferable(JComponent c) {
            JTree tree = (JTree) c;
            TreePath[] paths = tree.getSelectionPaths();
            if (paths == null) {
                return null;
            }
            // Make up a node array of copies for transfer and
            // another for/of the nodes that will be removed in
            // exportDone after a successful drop.
            List<DefaultMutableTreeNode> copies =
                    new ArrayList<DefaultMutableTreeNode>();
            List<DefaultMutableTreeNode> toRemove =
                    new ArrayList<DefaultMutableTreeNode>();
            DefaultMutableTreeNode firstNode =
                    (DefaultMutableTreeNode) paths[0].getLastPathComponent();
            HashSet<TreeNode> doneItems = new LinkedHashSet<>(paths.length);
            DefaultMutableTreeNode copy = copy(firstNode, doneItems, tree);
            copies.add(copy);
            toRemove.add(firstNode);
            for (int i = 1; i < paths.length; i++) {
                DefaultMutableTreeNode next =
                        (DefaultMutableTreeNode) paths[i].getLastPathComponent();
                if (doneItems.contains(next)) {
                    continue;
                }
                // Do not allow higher level nodes to be added to list.
                if (next.getLevel() < firstNode.getLevel()) {
                    break;
                } else if (next.getLevel() > firstNode.getLevel()) {  // child node
                    copy.add(copy(next, doneItems, tree));
                    // node already contains child
                } else {                                        // sibling
                    copies.add(copy(next, doneItems, tree));
                    toRemove.add(next);
                }
                doneItems.add(next);
            }
            DefaultMutableTreeNode[] nodes =
                    copies.toArray(new DefaultMutableTreeNode[copies.size()]);
            nodesToRemove =
                    toRemove.toArray(new DefaultMutableTreeNode[toRemove.size()]);
            return new NodesTransferable(nodes);
        }
    
        private DefaultMutableTreeNode copy(DefaultMutableTreeNode node, HashSet<TreeNode> doneItems, JTree tree) {
            DefaultMutableTreeNode copy = new DefaultMutableTreeNode(node);
            doneItems.add(node);
            for (int i=0; i<node.getChildCount(); i++) {
                copy.add(copy((DefaultMutableTreeNode)((TreeNode)node).getChildAt(i), doneItems, tree));
            }
            int row = tree.getRowForPath(new TreePath(copy.getPath()));
            tree.expandRow(row);
            return copy;
        }
    
        protected void exportDone(JComponent source, Transferable data, int action) {
            if((action & MOVE) == MOVE) {
                JTree tree = (JTree)source;
                DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
                // Remove nodes saved in nodesToRemove in createTransferable.
                for(int i = 0; i < nodesToRemove.length; i++) {
                    model.removeNodeFromParent(nodesToRemove[i]);
                }
            }
        }
    
        public int getSourceActions(JComponent c) {
            return COPY_OR_MOVE;
        }
    
        public boolean importData(TransferHandler.TransferSupport support) {
            if(!canImport(support)) {
                return false;
            }
            // Extract transfer data.
            DefaultMutableTreeNode[] nodes = null;
            try {
                Transferable t = support.getTransferable();
                nodes = (DefaultMutableTreeNode[])t.getTransferData(nodesFlavor);
            } catch(UnsupportedFlavorException ufe) {
                System.out.println("UnsupportedFlavor: " + ufe.getMessage());
            } catch(java.io.IOException ioe) {
                System.out.println("I/O error: " + ioe.getMessage());
            }
            // Get drop location info.
            JTree.DropLocation dl =
                    (JTree.DropLocation)support.getDropLocation();
            int childIndex = dl.getChildIndex();
            TreePath dest = dl.getPath();
            DefaultMutableTreeNode parent =
                    (DefaultMutableTreeNode)dest.getLastPathComponent();
            JTree tree = (JTree)support.getComponent();
            DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
            // Configure for drop mode.
            int index = childIndex;    // DropMode.INSERT
            if(childIndex == -1) {     // DropMode.ON
                index = parent.getChildCount();
            }
            // Add data to model.
            for(int i = 0; i < nodes.length; i++) {
                model.insertNodeInto(nodes[i], parent, index++);
            }
            return true;
        }
    
        public String toString() {
            return getClass().getName();
        }
    
        public class NodesTransferable implements Transferable {
            DefaultMutableTreeNode[] nodes;
    
            public NodesTransferable(DefaultMutableTreeNode[] nodes) {
                this.nodes = nodes;
            }
    
            public Object getTransferData(DataFlavor flavor)
                    throws UnsupportedFlavorException {
                if(!isDataFlavorSupported(flavor))
                    throw new UnsupportedFlavorException(flavor);
                return nodes;
            }
    
            public DataFlavor[] getTransferDataFlavors() {
                return flavors;
            }
    
            public boolean isDataFlavorSupported(DataFlavor flavor) {
                return nodesFlavor.equals(flavor);
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-05
      • 1970-01-01
      • 2012-09-01
      • 1970-01-01
      相关资源
      最近更新 更多