Java JTree在不折叠的情况下更新节点

Java JTree在不折叠的情况下更新节点,java,swing,jtree,defaulttablemodel,Java,Swing,Jtree,Defaulttablemodel,我有一个JavaSE7应用程序,需要更新JTree节点。从Oracle提供的使用此工具的教程中,没有给出如何在代码上更新标签(树上节点的显示文本)的提示。目前,我使用DefaultTreeModel作为我的JTree的模型,使用DefaultMutableTreeNode作为所述树的节点 为了进一步了解我正在使用的应用程序的详细信息,我正在开发一个聊天工具,其中显示每个帐户的联系人及其可用状态(在线、离线等) 问题是,如何更新特定节点的显示文本,而不(最多)将其从其父节点删除并添加到其指定索引中

我有一个JavaSE7应用程序,需要更新
JTree
节点。从Oracle提供的使用此工具的教程中,没有给出如何在代码上更新标签(树上节点的显示文本)的提示。目前,我使用
DefaultTreeModel
作为我的
JTree
的模型,使用
DefaultMutableTreeNode
作为所述树的节点

为了进一步了解我正在使用的应用程序的详细信息,我正在开发一个聊天工具,其中显示每个帐户的联系人及其可用状态(在线、离线等)

问题是,如何更新特定节点的显示文本,而不(最多)将其从其父节点删除并添加到其指定索引中。像
DefaultMutableTreeNode.setText(“”


更新:2013年1月20日


重新定义问题以供澄清。

这个简单的可执行程序可以帮助您解决问题

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();
        }
    });
 }
}
公共类JTreeDemo扩展了JPanel
实现可运行{
私有JTree树;
私有DefaultTreeModel树模型;
私有随机rnd=新随机();
私有列表用户列表;
公共JTreeDemo(){
超级();
//创建节点。
DefaultMutableTreeNode顶部=
新的DefaultMutableTreeNode(“用户”);
treeModel=新的默认treeModel(顶部);
createNodes(顶部);
//创建一个允许一次选择一个选项的树。
树=新的JTree(树模型);
tree.getSelectionModel().setSelectionMode
(树选择模型。单树选择);
//创建滚动窗格并将树添加到其中。
JScrollPane treeView=新的JScrollPane(树);
//将拆分窗格添加到此面板。
添加(treeView);
}
公共字符串getRandomStatus(){
int-nextInt=rnd.nextInt(100);
如果(nextInt%2==0){
返回“在线”;
}否则{
返回“离线”;
}
}
@凌驾
公开募捐{
while(true){
试试{
睡眠(1000);
int-nextInt=rnd.nextInt(10);
User=userList.get(nextInt);
user.setStatus(getRandomStatus());
treeModel.nodeChanged(用户);
}捕获(中断异常例外){
//必要时处理它
}
}
}
私有类用户扩展DefaultMutableTreeNode{
公共字符串用户名;
公共字符串状态;
公共用户(字符串名称){
用户名=名称;
}
公共无效设置状态(字符串状态){
这个状态=状态;
}
公共字符串getStatus(){
返回状态;
}
@凌驾
公共字符串toString(){
字符串颜色=状态。等于(“在线”)?“绿色”:“红色”;
返回“”+
用户名+“-”+状态+”;
}
}
私有void createNodes(DefaultMutableTreeNode顶部){
userList=newarraylist();

对于(int i=0;i,如果您使用'nodeChanged()'而不是'reload()',可能会得到您想要的效果

DefaultTreeModel类上有一组方法导致树的各个部分被更改和重画。DefaultTreeModel上还有其他方法只导致重画

您提到了“重新加载(节点)”,并评论说,当您调用它时,它会导致树崩溃。“重新加载”会导致从该节点开始完全重新绘制整个子树。(但如果该节点不可见,则不会更改任何内容。)这称为“结构更改”

“insertNodeInto()”和“removeNodeFromParent()”通过添加或删除节点,然后重新绘制来修改树结构

我认为“nodeChanged()”是您需要的,因为它只是通知模型节点中的某些更改会导致其显示不同。可能现在可显示的文本与以前不同。可能您更改了节点中的用户对象。这就是在节点上调用“nodeChanged()”的时候

您应该在自己正在崩溃的代码和提供的示例程序vels4j中尝试使用“nodeChanged()”代替“reload()”调用。这可能会解决问题

请注意,DefaultTreeModel上还有两个在其他情况下使用的其他方法系列:

这些方法与树节点一起工作,并使用树路径来定义更改发生的位置。它们不会更改树下的数据结构,而是通知模型某些内容已更改,以便它可以通知侦听器实际重新绘制内容或以其他方式响应更改

nodesWereInserted()
nodesweremovde()
nodesChanged()
nodeStructureChanged()


还有一套
fire…()
在DefaultTreeModel和您可能创建的任何子类内部使用的方法。它们仅通知任何侦听器某些内容已更改。请注意,它们受到保护。

如果节点包含树中唯一的对象,并且实现了method
equals
hashCode
(例如,显示数据库中具有唯一ID的字符串或对象)。首先,迭代所有展开的节点并将节点中的对象保存到集合中。然后,执行模型更新。更新后,迭代所有节点,如果它们在集合中,则展开树中的节点。
如果节点不唯一-您需要在集合中保存完整的树路径(例如,作为列表),并在更新后检查它以展开节点。
如果对象既不具有
equals
也不具有
hashCode
(这两种方法都必须实现)-则不能使用此变量。

只是为了记录在案(我投票支持Lee Meador),这是一种方法:

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);
            }
        });
    }
}

您应该只从UI线程更新JTree—如果这样做,则不需要同步任何内容。如果所有内容都在同一线程上执行,JTree可能会在中更新多次,这是否仍然不是问题(应该是