Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaFX FilteredList, filtering based on property of items in the list

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.

like image 966
Bjarne Boström Avatar asked Jan 24 '26 22:01

Bjarne Boström


1 Answers

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();
        }
    }
}
like image 151
James_D Avatar answered Jan 26 '26 15:01

James_D



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!