How can I refresh a JTree after adding some nodes to the underlying model?

前端 未结 8 2080
情歌与酒
情歌与酒 2020-12-08 21:42

First of all, let me say that I dont use the DefaultTreeModel. I implement my own TreeModel, so i cant use the DefaultXXX stuff. The problem is this: Through some addStuff()

相关标签:
8条回答
  • 2020-12-08 22:26

    I faced the same "problem": calling treeNodesInserted() did not cause my JTree to update its contents.

    But the problem was in other place: I used wrong constructor for TreeModelEvent. I thought that I can create TreeModelEvent for treeNodesInserted() like that:

    //-- Wrong!!
    TreePath path_to_inserted_item = /*....*/ ;
    TreeModelEvent tme = new TreeModelEvent(my_source, path_to_inserted_item);
    

    This doesn't work.

    As stated in TreeModelEvent docs, this constructor is only needed for treeStructureChanged(). But for treeNodesInserted(), treeNodesRemoved(), treeNodesChanged() we should use another constructor:

    TreePath path_to_parent_of_inserted_items = /*....*/ ;
    int[] indices_of_inserted_items = /*....*/ ;
    Object[] inserted_items = /*....*/ ;
    TreeModelEvent tme = new TreeModelEvent(
          my_source,
          path_to_parent_of_inserted_items,
          indices_of_inserted_items,
          inserted_items
       );
    

    This code works, and JTree updates its contents properly.


    UPD: Actually, docs are unclear about using these TreeModelEvents, and especially with JTree, so, I want to tell about some questions that came to me when I tried to figure out how to deal with all this stuff.

    Firstly, as Paralife noted it his comment, cases when nodes are inserted/changed/removed, or when tree structure is changed, aren't orthogonal. So,

    Question #1: when should we use treeNodesInserted()/Changed()/Removed(), and when treeStructureChanged()?

    Answer: treeNodesInserted()/Changed()/Removed() can be used if only all the affected nodes have the same parent. Otherwise you may make several calls to these methods, or just call treeStructureChanged() once (and pass the root node of affected nodes to it). So, treeStructureChanged() is a kind of universal way, while treeNodesInserted()/Changed()/Removed() are more specific.

    Question #2: As far as treeStructureChanged() is a universal way, why do I need to deal with these treeNodesInserted()/Changed()/Removed()? Just call to treeStructureChanged() seems to be easier.

    Answer: If you use JTree to display contents of your tree, then the following thing might be a surprize for you (as it was for me) : when you call treeStructureChanged(), then JTree doesn't keep expand state of sub-nodes! Consider the example, here's contents of our JTree now:

    [A]
     |-[B]
     |-[C]
     |  |-[E]
     |  |  |-[G]
     |  |  |-[H]
     |  |-[F]
     |     |-[I]
     |     |-[J]
     |     |-[K]
     |-[D]
    

    Then you make some changes to C (say, rename it to C2), and you call treeStructureChanged() for that:

      myTreeModel.treeStructureChanged(
            new TreeModelEvent(
               this,
               new Object[] { myNodeA, myNodeC } // Path to changed node
               )
            );
    

    Then, nodes E and F will be collapsed! And your JTree will look like that:

    [A]
     |-[B]
     |-[C2]
     |  +-[E]
     |  +-[F]
     |-[D]
    

    To avoid that, you should use treeNodesChanged(), like that:

      myTreeModel.treeNodesChanged(
            new TreeModelEvent(
               this,
               new Object[] { myNodeA }, // Path to the _parent_ of changed item
               new int[] { 1 },          // Indexes of changed nodes
               new Object[] { myNodeC }, // Objects represents changed nodes
                                         //    (Note: old ones!!! 
                                         //     I.e. not "C2", but "C",
                                         //     in this example)
               )
            );
    

    Then, expanding state will be kept.


    I hope this post will be useful for somebody.

    0 讨论(0)
  • 2020-12-08 22:32

    I also found implementing TreeModel a bit confusing when the tree consist of more than just Folders(root) and Files(child), so I've used the DefaultTreeModel. This works for me. The method creates or refreshes the JTree. Hope this helps.

        public void constructTree() {       
    
            DefaultMutableTreeNode root =
                    new DefaultMutableTreeNode(UbaEnv.DB_CONFIG);
            DefaultMutableTreeNode child = null;
    
            HashMap<String, DbConfig> dbConfigs = env.getDbConfigs();
            Iterator it = dbConfigs.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry pair = (Map.Entry) it.next();
                child = new DefaultMutableTreeNode(pair.getKey());
    
                child.add(new DefaultMutableTreeNode(UbaEnv.PROP));
                child.add(new DefaultMutableTreeNode(UbaEnv.SQL));
                root.add(child);
            }
    
            if (tree == null) {
                tree = new JTree(new DefaultTreeModel(root));
                tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
                tree.addTreeSelectionListener(new TreeListener());
            } else {
                tree.setModel(new DefaultTreeModel(root));
            }
    }
    
    0 讨论(0)
提交回复
热议问题