0

I'm currently trying to filter a TableView using CheckComboBox. For reference I'm using the source code from here. The problem is that the value (or property) of the CheckComboBox is an ArrayList unlike the normal ComboBox, so I can't use bindings + valueProperty()etc. to update my TableView when something is selected or changed.

What I want to achieve is if I select one (or more) gender from genderFilterCheckCombo then the TableView will show all peoples with first gender (or both) only. If after that I type "Jacob" on nameFilter then the TableViewwill now show all peoples with name Jacob AND have gender Male or both Male and Female. If you have a better way to achieve this, please let me know.

Here is the code and you need a JAR file of ControlsFX from here.

MainApp.java

import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.transformation.FilteredList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import org.controlsfx.control.CheckComboBox;


public class MainApp extends Application {

    @Override
    public void start(Stage primaryStage) {
        TableView<Person> table = new TableView<>();
        table.getColumns().add(column("Name", Person::nameProperty));
        table.getColumns().add(column("Email", Person::emailProperty));
        table.getColumns().add(column("Gender", Person::genderProperty));
        table.getColumns().add(column("Birthday",Person::birthdayProperty));

        ComboBox<Person.Gender> genderFilterCombo = new ComboBox<>();
        genderFilterCombo.getItems().addAll(Person.Gender.values());

        CheckComboBox<Person.Gender> genderFilterCheckCombo= new CheckComboBox<>();
        genderFilterCheckCombo.getItems().addAll(Person.Gender.values());

        TextField nameFilterField = new TextField();
        DatePicker datefromFilterField = new DatePicker();
        DatePicker dateuntilFilterField = new DatePicker();

        ObjectProperty<Predicate<Person>> nameFilter = new SimpleObjectProperty<>();
        ObjectProperty<Predicate<Person>> genderFilter = new SimpleObjectProperty<>();
        ObjectProperty<Predicate<Person>> gender2Filter = new SimpleObjectProperty<>();
        ObjectProperty<Predicate<Person>> datefromFilter = new SimpleObjectProperty<>();
        ObjectProperty<Predicate<Person>> dateuntilFilter = new SimpleObjectProperty<>();
//      ArrayList arr = java.util.Arrays.asList(Person.Gender.values());

        nameFilter.bind(Bindings.createObjectBinding(() ->
                        person -> person.getName().toLowerCase().contains(nameFilterField.getText().toLowerCase()),
                nameFilterField.textProperty()));


        genderFilter.bind(Bindings.createObjectBinding(() ->
                        person -> genderFilterCombo.getValue() == null || genderFilterCombo.getValue() == person.getGender(),
                genderFilterCombo.valueProperty()));

        ////////////HERE//////////////
        gender2Filter.bind(Bindings.createObjectBinding(() ->
                        person -> genderFilterCheckCombo.getCheckModel().getCheckedItems().isEmpty() ||
                                genderFilterCheckCombo.getCheckModel().getCheckedItems().contains(Person.Gender.MALE)||
                                genderFilterCheckCombo.getCheckModel().getCheckedItems().contains(Person.Gender.FEMALE),
                genderFilterCheckCombo.checkModelProperty()));


        datefromFilter.bind(Bindings.createObjectBinding(() ->
                        person -> datefromFilterField.getValue() == null || datefromFilterField.getValue().isBefore(person.getBirthday())
                                || datefromFilterField.getValue().isEqual(person.getBirthday()),
                datefromFilterField.valueProperty()));

        dateuntilFilter.bind(Bindings.createObjectBinding(() ->
                        person -> dateuntilFilterField.getValue() == null || dateuntilFilterField.getValue().isAfter(person.getBirthday())
                                || dateuntilFilterField.getValue().isEqual(person.getBirthday()),
                dateuntilFilterField.valueProperty()));

        FilteredList<Person> filteredItems = new FilteredList<>(FXCollections.observableList(createData()));
        table.setItems(filteredItems);

        filteredItems.predicateProperty().bind(Bindings.createObjectBinding(
                () -> nameFilter.get().and(genderFilter.get().and(gender2Filter.get().and(datefromFilter.get().and(dateuntilFilter.get())))),
                nameFilter, genderFilter, gender2Filter, datefromFilter, dateuntilFilter));

        Button clear = new Button("Clear Filters");
        clear.setOnAction(e -> {
            genderFilterCombo.setValue(null);
 //         genderFilterCheckCombo.getCheckModel().clearChecks();
            System.out.println(genderFilterCombo.valueProperty());
            System.out.println(genderFilterCheckCombo.checkModelProperty());
            System.out.println(genderFilterCheckCombo.getCheckModel().getCheckedItems());
            System.out.println(genderFilterCheckCombo.getCheckModel().getCheckedItems().isEmpty());
            System.out.println(genderFilterCheckCombo.getCheckModel().getCheckedItems().contains(Person.Gender.MALE) ||
                    genderFilterCheckCombo.getCheckModel().getCheckedItems().contains(Person.Gender.FEMALE) );
            nameFilterField.clear();
            datefromFilterField.setValue(null);
            dateuntilFilterField.setValue(null);
        });

        HBox filters = new HBox(5, nameFilterField, genderFilterCombo, datefromFilterField, dateuntilFilterField, genderFilterCheckCombo, clear);
        filters.setPadding(new Insets(5));
        BorderPane root = new BorderPane(table, filters, null, null, null);
        Scene scene = new Scene(root, 800, 600);


        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private List<Person> createData() {
        return Arrays.asList(
                new Person("Jacob Smith", "jacob.smith@example.com", Person.Gender.MALE, LocalDate.parse("2018-08-25")),
                new Person("Jacob Smith", "jacob.smith@example.com", Person.Gender.MALE, LocalDate.parse("2018-08-26")),
                new Person("Jacob Smith", "jacob.smith@example.com", Person.Gender.FEMALE, LocalDate.parse("2018-08-26")),
                new Person("Jacob Smith", "jacob.smith@example.com", Person.Gender.MALE, LocalDate.parse("2018-08-25")),
                new Person("Isabella Johnson", "isabella.johnson@example.com", Person.Gender.FEMALE, LocalDate.parse("2018-08-21")),
                new Person("Ethan Williams", "ethan.williams@example.com", Person.Gender.MALE, LocalDate.parse("2018-08-21")),
                new Person("Emma Jones", "emma.jones@example.com", Person.Gender.FEMALE, LocalDate.parse("2018-08-29")),
                new Person("Michael Brown", "michael.brown@example.com", Person.Gender.MALE, LocalDate.parse("2018-08-05"))
        );
    }

    private static <S,T> TableColumn<S,T> column(String title, Function<S, ObservableValue<T>> property) {
        TableColumn<S,T> col = new TableColumn<>(title);
        col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
        return col ;
    }



    public static class Person {
 //     List<Gender> abc = Arrays.asList(Person.Gender.values());
        public enum Gender {MALE, FEMALE }

        private final StringProperty name = new SimpleStringProperty();
        private final StringProperty email = new SimpleStringProperty() ;
        private final ObjectProperty<Gender> gender = new SimpleObjectProperty<>();
        private final ObjectProperty<LocalDate> birthday = new SimpleObjectProperty<>();




        public Person(String name, String email, Gender gender, LocalDate birthday) {
            setName(name);
            setEmail(email);
            setGender(gender);
            setBirthday(birthday);
        }

        public final ObjectProperty<LocalDate> birthdayProperty(){
            return this.birthday;
        }

        public final LocalDate getBirthday() {
            return this.birthdayProperty().get();
        }

        public final void setBirthday(final LocalDate birthday) {
            this.birthdayProperty().set(birthday);
        }

        public final StringProperty emailProperty() {
            return this.email;
        }

        public final String getEmail() {
            return this.emailProperty().get();
        }

        public final void setEmail(final String email) {
            this.emailProperty().set(email);
        }

        public final ObjectProperty<Gender> genderProperty() {
            return this.gender;
        }

        public final Gender getGender() {
            return this.genderProperty().get();
        }

        public final void setGender(final Gender gender) {
            this.genderProperty().set(gender);
        }

        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 static void main(String[] args) {
        launch(args);
    }
}
fabian
  • 80,457
  • 12
  • 86
  • 114
Johnny212
  • 11
  • 8

1 Answers1

1

Use CheckModel's checkedItems for this purpose

gender2Filter.bind(Bindings.createObjectBinding(() -> genderFilterCheckCombo.getCheckModel().getCheckedItems().isEmpty()
                        ? null
                        : person -> genderFilterCheckCombo.getCheckModel().getCheckedItems().contains(person.getGender()),
        genderFilterCheckCombo.getCheckModel().getCheckedItems()));

Edit

The above binding would worked for the predicate property of a FilteredList. Since you create that binding in a different way, a non-null object needs to be returned from the binding:

gender2Filter.bind(Bindings.createObjectBinding(() -> genderFilterCheckCombo.getCheckModel().getCheckedItems().isEmpty()
                        ? person -> true
                        : person -> genderFilterCheckCombo.getCheckModel().getCheckedItems().contains(person.getGender()),
        genderFilterCheckCombo.getCheckModel().getCheckedItems()));
fabian
  • 80,457
  • 12
  • 86
  • 114
  • Your code is working just like what I wanted, but why there is a `WARNING: Exception while evaluating binding java.lang.NullPointerException at MainApp.lambda$start$10(MainApp.java:80)` etc. when I run it? Could you also provide a solution? – Johnny212 Sep 08 '18 at 13:16
  • @Johnny212 sorry, thought a `FilteredList.predicate` would be bound to `gender2Filter` directly. Edited the answer. – fabian Sep 08 '18 at 15:38