Is there a method to bind the content of two observable lists with conversion of elements between them? For example, something like this:
ObservableList<Model> models = FXCollections.observableArrayList();
ObservableList<TreeItem<Model>> treeItemModels = FXCollections.observableArrayList();
Bindings.bindContent(treeItemModels, models, m -> new TreeItem<Model>(m));
As James_D say, this functionality is absent in standard API and the one can use ReactFX framework for doing so. But if the excess dependents is not the option, such functionality can be easy implemented without them. For this it is enough to go throught the JDK sources of Bindings#bindContent
to ContentBinding#bind
to ListContentBinding
class and copy/paste with the neccessary modifications. As a result we get a binding which work like standard content binding:
ObservableList<Model> models = FXCollections.observableArrayList();
ObservableList<TreeItem<Model>> treeItemModels = FXCollections.observableArrayList();
BindingUtil.mapContent(treeItemModels, models, m -> new TreeItem<Model>(m));
The sources of this BindingUtil
:
public class BindingUtil {
public static <E, F> void mapContent(ObservableList<F> mapped, ObservableList<? extends E> source,
Function<? super E, ? extends F> mapper) {
map(mapped, source, mapper);
}
private static <E, F> Object map(ObservableList<F> mapped, ObservableList<? extends E> source,
Function<? super E, ? extends F> mapper) {
final ListContentMapping<E, F> contentMapping = new ListContentMapping<E, F>(mapped, mapper);
mapped.setAll(source.stream().map(mapper).collect(toList()));
source.removeListener(contentMapping);
source.addListener(contentMapping);
return contentMapping;
}
private static class ListContentMapping<E, F> implements ListChangeListener<E>, WeakListener {
private final WeakReference<List<F>> mappedRef;
private final Function<? super E, ? extends F> mapper;
public ListContentMapping(List<F> mapped, Function<? super E, ? extends F> mapper) {
this.mappedRef = new WeakReference<List<F>>(mapped);
this.mapper = mapper;
}
@Override
public void onChanged(Change<? extends E> change) {
final List<F> mapped = mappedRef.get();
if (mapped == null) {
change.getList().removeListener(this);
} else {
while (change.next()) {
if (change.wasPermutated()) {
mapped.subList(change.getFrom(), change.getTo()).clear();
mapped.addAll(change.getFrom(), change.getList().subList(change.getFrom(), change.getTo())
.stream().map(mapper).collect(toList()));
} else {
if (change.wasRemoved()) {
mapped.subList(change.getFrom(), change.getFrom() + change.getRemovedSize()).clear();
}
if (change.wasAdded()) {
mapped.addAll(change.getFrom(), change.getAddedSubList()
.stream().map(mapper).collect(toList()));
}
}
}
}
}
@Override
public boolean wasGarbageCollected() {
return mappedRef.get() == null;
}
@Override
public int hashCode() {
final List<F> list = mappedRef.get();
return (list == null) ? 0 : list.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
final List<F> mapped1 = mappedRef.get();
if (mapped1 == null) {
return false;
}
if (obj instanceof ListContentMapping) {
final ListContentMapping<?, ?> other = (ListContentMapping<?, ?>) obj;
final List<?> mapped2 = other.mappedRef.get();
return mapped1 == mapped2;
}
return false;
}
}
}
This functionality is not available in the standard API. However, the ReactFX framework provides a mechanism for doing this:
ObservableList<Model> models = FXCollections.observableArrayList();
ObservableList<TreeItem<Model>> treeItemModels
= LiveList.map(models, m -> new TreeItem<Model>(m));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With