简体   繁体   English

如何获取选定的行,包括 JTree 的子行

[英]How to get selected rows including child rows of a JTree

I'm working on implementing Drag & Drop functionality on a JTree component by using a TransferHandler .我正在使用TransferHandler在 JTree 组件上实现拖放功能。 I've used this old thread on CodeRanch, which is pretty much the only apparent ressource that gets linked to whenever any question regarding this comes up in any forum.我在 CodeRanch 上使用了 这个旧线程,它几乎是唯一一个明显的资源,只要任何论坛上出现任何关于此的问题,就会被链接到。

I've gotten it to work well with my Data and my UI, so that leaf-Nodes can easily be dragged and dropped.我已经让它与我的数据和 UI 配合得很好,因此可以轻松拖放叶节点。 The problem comes, when I try to drag Nodes that have children.问题来了,当我尝试拖动有孩子的节点时。 The overriden boolean function canImport(TransferSupport support) returns false, because it checks the selected rows on the tree.覆盖布尔函数canImport(TransferSupport support)返回 false,因为它检查树上的选定行。 Therefore, I then tested wether I could CTRL + Click to manually select child nodes after selecting the "root"-node of my drag operation, which works just fine and the node, including all it's children gets drag and dropped.因此,我然后测试了我是否可以在选择我的拖动操作的“根”节点后CTRL + Click手动选择子节点,这工作得很好,并且节点,包括它的所有子节点都被拖放。

My question is this: to get the selected rows, I call tree.getSelectionRows() and save that value to an integer array.我的问题是:为了获取选定的行,我调用tree.getSelectionRows()并将该值保存到一个整数数组中。 How do I get the child-rows into that integer array?如何将子行放入该整数数组中? I'm aware I could probably rewrite other parts of my TransferHandler , but since everything else works just fine, if only it would actually select the rows how I anticipate it to, I feel like this is the simpler approach.我知道我可能会重写TransferHandler其他部分,但是由于其他一切都正常工作,如果它实际上会按照我预期的方式选择行,我觉得这是更简单的方法。

So, drag and drop is ... complicated.所以,拖放是......很复杂。 There are lots of gotchas you need to be aware of.您需要注意很多问题。

In the linked example you should beware that it's dealing with DefaultMutableTreeNode s and not TreeNode s, might not be an issue, but you might want to keep it in mind.在链接的示例中,您应该注意它正在处理DefaultMutableTreeNode s 而不是TreeNode s,这可能不是问题,但您可能要记住它。

The haveCompleteNode seems to be stopping you from moving a branch node if its child elements aren't selected ... which seems somewhat weird to me.如果未选择其子元素,那么haveCompleteNode似乎会阻止您移动分支节点……这对我来说似乎有些奇怪。

Also, the code following it seems to prevent you from moving/copying a node to a position above the source nodes current level ... for some reason此外,它后面的代码似乎阻止您将节点移动/复制到源节点当前级别以上的位置......出于某种原因

I'm also not sure I'd keep two copies of the selected nodes, this is me, but a defensive copy seems a little over the top (could be a particular edge case, but I'd be worried if the tree can be updated during a drag operation, but that's me).我也不确定我会保留所选节点的两个副本,这是我,但防御性副本似乎有点过头(可能是一个特殊的边缘情况,但我会担心树是否可以在拖动操作期间更新,但那是我)。

So, what are the lessons here?那么,这里的教训是什么?

Any code you find on the web is going to need you to spend some time actually figuring out what it's doing.你在网上找到的任何代码都需要你花一些时间来真正弄清楚它在做什么。 It might start out as a good fit for your needs, but at some point in time, you're going to have to pull up your sleeves and dig into to modify it for your own needs.它可能刚开始非常适合您的需求,但在某个时间点,您将不得不卷起袖子深入研究以根据自己的需求对其进行修改。

The first thing you should do is get a good understanding of the Drag and Drop and Data Transfer APIs.您应该做的第一件事是充分了解 拖放和数据传输API。

The following is a modified example of the code you linked, which will allow you to move a branch node (and its children) and also allow you to move them to a position "above" their source depth.以下是您链接的代码的修改示例,它允许您移动分支节点(及其子节点)并允许您将它们移动到“高于”其源深度的位置。

But again - you need to take the time to understand what the code is doing and be prepared to modify it to your needs但同样 - 您需要花时间了解代码在做什么,并准备好根据您的需要修改它

import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import javax.swing.DropMode;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.TransferHandler;
import static javax.swing.TransferHandler.COPY_OR_MOVE;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

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];

        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)) {
                System.out.println("Unsupported flavor");
                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;
                }
            }
            // This seems to stop the node from been copied to a level above itself?!
//            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;
        }

        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>();

                for (int i = 0; i < paths.length; i++) {
                    DefaultMutableTreeNode next = (DefaultMutableTreeNode) paths[i].getLastPathComponent();
                    System.out.println("Selected = " + next.getUserObject());
                    copies.add(next);
                }
                DefaultMutableTreeNode[] nodes = copies.toArray(new DefaultMutableTreeNode[copies.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) {
            // Already dealt with this
        }

        public int getSourceActions(JComponent c) {
            return COPY_OR_MOVE;
        }

        public boolean importData(TransferHandler.TransferSupport support) {
            System.out.println("Import here");
            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();
            }

            // One consideration to consider is what to do if it's a different
            // source model ... and it's only a "copy" operation
            // Might want to look into that
            for (DefaultMutableTreeNode node : nodes) {
                model.removeNodeFromParent(node);
            }

            // Add data to model.
            for (DefaultMutableTreeNode node : nodes) {
                model.insertNodeInto(node, 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);
            }
        }
    }
}

As far as "code I found on the web", this isn't too bad.至于“我在网上找到的代码”,这还不错。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM