Skip to Main Content

Java SE (Java Platform, Standard Edition)

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

How to reload a JTree and preserve previously expanded nodes

843806Sep 2 2007 — edited Sep 7 2007
Hi all,

This is a problem that I have been grappling with for some time. BUT - I think I have cracked it and in a reasonably urbane manner. I post my solution here in the hope that it may of help to others.

The problem:

I have a JTree populated with items from a database (this could be any other type of volatile data source). I browse my tree, expanding and collapsing nodes as I go. I get to a point where I want to refresh the tree so that it resembles the most up to date state of the backing data. BUT - after I refresh it, I want it to expand all those nodes that are currently expanded. The default behaviour is for all nodes to be collapsed. Here is what I do:

I trap each expand and collapse event. For each expand event, I populate a list with the user object present in the node, thus:
  private void processTreeExpansion(TreeExpansionEvent e){
        
        if (supressExpansionEvent == false) { // Not interested in this event if we are currently restoring the tree
            
            TreePath p = (TreePath) e.getPath(); // Get the tree path
            Object[] Objs = p.getPath(); // Get all the objects withiin that path
            if (Objs.length == 0) { return;} // This should never happen
            DefaultMutableTreeNode dmtn = (DefaultMutableTreeNode) Objs[Objs.length - 1]; // Derive a DMTN
            YObject myObject = (YObject) dmtn .getUserObject(); // This will always be a YObject Class object            
            expandedTreeObjects.add(myObject); // Place the object in my list
            
        }
        
    }
I do the opposite for each collapse event.

When I refresh the JTree I:

removeAllChildren() from the Root Node
Re-populate the tree nodes with the fresh data
call reload(0 on the Tree Model
and then call these methods:
private void restoreTree() {
        
        // Some ommited stuff
        
        // Process tree nodes from root node
        restoreTreeNode(tree, new TreePath(rootNode), null); // Not surprisingly, rootNode is my rootNode
        
    }
    private void restoreTreeNode(JTree tree, TreePath parent, DefaultMutableTreeNode treeNode) {
        
        // Traverse down through the children
        TreeNode node = (TreeNode) parent.getLastPathComponent(); // Get the last TreeNode component for this path
        
        if (node.getChildCount() >= 0) { // If the node have children?
            
            // Create a child numerator over the node
            Enumeration en = node.children();            
            while (en.hasMoreElements()) { // While we have children
                
                DefaultMutableTreeNode dmTreeNode = (DefaultMutableTreeNode)en.nextElement(); // Derive the node
                TreePath path = parent.pathByAddingChild(dmTreeNode); // Derive the path
                restoreTreeNode(tree, path, dmTreeNode); // Recursive call with new path
                
            } // End While we have more children
            
        } // End If the node have children?
        
        // Nodes need to be expanded from last branch node up
        
        
        if (treeNode != null) { // If true, this is the root node - ignore it
            
            YObject myUserObject = (YObject) treeNode.getUserObject(); // Get the user object from the node
                                                                       // Note - all the objects I place in tree nodes 
                                                                       // belong to the same class - YObject
            
            if (expandedTreeObjects.contains(myUserObject)) { // Is this present on the previously expanded list?
                
                tree.expandPath(parent); // et viola
                
            } 
            
        } // End If - root node
        
    }
Notes:

1. If any gurus out there spot a problem with this, please expand (pardon the pun) on it.
2. If the backing data for the tree can change, then using the row count to expand nodes is not very good, because they will have changed. In any case, tree.getRowCount() only returns the number of rows currently being displayed.
3. I had considered storing paths instead of user objects to be the basis of my treeRestore; but when I call tableModel.reload(), I think I get a completely new set of paths, and so the old ones are useless.
4. It just so happens that I populate this tree with objects all of the same class (YObject). But it would not be so difficult to extend it to cater for several different classes of object.


I post this here in the sincere hope that others may benefit from the lessons I have learned. This must be an extremely common requirement of a JTree, and yet it was not apparent to me how it may be easily achieved (can one call the above easy?). If anyone can improve on this, I welcome their thoughts.

Best regards to all,

Steve
Comments
Locked Post
New comments cannot be posted to this locked post.
Post Details
Locked on Oct 5 2007
Added on Sep 2 2007
5 comments
658 views