Reputation: 366
I have created a custom ObservableList implementation for a list of TreeItems. My custom implementation can listen to various notifications from inside my app (using OSGi EventAdmin), and update itself accordingly. I then expect its consumer (a TreeView widget) to be updated with the changes to the list. However, I can't see how to notify the consumer.
In the ObservableList subclass I am implementing addListener(ListChangeListener), which I would expect to get called when the object is added to the widget. However it is never called; I have no listeners thus no apparent way to notify anyone when the list changes. I must be missing something.
Here is a snippet from my TreeItem implementation, which returns an instance of my ObservableList in response to a getChildren call:
@Override
public ObservableList<TreeItem<DataObject>> getChildren() {
if (needChildren) {
needChildren = false;
children = new MyObservableList();
}
return children;
}
Here is an abridged version of my custom ObservableList implementation, which simply wraps an FXCollections.observableArrayList and adds an OSGi event handler. I listen to changes on the internal list so that I can pass those changes on to my listeners.
public class MyObservableList implements ObservableList<TreeItem<DataObject>>, EventHandler {
private List<ListChangeListener<? super TreeItem<DataObject>>> changeListeners = new ArrayList<>();
private List<InvalidationListener> invalidationListeners = new ArrayList<>();
private ObservableList<TreeItem<DataObject>> theList;
private int size;
public MyObservableList() {
theList = FXCollections.observableArrayList();
theList.addListener(new ListChangeListener<TreeItem<DataObject>>() {
@Override
public void onChanged(Change<? extends TreeItem<DataObject>> change) {
fireValueChangedEvent(change);
}
});
}
@Override
public int size() {
return theList.size();
}
@Override
public boolean isEmpty() {
return (size == 0);
}
@Override
public boolean contains(Object o) {
return theList.contains(o);
}
@Override
public Iterator iterator() {
return theList.iterator();
}
@Override
public boolean remove(Object o) {
return theList.remove(o);
}
@Override
public boolean addAll(Collection c) {
return theList.addAll(c);
}
@Override
public boolean addAll(int index, Collection c) {
return theList.addAll(index, c);
}
@Override
public void clear() {
theList.clear();
}
@Override
public TreeItem<DataObject> get(int index) {
return theList.get(index);
}
@Override
public int indexOf(Object o) {
return theList.indexOf(o);
}
@Override
public int lastIndexOf(Object o) {
return theList.lastIndexOf(o);
}
@Override
public ListIterator listIterator() {
return theList.listIterator();
}
@Override
public ListIterator listIterator(int index) {
return theList.listIterator(index);
}
@Override
public List<TreeItem<DataObject>> subList(int fromIndex, int toIndex) {
return theList.subList(fromIndex, toIndex);
}
@Override
public Object[] toArray(Object[] a) {
return theList.toArray(a);
}
@Override
public void addListener(ListChangeListener<? super TreeItem<DataObject>> listChangeListener) {
changeListeners.add(listChangeListener);
}
@Override
public void removeListener(ListChangeListener<? super TreeItem<DataObject>> listChangeListener) {
changeListeners.remove(listChangeListener);
}
@Override
public boolean addAll(TreeItem<DataObject>... treeItems) {
return theList.addAll(treeItems);
}
@Override
public boolean setAll(TreeItem<DataObject>... treeItems) {
return theList.setAll(treeItems);
}
@Override
public boolean setAll(Collection<? extends TreeItem<DataObject>> treeItems) {
return theList.setAll(treeItems);
}
@Override
public boolean removeAll(TreeItem<DataObject>... treeItems) {
return theList.removeAll(treeItems);
}
@Override
public boolean retainAll(TreeItem<DataObject>... treeItems) {
return theList.retainAll(treeItems);
}
@Override
public void remove(int i, int i2) {
theList.remove(i, i2);
}
@Override
public Object[] toArray() {
return theList.toArray();
}
@Override
public boolean add(TreeItem<DataObject> dataObjectTreeItem) {
return theList.add(dataObjectTreeItem);
}
@Override
public boolean containsAll(Collection<?> c) {
return theList.containsAll(c);
}
@Override
public boolean removeAll(Collection<?> c) {
return theList.removeAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
return theList.retainAll(c);
}
@Override
public TreeItem<DataObject> set(int index, TreeItem<DataObject> element) {
return theList.set(index, element);
}
@Override
public void add(int index, TreeItem<DataObject> element) {
theList.add(index, element);
}
@Override
public TreeItem<DataObject> remove(int index) {
return theList.remove(index);
}
@Override
public void addListener(InvalidationListener invalidationListener) {
invalidationListeners.add(invalidationListener);
}
@Override
public void removeListener(InvalidationListener invalidationListener) {
invalidationListeners.remove(invalidationListener);
}
private void fireValueChangedEvent(ListChangeListener.Change<? extends TreeItem<DataObject>> change) {
for (ListChangeListener<? super TreeItem<DataObject>> listener : changeListeners) {
listener.onChanged(change);
}
}
@Override
public void handleEvent(Event event) {
// Here I add or remove TreeItem<DataObject> instances to the list based on event.
//
// At this point, onChanged() gets called above in my listener, but my changeListeners list is empty. There is
// no one to pass the Change on to.
}
}
Thanks for any help.
Upvotes: 1
Views: 1256
Reputation: 366
I figured out what's going on here after looking through the JavaFX source.
It turns out that the listening on the ObservableList of children is all set up in the TreeItem itself, not the TreeView as I had somehow assumed. This means that any subclass of TreeView that overrides getChildren() must call super.getChildren() and add its children to the resulting list. This means that using a custom ObservableList implementation is not possible as TreeItem is hardcoded to use FXCollections.observableArrayList() to create the list.
I am taking a different approach to this now where I call super.getChildren(), add my children, and then instantiate another object that holds a reference to that list and does all of my app's event handling business, operating on the list as needed. So my getChildren() method looks something like this now.
private MyEventHandler eventHandler;
private ObservableList<TreeItem<DataObject>> children;
@Override
public ObservableList<TreeItem<DataObject>> getChildren() {
if (needChildren) {
needChildren = false;
children = super.getChildren();
eventHandler = new MyEventHandler(children); // handles app events
}
return children;
}
Upvotes: 1