12

我有一个需要JTree更新节点的 Java SE 7 应用程序。从 Oracle 使用这个线程给出的教程中,没有给出关于如何更新代码上的标签(树上节点的显示文本)的提示。目前我正在DefaultTreeModel用作我的模型JTreeDefaultMutableTreeNode所述树的节点。

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

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


更新:2013 年 1 月 20 日

重新定义问题以进行澄清。

4

4 回答 4

10

也许如果你使用'nodeChanged()' 而不是'reload()' 你会得到你想要的效果。

DefaultTreeModel 类中有很多方法会导致树的各个部分被更改和重绘。DefaultTreeModel 上还有其他方法只会导致重绘发生。

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

'insertNodeInto()' 和 'removeNodeFromParent()' 通过添加或删除节点然后重绘来修改树结构。

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

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

请注意,DefaultTreeModel 上还有另外两个用于其他情况的方法系列:

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

nodesWereInserted() nodesWereRemovde() nodesChanged() nodeStructureChanged()

还有一组fire...()在 DefaultTreeModel 内部使用的方法以及您可以创建的任何子类。他们只是通知任何听众发生了一些变化。请注意,它们受到保护。

于 2013-01-21T19:13:38.343 回答
8

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

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)并添加了树模型重新加载。

于 2013-01-20T10:58:09.877 回答
0

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

于 2013-01-23T18:58:49.320 回答
0

只是为了记录(我投票给 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);
            }
        });
    }
}
于 2013-01-27T17:54:10.560 回答