JTree 更新节点而不折叠
Posted
技术标签:
【中文标题】JTree 更新节点而不折叠【英文标题】:JTree update nodes without collapsing 【发布时间】:2012-08-28 07:17:51 【问题描述】:我有一个需要更新 JTree
节点的 Java SE 7 应用程序。从 Oracle 使用此 thread 给出的教程中,没有给出关于如何更新代码上的标签(树上节点的显示文本)的提示。目前我使用DefaultTreeModel
作为我的JTree
的模型和DefaultMutableTreeNode
作为所述树的节点。
为了进一步详细了解我正在开发的应用程序,我正在开发一个聊天工具,让每个帐户的联系人及其可用性状态(在线、离线等)都显示出来。
问题是,如何更新特定节点的显示文本而不(最多)将其从其父节点中删除并将其添加到其指定索引上。喜欢DefaultMutableTreeNode.setText("<new label>")
?
更新:2013 年 1 月 20 日
重新定义问题以进行澄清。
【问题讨论】:
您应该只从 UI 线程更新您的 JTree - 如果您这样做,则无需同步任何内容。 如果JTree可能在 如果一切都在同一个线程上执行(应该是这样),那么所有的操作都是顺序的。可能发生的最糟糕的事情是,如果更新过于频繁,它可能会很慢,但更新会一个接一个地运行。 是的,但是当我调用DefaultTreeModel.reload()
到我的 JTree
的模型时,节点崩溃了,我如何更新特定节点而不让任何其他有子节点崩溃?跨度>
也许检查this post和***.com/questions/10435133/…
【参考方案1】:
愿这个简单且可执行的程序帮助您解决问题。
public class JTreeDemo extends JPanel
implements Runnable
private JTree tree;
private DefaultTreeModel treeModel ;
private Random rnd = new Random();
private List<User> userList;
public JTreeDemo()
super( );
//Create the nodes.
DefaultMutableTreeNode top =
new DefaultMutableTreeNode("Users");
treeModel = new DefaultTreeModel(top);
createNodes(top);
//Create a tree that allows one selection at a time.
tree = new JTree(treeModel);
tree.getSelectionModel().setSelectionMode
(TreeSelectionModel.SINGLE_TREE_SELECTION);
//Create the scroll pane and add the tree to it.
JScrollPane treeView = new JScrollPane(tree);
//Add the split pane to this panel.
add(treeView);
public String getRandomStatus()
int nextInt = rnd.nextInt(100);
if( nextInt%2==0)
return "Online";
else
return "Offline";
@Override
public void run()
while(true)
try
Thread.sleep(1000);
int nextInt = rnd.nextInt(10);
User user = userList.get(nextInt);
user.setStatus(getRandomStatus());
treeModel.nodeChanged(user);
catch (InterruptedException ex)
// handle it if necessary
private class User extends DefaultMutableTreeNode
public String userName;
public String status;
public User(String name)
userName = name;
public void setStatus(String status)
this.status = status;
public String getStatus()
return status;
@Override
public String toString()
String color = status.equals("Online") ? "Green" : "Red";
return "<html><b color='"+color+"'>"+
userName +"-"+status +"</b></html>";
private void createNodes(DefaultMutableTreeNode top)
userList = new ArrayList() ;
for(int i=0;i<10;i++)
User u1 = new User("User " + (i+1));
u1.setStatus("Online");
top.add(u1);
userList.add(u1);
private static void createAndShowGUI()
JFrame frame = new JFrame("TreeDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Add content to the window.
JTreeDemo jTreeDemo = new JTreeDemo();
frame.add(jTreeDemo);
frame.pack();
frame.setVisible(true);
// update status randomly
Thread thread = new Thread(jTreeDemo);
thread.start();
public static void main(String[] args)
javax.swing.SwingUtilities.invokeLater(new Runnable()
public void run()
createAndShowGUI();
);
我添加了一个线程来随机更新状态,希望您可以根据需要进行修改。
输出:
编辑:
1. 根据建议我删除了reload(node)
并添加了树模型重新加载。
【讨论】:
-1 a) 从 EDT 更新模型 b) 重新加载而不是触发更改 c) (小问题)节点内的视觉装饰 - 出于视图原因,不要覆盖 toString,这就是任务渲染器的 @kleopatra 感谢您的意见。据我所知,这就是我提到我的回答可以帮助您解决问题的原因。我可以知道什么是 EDT 吗?【参考方案2】:也许如果你使用 'nodeChanged()' 而不是 'reload()' 你会得到你想要的效果。
DefaultTreeModel 类中有很多方法会导致树的各个部分被更改和重绘。 DefaultTreeModel 上还有其他方法只会导致重绘发生。
您提到“reload(node)”并评论说当您调用它时它会导致树崩溃。 'reload' 导致整个子树从该节点开始完全重绘。 (但如果该节点不可见,则不会发生任何变化。)这称为“结构变化”。
'insertNodeInto()' 和 'removeNodeFromParent()' 通过添加或删除节点然后重绘来修改树结构。
我认为 'nodeChanged()' 是您需要的,因为它只是通知模型节点中的某些更改将导致它以不同的方式显示。也许可显示的文本现在与以前不同了。也许您更改了节点中的用户对象。那是您在节点上调用“nodeChanged()”的时候。
您应该在自己的崩溃代码和提供的示例程序 vels4j 中尝试使用“nodeChanged()”来代替“reload()”调用。这可能会解决问题。
请注意,DefaultTreeModel 上还有另外两个用于其他情况的方法系列:
这些方法使用树节点并使用树路径来定义发生更改的位置。它们不会更改树下的数据结构,但会通知模型发生了某些更改,以便它可以通知实际重绘事物或以其他方式响应更改的侦听器。
nodesWereInserted()
nodesWereRemovde()
nodesChanged()
nodeStructureChanged()
还有一组 fire...()
方法在 DefaultTreeModel 和您可能创建的任何子类内部使用。他们只是通知任何听众发生了一些变化。请注意,它们受到保护。
【讨论】:
+1 DefaultTreeModel#nodeChanged(javax.swing.tree.TreeNode) 是要走的路。 您能否提供@vels4j 提供的SSCCE,以便我接受您的回答。 您可以使用@vels4j 代码并将reload() 调用更改为nodeChanged()。我认为不需要任何进一步的。我希望有一种方法来分配功劳,因为他/她做了大部分工作来给你答案。我只是添加了一个改进,以防止它崩溃并认为结构发生了变化。【参考方案3】:如果节点包含树中唯一的对象并且实现了方法equals
和hashCode
(例如,您显示来自数据库的具有唯一ID 的字符串或对象),则很容易。首先,您遍历所有扩展节点并将对象从节点中保存在集合中。然后您执行模型的更新。更新后迭代所有节点,如果它们在集合中,则展开树中的节点。
如果节点不是唯一的 - 您需要在集合中保存完整的树路径(例如作为列表)并在更新后检查它以展开节点。
如果对象既没有equals
也没有hashCode
(这两个方法都必须实现)- 不能使用这个变体。
【讨论】:
是的,但它可能需要一些 CPU 时间,因为我们谈论的是每次要进行更新时都遍历节点。【参考方案4】:只是为了记录(我投票给 Lee Meador),DefaultTreeModel#nodeChanged(javax.swing.tree.TreeNode) 是要走的路:
public class TestFrame extends JFrame
public TestFrame()
//create gui with simple jtree (and DefaultTreeModel)
JButton changeBtn = new JButton();
final JTree jTree = new JTree();
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
changeBtn.setText("update selected node");
getContentPane().add(changeBtn, java.awt.BorderLayout.PAGE_END);
DefaultMutableTreeNode treeNode1 = new DefaultMutableTreeNode("root");
DefaultMutableTreeNode treeNode2 = new DefaultMutableTreeNode("blue");
treeNode1.add(treeNode2);
treeNode2 = new DefaultMutableTreeNode("violet");
DefaultMutableTreeNode treeNode3 = new DefaultMutableTreeNode("red");
treeNode2.add(treeNode3);
treeNode3 = new DefaultMutableTreeNode("yellow");
treeNode2.add(treeNode3);
treeNode1.add(treeNode2);
jTree.setModel(new DefaultTreeModel(treeNode1));
getContentPane().add(jTree, BorderLayout.CENTER);
pack();
//add listener to button, to change selected node on button click
changeBtn.addActionListener(new ActionListener()
public void actionPerformed(ActionEvent evt)
DefaultMutableTreeNode dmt = (DefaultMutableTreeNode)jTree.getSelectionPath().getLastPathComponent();
//update content/representation of selected node
dmt.setUserObject("My update: " + new Date());
//nodeChanged
((DefaultTreeModel) jTree.getModel()).nodeChanged(dmt);
);
public static void main(String args[])
EventQueue.invokeLater(new Runnable()
public void run()
new TestFrame().setVisible(true);
);
【讨论】:
以上是关于JTree 更新节点而不折叠的主要内容,如果未能解决你的问题,请参考以下文章