I have a case where I need to filter a ObservableList<Item> based on some properties of the items (i.e. the condition is internal and not external). I found out that javafx has FilteredList so I tried it. I could set the predicate and filtering works, until the property value that determines the filtering changes. Setting the predicate is done now like following:
list.setPredicate(t -> !t.filteredProperty().get())
Since the predicate returns boolean and not BooleanProperty, the changes to that property are not reflected on the list.
Is there any easy solution to this? I could try to do some workarounds, e.g. create a separate list and sync that, or reset the predicate every time the property changes in one item hopefully retriggering the filtering, but I first wanted to ask in case someone knows a pretty solution as those workarounds certainly are not.
Create the underlying list with an extractor. This will enable the underlying list to fire update events when the filteredProperty() of any elements change. The FilteredList will observe these events and so will update accordingly:
ObservableList<Item> baseList = FXCollections.observableArrayList(item ->
new Observable[] {item.filteredProperty()});
FilteredList<Item> list = new FilteredList<>(baseList, t -> ! t.filteredProperty().get());
Quick demo:
import java.util.stream.IntStream;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
public class DynamicFilteredListTest {
public static void main(String[] args) {
ObservableList<Item> baseList = FXCollections.observableArrayList(item ->
new Observable[] {item.filteredProperty()});
FilteredList<Item> list = new FilteredList<>(baseList, t -> ! t.isFiltered());
list.addListener((Change<? extends Item> c) -> {
while (c.next()) {
if (c.wasAdded()) {
System.out.println(c.getAddedSubList()+ " added to filtered list");
}
if (c.wasRemoved()) {
System.out.println(c.getRemoved()+ " removed from filtered list");
}
}
});
System.out.println("Adding ten items to base list:\n");
IntStream.rangeClosed(1, 10).mapToObj(i -> new Item("Item "+i)).forEach(baseList::add);
System.out.println("\nFiltered list now:\n"+list);
System.out.println("\nSetting filtered flag for alternate items in base list:\n");
IntStream.range(0, 5).map(i -> 2*i).mapToObj(baseList::get).forEach(i -> i.setFiltered(true));
System.out.println("\nFiltered list now:\n"+list);
}
public static class Item {
private final StringProperty name = new SimpleStringProperty() ;
private final BooleanProperty filtered = new SimpleBooleanProperty() ;
public Item(String name) {
setName(name);
}
public final StringProperty nameProperty() {
return this.name;
}
public final String getName() {
return this.nameProperty().get();
}
public final void setName(final String name) {
this.nameProperty().set(name);
}
public final BooleanProperty filteredProperty() {
return this.filtered;
}
public final boolean isFiltered() {
return this.filteredProperty().get();
}
public final void setFiltered(final boolean filtered) {
this.filteredProperty().set(filtered);
}
@Override
public String toString() {
return getName();
}
}
}
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