以不同的嵌套级别删除JTree中的节点

时间:2022-10-27 12:37:10

I am implementing drag and drop in a JTree. I want the user to be able to drop a node at different levels in the tree.

我正在JTree中实现拖放。我希望用户能够在树中的不同级别删除节点。

In the example below, imagine that the user inserts an item between "grandchild A2" and "child C":

在下面的示例中,假设用户在“孙子A2”和“子C”之间插入一个项目:

root
  child A
    grandchild A1
    grandchild A2
  child C
    grandchild C1

Now there are two options:

现在有两种选择:

  1. Append a new grandchild to "child A", which will be "grandchild A3", or
  2. 将新孙子添加到“孩子A”,这将是“孙子A3”,或

  3. Insert a new "child B" between "child A" and "child B".
  4. 在“孩子A”和“孩子B”之间插入一个新的“孩子B”。

In SWT this is possible by moving the node a bit in the vertical direction. A horizontal line indicator will show at which nesting level the tree node is inserted.

在SWT中,这可以通过在垂直方向上移动节点来实现。水平线指示符将显示树节点插入的嵌套级别。

Is this possible at all in Swing? I cannot seem to find information on this. The line indicator in Swing is always shown at one level only.

这在Swing中是否可行?我似乎无法找到相关信息。 Swing中的线指示器始终仅显示在一个级别。

And if not, is there a workaround?

如果没有,是否有解决方法?

2 个解决方案

#1


2  

I don't believe it's possible to accomplish exactly the behavior you want using Swing's built in drag and drop.

我不相信使用Swing内置的拖放功能可以完全实现您想要的行为。

A potential workaround is to set your drop mode to ON_OR_INSERT as follows: tree.setDropMode(DropMode.ON_OR_INSERT);

可能的解决方法是将drop模式设置为ON_OR_INSERT,如下所示:tree.setDropMode(DropMode.ON_OR_INSERT);

ON_OR_INSERT supports dropping either ON a node directly or between nodes. The INSERT part supports dropping between "A" and "B". You can then allow users to add a new grandchild of "A" after "A3" in one of two ways(or both):

ON_OR_INSERT支持直接在节点上或节点之间删除。 INSERT部分支持“A”和“B”之间的下降。然后,您可以允许用户以两种方式之一(或两者)在“A3”之后添加“A”的新孙子:

  • Interpret a drop directly on "A" as adding the item as the last child of "A"
  • 直接在“A”上解释一下,将该项添加为“A”的最后一个子项

  • Interpret a drop on a node as adding an element after the node(this is problematic if the node is not a leaf as the expected behavior is to add the element as a child)
  • 将节点上的删除解释为在节点后添加元素(如果节点不是叶子则会出现问题,因为预期的行为是将元素添加为子节点)

If you need exactly the behavior you describe, you'd probably need to write a custom DropTarget for the table and draw the effects you want(line showing where the drop will happen) yourself. I'd recommend avoiding this if at all possible.

如果您确切需要您描述的行为,您可能需要为表格编写自定义DropTarget并自己绘制所需的效果(显示放置位置的线条)。我建议尽可能避免这种情况。

#2


1  

This is a bug in the java drop location handling and is difficult to resolve without modifying the JDK source. It is still unresolved, but there is also a related enhancement request with possible impractical workarounds that was closed as won't fix.

这是java drop location处理中的一个错误,如果不修改JDK源,很难解决。它仍然没有得到解决,但也有一个相关的增强请求,可能的不切实际的解决方案已关闭,因为无法解决。

The only way I've been able to do this is by adding a dummy node as the last child of any container node. This is complicated and adds a row that may not be desirable, but allows the user to drop as the last child of a container node.

我能够做到这一点的唯一方法是添加一个虚节点作为任何容器节点的最后一个子节点。这很复杂并且添加了可能不合适的行,但允许用户作为容器节点的最后一个子节点。

public class TreeDragAndDrop {
    private static final int CONTAINER_ROW = 0;
    private static final int PLACEHOLDER_ROW = 3;

    private JScrollPane getContent() {
        JTree tree = new JTree(getTreeModel()) {
            // Overridden to prevent placeholder selection via the keyboad
            @Override
            public void setSelectionInterval(int index0, int index1) {
                int index = index0;
                // Probably would use a better check for placeholder row in production
                // and use a while loop in case the new index is also a placeholder
                if (index == PLACEHOLDER_ROW) {
                    int currentSelection = getSelectionCount() > 0 ? getSelectionRows()[0] : -1;
                    if (currentSelection < index) {
                        index++;
                    } else {
                        index--;
                    }
                }
                super.setSelectionInterval(index, index);
            }

            // Overridden to prevent placeholder selection via the mouse
            @Override
            public void setSelectionPath(TreePath path) {
                if (path != null && getRowForPath(path) != PLACEHOLDER_ROW) {
                    super.setSelectionPath(path);
                }
            }
        };
        tree.setRootVisible(false);
        tree.setDragEnabled(true);
        tree.setDropMode(DropMode.INSERT);
        tree.setTransferHandler(...);
        tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        tree.expandRow(CONTAINER_ROW);
        return new JScrollPane(tree);
    }

