java - 突出显示 JTree 节点在幕后工作,但不直观

标签 java swing jtree

在我的应用程序中,我在左侧显示一个 JTree,如果用户双击叶子,相应的数据就会加载到右侧。加载此数据时,我 (i) 保存现有文档(此处不相关),(ii) 更新树以考虑可能发生的任何更改,(iii) 确保在更新后的树(即用户双击的节点)和 (iv) 加载所选节点。 应用程序逻辑工作正常,即加载了正确的文件,因此我们知道在树中选择了正确的节点,但在上述步骤之后,视觉上根本没有选择任何节点。

我知道this question但问题似乎是那棵树没有聚焦。我已经尝试了该帖子中建议的不同补救措施,但未能解决我的问题。 (还有 this related forum thread ,尽管该网站现在似乎已关闭。此外, this question 表面上似乎相似,但问题源于 OP 构建了专有渲染器。)

请参阅下面我的代码;我试图将其简化为 SSCCE,但我仍然陷入困境。 此时我最好的猜测是,问题与每次调用 updateTree 时都会创建一个全新的 TreeModel 并将其加载到树中这一事实有关,这在某种程度上使其变得不可能直观地选择正确的节点。如果情况确实如此,那么一种潜在的解决方案是更改 TreeModel,而不是从头开始重新创建它,但是 (i) 这对我来说不太方便,并且 (ii )我相信这本身就是一个有趣的问题。

public class JTree_Problem_SSCCE
extends JFrame
{
    private final JTree tree;

    public JTree_Problem_SSCCE()
    {
        super("XYZ");

        // Tree to select data
        DefaultTreeModel treeModel = getTreeModel();

        this.tree = new JTree(treeModel);
        // I don't allow the user to select more than one node at a time but I can reproduce the problem with or without this
        //TreeSelectionModel tsm = new DefaultTreeSelectionModel();
        //tsm.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        //tree.setSelectionModel(tsm);
        tree.addMouseListener(new TreeMouseAdapter());
        expandAllTreeNodes();
        getContentPane().add(tree,BorderLayout.WEST);

        setLocation(25,25);
        setSize(1700,1000);
        setVisible(true);
    }

    private DefaultTreeModel getTreeModel()
    {
        DefaultMutableTreeNode n1 = new DefaultMutableTreeNode("Root");
        DefaultMutableTreeNode n2 = new DefaultMutableTreeNode("Child 1");
        DefaultMutableTreeNode n3 = new DefaultMutableTreeNode("Child 2");
        n1.add(n2);
        n1.add(n3);
        return new DefaultTreeModel(n1);
    }

    private void updateTree(DefaultMutableTreeNode treeNode)
    {
        DefaultTreeModel dtm = getTreeModel();
        tree.setModel(dtm);
        expandAllTreeNodes();

        // No idea why the below doesn't work visually (the application logic works just fine but no tree node is actually selected)
        System.err.println(tree.getExpandsSelectedPaths()); // always returns true (I've seen this to be the problem in other questions)
        System.err.println(new TreePath(((DefaultTreeModel)tree.getModel()).getPathToRoot(treeNode)));
        if (treeNode != null)
        {
            tree.setSelectionPath(new TreePath(((DefaultTreeModel)tree.getModel()).getPathToRoot(treeNode)));
        }

        // As recommended in the answers here (https://stackoverflow.com/q/8896678/8031521),
        // I have tried the below but to no avail
//        tree.requestFocus(); // I have also tried requestFocusInWindow
//        SwingUtilities.invokeLater(new Runnable() {
//            @Override
//            public void run()
//            {
//                tree.setSelectionPath(new TreePath(((DefaultTreeModel)tree.getModel()).getPathToRoot(treeNode)));
//            }
//        });
    }

    private void expandAllTreeNodes()
    {
        // Expand all the nodes in the tree
        // See https://stackoverflow.com/a/15211697/8031521
        for (int i = 0; i < tree.getRowCount(); i++)
        {
            tree.expandRow(i);
        }
    }

    class TreeMouseAdapter
    extends MouseAdapter
    {
        @Override
        public void mouseClicked(MouseEvent e)
        {
            if (e.getClickCount() == 2 &&
                    ((DefaultMutableTreeNode)tree.getLastSelectedPathComponent()).isLeaf())
            {
                // [Before opening the new file, save the old one]
                // After saving the old file, make sure the tree is up to date
                updateTree((DefaultMutableTreeNode)tree.getLastSelectedPathComponent());
                // [Now we open the new file]
            }
        }
    }

}

最佳答案

该问题与每次调用 updateTree 时创建一个全新的 TreeModel 有关。问题在于 treeNode 变量引用旧树中的节点。因此,从新树的根到旧树中的节点不存在路径(即,从 treeNode 开始多次调用 getParent() 将导致旧树的根,而不是新树的根)。我发现有两种可能的选择可以解决您的问题。

选项1:搜索新的树节点

您可以编写如下所示的函数,以旧treeNode的路径从新根开始搜索树节点。

private static DefaultMutableTreeNode searchTree(DefaultMutableTreeNode root, Object[] path) {
    if (!root.getUserObject().equals(path[0])) {
        // completely different root
        // potentially problematic
        return null;
    }

    DefaultMutableTreeNode node = root;
    for (int i = 1; i < path.length; ++i) {
        Object searchItem = path[i];
        Enumeration<TreeNode> children = node.children();
        boolean found = false;
        while (children.hasMoreElements()) {
            DefaultMutableTreeNode child = (DefaultMutableTreeNode) children.nextElement();
            if (searchItem.equals(child.getUserObject())) {
                found = true;
                node = child;
                break;
            }
        }

        if (!found) {
            // path does not exist any more
            // potentially problematic
            return null;
        }
    }

    return node;
}

然后,在设置树选择路径之前,您可以将以下内容添加到 updateTree 方法中。

treeNode = searchTree((DefaultMutableTreeNode) tree.getModel().getRoot(), treeNode.getUserObjectPath());

选项 2:修改现有树

不要每次修改现有的树模型时都创建一个新的树模型。对于每个目录,首先在目录中创建一组文件,并在树中为该目录创建一组树节点。删除不在目录中的文件的所有树节点。然后,为目录中尚未在树中的所有文件创建节点。此选项可能比选项 1 更复杂,但它不会在整个代码中产生重复树节点的潜在问题。

关于java - 突出显示 JTree 节点在幕后工作,但不直观,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53291818/

相关文章:

java - 如何在 DefaultTableModel 的单元格中获取新更新的数据

swing - 对象的 JTree?

java - 有没有办法检测是否要在 JTree 上进行放置?

java - 未找到 Servlet-Context : Spring 4 Hibernate 4 Maven JSF PrimeFaces in NetBeans

java - 为什么 Netbeans 在我的 Java 代码中建议我使用 "Flip operands of the binary operators"

java - 是否可以在 JSP 中显示 Swing 组件?

java - 如何在 Java 中使轻量级 Swing 或 AWT 组件半透明

java - 在 JTree 节点 Swing 处右键单击显示弹出框

java - 如何在多线程应用程序关闭时摆脱有关 "no appenders"的 log4j 消息警告?

JAVA-8流收集高级用法