Reputation: 19607
I have a series of threads retrieving RSS feeds at regular intervals and want to refresh a custom JTree using PropertyChangeSupport. However it uses a custom class implementing TreeModel and I'm not sure how to trigger an automatic change. Is this possible or should I use another class?
Upvotes: 2
Views: 5371
Reputation: 51524
Not entirely sure, what you mean by "refresh a tree by using a PropertyChangeListener", but agree with Paul: a PropertyChangeListener is not helpful in notifying the treeModel listeners (which include the JTree).
It is an intrinsic responsibility of any implementation of a TreeModel to notify its listeners by means of appropriate TreeModelEvents. If it listens to changes of contained nodes (which might fire f.i. PropertyChangeEvents) then it must translate those to TreeModelEvents.
A pseudo-code snippet:
public class MyTreeModel implements TreeModel {
PropertyChangeListener nodeListener;
// custom method to insert a node
public void addNodeTo(MyBean child, MyBean parent) {
// ... internal logic to add the new node
fireChildAdded(getPathToRoot(parent), child)
// add a PropertyChangeListener to new node so the model
// can comply to its notification contract
child.addPropertyChangeListener(getPropertyChangeListener();
}
protected void nodePropertyChanged(MyBean bean) {
firePathChanged(getPathToRoot(bean));
}
protected TreePath getPathToRoot(MyBean bean) {
// construct and return a treePath to the root
}
protected PropertyChangeListener getPropertyChangeListener() {
if (nodeListener == null) {
nodeListener = new PropertyChangeChangeListener() {
public void propertyChanged(...) {
nodeChanged((MyBean) e.getSource();
}
);
}
}
Constructing the actual events be a bit confusing (and is poorly documented), that's why SwingX has a helper class TreeModelSupport to simplify that task.
Upvotes: 2
Reputation: 74750
You'll have to use a collection of listeners of your model, and when your refreshing thread remarks some changes, you'll have to fire events to them. I don't think PropertyChangeSupport
will help you here much, as the Tree model's data is not in the form of Java beans properties, and you don't want to fire PropertyChangeEvents.
In a TreeModel implementation I did lastly, I made these methods (with german comments :-p)
/**
* Benachrichtigt die Listener, dass die Struktur unterhalb
* eines bestimmten Knotens komplett geändert wurde.
*/
private void fireStructureChanged(TreePath path) {
TreeModelEvent event = new TreeModelEvent(this, path);
for(TreeModelListener lis : listeners) {
lis.treeStructureChanged(event);
}
}
/**
* Benachrichtigt die Listener, dass unterhalb eines Knotens
* einige Knoten entfernt wurden.
*/
private void fireNodesRemoved(TreePath parentPath,
int[] indices, Object[] nodes) {
TreeModelEvent event =
new TreeModelEvent(this, parentPath, indices, nodes);
for(TreeModelListener lis : listeners) {
lis.treeNodesRemoved(event);
}
}
/**
* Benachrichtigt die Listener, dass ein bestimmter Knoten
* entfernt wurde.
*/
private void fireNodeRemoved(TreePath path, int index, Object node) {
fireNodesRemoved(path, new int[]{index}, new Object[]{node});
}
/**
* Benachrichtigt die Listener, dass sich das Aussehen einiger
* Unterknoten eines Knotens geändert hat.
*/
private void fireNodesChanged(TreePath parentPath,
int[] indices, Object[] nodes) {
TreeModelEvent event =
new TreeModelEvent(this, parentPath, indices, nodes);
for(TreeModelListener lis : listeners) {
lis.treeNodesChanged(event);
}
}
/**
* Benachrichtigt die Listener, dass sich das Aussehen eines Knotens
* geändert hat.
*
* @param parentPath der Pfad des Elternknotens des relevanten Knotens.
* @param index der Index des Knotens unterhalb des Elternknotens.
* Falls < 0, werden die Listener nicht benachrichtigt.
* @param node der Subknoten.
*/
private void fireNodeChanged(TreePath parentPath,
int index, Object node) {
if(index >= 0) {
fireNodesChanged(parentPath, new int[]{index}, new Object[]{node});
}
}
/**
* Benachrichtigt die Listener, dass unterhalb eines Knotens einige
* Knoten eingefügt wurden.
*/
private void fireNodesInserted(TreePath parentPath,
int[] indices, Object[] subNodes) {
TreeModelEvent event =
new TreeModelEvent(this, parentPath, indices, subNodes);
for(TreeModelListener lis : listeners) {
lis.treeNodesInserted(event);
}
}
/**
* Benachrichtigt die Listener, dass ein Knoten eingefügt wurde.
*/
private void fireNodeInserted(TreePath parentPath,
int index, Object node) {
fireNodesInserted(parentPath, new int[]{index}, new Object[]{node});
}
Then from the other parts of the model the right methods were invoked whenever something changed.
If you want to make it simple, you could simple always fire a treeStructureChanged
event with the root node, which would cause the whole tree to reload. For finer events, you'll need to look what in fact changed, and fire this.
Upvotes: 3