在我的应用程序中,我在左侧显示了一个JTree,如果用户双击叶子,则将在右侧加载相应的数据。加载此数据时,我(i)保存现有文档(此处不相关),(ii)更新树以考虑可能发生的任何更改,(iii)确保在树中选择了正确的节点。更新后的树(即用户双击的节点)并(iv)加载选定的节点。 应用程序逻辑运行正常,即正确的文件已加载,因此我们知道在树中选择了正确的节点,但从视觉上看,在上述步骤之后根本没有选择任何节点。< / strong>
我知道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]
}
}
}
}
答案 0 :(得分:1)
问题与在每次致电TreeModel
时创建一个全新的updateTree
有关。问题在于treeNode
变量引用旧树中的一个节点。因此,从新树的根到旧树的节点之间没有路径(即,从getParent()
开始多次调用treeNode
会导致旧树的根,而不是旧树的根)。新的一个)。我看到两个可能的方案可以解决您的问题。
您可以编写以下函数,从具有新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());
不是每次都修改现有的树模型都创建新的树模型。对于每个目录,首先在目录中创建一组文件,并在目录树中创建目录的一组树节点。删除目录中没有的文件的所有树节点。然后,为目录中所有尚未在树中的文件创建节点。该选项可能比选项1更复杂,但不会在整个代码中造成树节点重复的潜在问题。