    protected static TreeModel getTreeModel() {
        DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
        DefaultMutableTreeNode a;

        a = new DefaultMutableTreeNode("A");
        root.add(a);
        a.add(new DefaultMutableTreeNode("X"));
        a.add(new DefaultMutableTreeNode("Y"));
        a.add(new DefaultMutableTreeNode("")); // Placeholder node

        root.add(new DefaultMutableTreeNode("B"));
        root.add(new DefaultMutableTreeNode("C"));
        return new DefaultTreeModel(root);
    }

    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.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    // TransferHandler code omitted
}

You may want to have a custom renderer to change the look of the placeholder rows (e.g. hide the icon, reduce the height (although you cannot make it 0 height), etc).

您可能希望使用自定义渲染器来更改占位符行的外观(例如,隐藏图标,降低高度(尽管不能使其高度为0),等等)。

#1


2  

I don't believe it's possible to accomplish exactly the behavior you want using Swing's built in drag and drop.

我不相信使用Swing内置的拖放功能可以完全实现您想要的行为。

A potential workaround is to set your drop mode to ON_OR_INSERT as follows: tree.setDropMode(DropMode.ON_OR_INSERT);

可能的解决方法是将drop模式设置为ON_OR_INSERT,如下所示:tree.setDropMode(DropMode.ON_OR_INSERT);

ON_OR_INSERT supports dropping either ON a node directly or between nodes. The INSERT part supports dropping between "A" and "B". You can then allow users to add a new grandchild of "A" after "A3" in one of two ways(or both):

ON_OR_INSERT支持直接在节点上或节点之间删除。 INSERT部分支持“A”和“B”之间的下降。然后,您可以允许用户以两种方式之一(或两者)在“A3”之后添加“A”的新孙子:

  • Interpret a drop directly on "A" as adding the item as the last child of "A"
  • 直接在“A”上解释一下,将该项添加为“A”的最后一个子项

  • Interpret a drop on a node as adding an element after the node(this is problematic if the node is not a leaf as the expected behavior is to add the element as a child)
  • 将节点上的删除解释为在节点后添加元素(如果节点不是叶子则会出现问题,因为预期的行为是将元素添加为子节点)

If you need exactly the behavior you describe, you'd probably need to write a custom DropTarget for the table and draw the effects you want(line showing where the drop will happen) yourself. I'd recommend avoiding this if at all possible.

如果您确切需要您描述的行为,您可能需要为表格编写自定义DropTarget并自己绘制所需的效果(显示放置位置的线条)。我建议尽可能避免这种情况。

#2


1  

This is a bug in the java drop location handling and is difficult to resolve without modifying the JDK source. It is still unresolved, but there is also a related enhancement request with possible impractical workarounds that was closed as won't fix.

这是java drop location处理中的一个错误,如果不修改JDK源,很难解决。它仍然没有得到解决,但也有一个相关的增强请求,可能的不切实际的解决方案已关闭,因为无法解决。

The only way I've been able to do this is by adding a dummy node as the last child of any container node. This is complicated and adds a row that may not be desirable, but allows the user to drop as the last child of a container node.

我能够做到这一点的唯一方法是添加一个虚节点作为任何容器节点的最后一个子节点。这很复杂并且添加了可能不合适的行,但允许用户作为容器节点的最后一个子节点。

public class TreeDragAndDrop {
    private static final int CONTAINER_ROW = 0;
    private static final int PLACEHOLDER_ROW = 3;

    private JScrollPane getContent() {
        JTree tree = new JTree(getTreeModel()) {
            // Overridden to prevent placeholder selection via the keyboad
            @Override
            public void setSelectionInterval(int index0, int index1) {
                int index = index0;
                // Probably would use a better check for placeholder row in production
                // and use a while loop in case the new index is also a placeholder
                if (index == PLACEHOLDER_ROW) {
                    int currentSelection = getSelectionCount() > 0 ? getSelectionRows()[0] : -1;
                    if (currentSelection < index) {
                        index++;
                    } else {
                        index--;
                    }
                }
                super.setSelectionInterval(index, index);
            }

            // Overridden to prevent placeholder selection via the mouse
            @Override
            public void setSelectionPath(TreePath path) {
                if (path != null && getRowForPath(path) != PLACEHOLDER_ROW) {
                    super.setSelectionPath(path);
                }
            }
        };
        tree.setRootVisible(false);
        tree.setDragEnabled(true);
        tree.setDropMode(DropMode.INSERT);
        tree.setTransferHandler(...);
        tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        tree.expandRow(CONTAINER_ROW);
        return new JScrollPane(tree);
    }

    protected static TreeModel getTreeModel() {
        DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
        DefaultMutableTreeNode a;

        a = new DefaultMutableTreeNode("A");
        root.add(a);
        a.add(new DefaultMutableTreeNode("X"));
        a.add(new DefaultMutableTreeNode("Y"));
        a.add(new DefaultMutableTreeNode("")); // Placeholder node

        root.add(new DefaultMutableTreeNode("B"));
        root.add(new DefaultMutableTreeNode("C"));
        return new DefaultTreeModel(root);
    }

    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.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    // TransferHandler code omitted
}

You may want to have a custom renderer to change the look of the placeholder rows (e.g. hide the icon, reduce the height (although you cannot make it 0 height), etc).

您可能希望使用自定义渲染器来更改占位符行的外观(例如,隐藏图标,降低高度(尽管不能使其高度为0),等等)。