Обновление / обновление TreeModel
У меня есть серия потоков, получающих RSS-каналы через равные промежутки времени, и я хочу обновить пользовательское JTree с помощью PropertyChangeSupport. Однако он использует собственный класс, реализующий TreeModel, и я не уверен, как вызвать автоматическое изменение. Это возможно, или я должен использовать другой класс?
2 ответа
Вам придется использовать набор слушателей вашей модели, и когда ваш обновляющий поток отмечает некоторые изменения, вам придется запускать для них события. Я не думаю PropertyChangeSupport
очень поможет вам, так как данные модели дерева не в форме свойств Java-бинов, и вы не хотите запускать PropertyChangeEvents.
Наконец, в реализации TreeModel я сделал эти методы (с комментариями на немецком языке:-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});
}
Затем из других частей модели вызывались правильные методы всякий раз, когда что-то менялось.
Если вы хотите сделать это просто, вы всегда можете просто запустить treeStructureChanged
событие с корневым узлом, которое приведет к перезагрузке всего дерева. Для более тонких событий вам нужно посмотреть, что на самом деле изменилось, и запустить это.
Не совсем уверен, что вы подразумеваете под "обновить дерево с помощью PropertyChangeListener", но согласен с Полом: PropertyChangeListener не помогает в уведомлении слушателей treeModel (которые включают JTree).
Любая реализация TreeModel несет внутреннюю ответственность за уведомление своих слушателей с помощью соответствующих TreeModelEvents. Если он слушает изменения содержащихся узлов (которые могут запустить fi PropertyChangeEvents), он должен преобразовать их в TreeModelEvents.
Фрагмент псевдокода:
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();
}
);
}
}
Создание фактических событий немного сбивает с толку (и плохо документировано), поэтому SwingX имеет вспомогательный класс TreeModelSupport для упрощения этой задачи.