There are several aspects to your question (and I'm not entirely sure which aspect is the problem :-) I'll assume that your POJO is somehow notifying listeners about changes, could be by being a full-fledged JavaBean. That is, it complies with its notification contract via firing propertyChange events as needed or by some other means - otherwise, you would need some manual push of the change anyway.
The basic approach to make an FX-ObservableList notify its own listeners on mutations of contained elements is to configure it with a custom Callback that provides an array of Observables. If the elements have fx-properties you would do something like:
Callback<Person, Observable[]> extractor = new Callback<Person, Observable[]>() {
@Override
public Observable[] call(Person p) {
return new Observable[] {p.lastNameProperty(), p.firstNameProperty()};
}
};
ObservableList<Person> teamMembers = FXCollections.observableArrayList(extractor);
// fill list
If the pojo is-a full-fledged core javaBean, its properties have to be adapted to fx-properties, f.i. by using JavaBeanProperty:
Callback<PersonBean, Observable[]> extractor = new Callback<PersonBean, Observable[]>() {
List<Property> properties = new ArrayList<Property>();
@Override
public Observable[] call(PersonBean arg0) {
JavaBeanObjectProperty lastName = null;
JavaBeanObjectProperty age = null;
try {
lastName = JavaBeanObjectPropertyBuilder.create()
.bean(arg0).name("lastName").build();
age = JavaBeanObjectPropertyBuilder.create()
.bean(arg0).name("age").build();
// hack around losing weak references ...
properties.add(age);
properties.add(lastName);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return new Observable[] {lastName, age};
}
};
ObservableList<Person> teamMembers = FXCollections.observableArrayList(extractor);
// fill list
Note a caveat: without keeping a strong reference to the adapted properties somewhere, they will be quickly garbage-collected - and then appear to have no effect at all (falling into the trap again and again, not sure if there's a good strategy to avoid it).
For any other means of (possibly coarse-grained) notification, you can implement a custom adapter: the adapter below listens to all propertyChanges of a bean. Listening to other types of events would be quite analogous.
/**
* Adapt a Pojo to an Observable.
* Note: extending ObservableValue is too much, but there is no ObservableBase ...
*
* @author Jeanette Winzenburg, Berlin
*/
public class PojoAdapter<T> extends ObservableValueBase<T> {
private T bean;
private PropertyChangeListener pojoListener;
public PojoAdapter(T pojo) {
this.bean = pojo;
installPojoListener(pojo);
}
/**
* Reflectively install a propertyChangeListener for the pojo, if available.
* Silently does nothing if it cant.
* @param item
*/
private void installPojoListener(T item) {
try {
Method method = item.getClass().getMethod("addPropertyChangeListener",
PropertyChangeListener.class);
method.invoke(item, getPojoListener());
} catch (NoSuchMethodException | SecurityException | IllegalAccessException |
IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
/**
* Returns the propertyChangeListener to install on each item.
* Implemented to call notifyList.
*
* @return
*/
private PropertyChangeListener getPojoListener() {
if (pojoListener == null) {
pojoListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
fireValueChangedEvent();
}
};
}
return pojoListener;
}
@Override
public T getValue() {
return bean;
}
}
Its usage just the same as above (getting boring, isn't it :-)
Callback<PersonBean, Observable[]> extractor = new Callback<PersonBean, Observable[]>() {
@Override
public Observable[] call(PersonBean arg0) {
return new Observable[] {new PojoAdapter<PersonBean>(arg0)};
}
};
ObservableList<Person> teamMembers = FXCollections.observableArrayList(extractor);
// fill list
Unfortunately, automatic updates of a ListView with such cool list won't work reliably due to a bug that's fixed only in jdk8. In earlier versions, you are back at square 1 - somehow listening to the change and then manually updating the list:
protected void notifyList(Object changedItem) {
int index = list.indexOf(changedItem);
if (index >= 0) {
// hack around RT-28397
//https://javafx-jira.kenai.com/browse/RT-28397
list.set(index, null);
// good enough since jdk7u40 and jdk8
list.set(index, changedItem);
}
}