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

时间:2011-01-31 12:42:15

标签: java swing drag-and-drop jtree

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

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

root
  child A
    grandchild A1
    grandchild A2
  child C
    grandchild C1

现在有两种选择:

  1. 将新孙子添加到“孩子A”,即“孙子A3”或
  2. 在“孩子A”和“孩子B”之间插入一个新的“孩子B”。
  3. 在SWT中,可以通过在垂直方向上移动节点来实现这一点。水平线指示符将显示树节点插入的嵌套级别。

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

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

2 个答案:

答案 0 :(得分:2)

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

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

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

  • 直接在“A”上解释一下,将该项目添加为“A”
  • 的最后一个子项
  • 解释节点上的drop作为在节点之后添加元素(如果节点不是叶子则会出现问题,因为预期的行为是将元素添加为子节点)

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

答案 1 :(得分:1)

这是bug in the java drop location handling,如果不修改JDK源,很难解决。它仍然没有得到解决,但也有一个related enhancement request可能不切实际的解决方案已被关闭,因为它不会解决。

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

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
}

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