Filtering JFX TableView with multiple values

别来无恙 提交于 2019-12-25 16:08:38

问题


I am currently trying to filter data in my TableView using FilteredList with predicate. I have 2 ComboBoxes to filter the values.

My table contains Result. Each Result has a Student, that Student has a Classroom he belongs in. Those are the values I want to filter them by.

I have no issues when I filter data using only 1 ComboBox. However, I don't know how to filter it based on both at the same time. The only real solution I could come up with would be a lot of if statements in both changeListeners on each ComboBox and that seems like unneccessarily complicated way to do it.

The values in my TableView get displayed multiple times if I use following code.

  private void filterData(){
        ObservableList<Result> observableList = FXCollections.observableList(view.getResultTableView().getAllResultsList());
        FilteredList<Result> filteredData = new FilteredList<>(observableList, p -> true);

        view.getClassroomComboBox().getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {

            filteredData.setPredicate(result -> {

                if (newValue.equals(view.getAllClassroomsChoice()) || newValue.equals(result.getStudent().getClassroom())) {
                    return true;
                }
                else
                    return false;
            });

            if(newValue.equals(view.getAllClassroomsChoice())){
                view.getStudentComboBox().setAllStudents();
            }
            else{
                view.getStudentComboBox().setStudentsByClassroom((Classroom) newValue);
            }
        });

        view.getStudentComboBox().getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            filteredData.setPredicate(result -> {

                if (newValue.equals(view.getAllClassroomsChoice()) || newValue.equals(result.getStudent())) {
                    return true;
                }
                else
                    return false;
            });
        });


        SortedList<Result> sortedData = new SortedList<>(filteredData);

        sortedData.comparatorProperty().bind(view.getResultTableView().comparatorProperty());

        view.getResultTableView().setItems(sortedData);

    }

回答1:


First, don't recreate the data structure every time. Just update the predicate on the FilteredList.

You can create predicates for each combo box and bind each to the value in the combo box. Then you can combine predicates using Predicate.and(...) and bind the predicate property of the filtered list to the result.

Here is a similar example:

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.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class MultipleFilterTableExample 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));

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

        TextField nameFilterField = new TextField();

        ObjectProperty<Predicate<Person>> nameFilter = new SimpleObjectProperty<>();
        ObjectProperty<Predicate<Person>> genderFilter = new SimpleObjectProperty<>();

        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()));

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

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

        Button clear = new Button("Clear Filters");
        clear.setOnAction(e -> {
            genderFilterCombo.setValue(null);
            nameFilterField.clear();
        });

        HBox filters = new HBox(5, nameFilterField, genderFilterCombo, clear);
        filters.setPadding(new Insets(5));
        BorderPane root = new BorderPane(table, filters, null, null, null);
        Scene scene = new Scene(root, 600, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

    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 {

        public enum Gender {MALE, FEMALE }

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

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

        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);
    }
}


来源:https://stackoverflow.com/questions/42138867/filtering-jfx-tableview-with-multiple-values

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!