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()
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 TreeModelEvent
s, 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.
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));
}
}