简体   繁体   中英

How to count number of items in TableView that matched with specified value/string

So I have a TableView that have filtering feature and some Labels for displaying the result.

x persons are found, x are VIP/NON VIP, etc.

Now what I'm trying to do is to count number of items that matched with specified value/string in my TableView to update the x on Labels text and display it as result. (the result should be shown and updated automatically when the items in TableView changed, without selecting any rows or columns).

For Label total person I already done it with table.getItems().size() , so there are 3 more to go. My idea is to create a string/int variable inside a for loop and compare it with items in TableView and then wrap it in a listener to make it update the Labels automatically, but I don't know whether it is possible or not.

Here is my code and you need to add controlsfx jar from here . Currently I put all Labels on "clear" button's method to update it manually.

MainApp

import java.time.LocalDate;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
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.getSelectionModel().setCellSelectionEnabled(true);
        table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        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));
        table.getColumns().add(column("Age", Person::ageProperty));
        table.getColumns().add(column("Status", Person::statusProperty));

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

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

        CheckComboBox<Person.Status> statusFilterCheckCombo= new CheckComboBox<>();
        statusFilterCheckCombo.getItems().addAll(Person.Status.values());

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

        Label totalperson = new Label();
        Label totalvip = new Label();
        Label totaloldperson = new Label();
        Label totalyoungperson = new Label();

        nameFilterField.setPromptText("Name");
        agefromFilterField.setPromptText("Age from");
        ageuntilFilterField.setPromptText("Age until");
        datefromFilterField.setPromptText("Birthday from");
        dateuntilFilterField.setPromptText("Birthday until");

        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<>();
        ObjectProperty<Predicate<Person>> agefromFilter = new SimpleObjectProperty<>();
        ObjectProperty<Predicate<Person>> ageuntilFilter = new SimpleObjectProperty<>();
        ObjectProperty<Predicate<Person>> statusFilter = 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()));

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

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

        agefromFilter.bind(Bindings.createObjectBinding(() ->
                        person -> agefromFilterField.getText().isEmpty() || Integer.parseInt(agefromFilterField.getText()) <= person.getAge(),
                agefromFilterField.textProperty()));

        ageuntilFilter.bind(Bindings.createObjectBinding(() ->
                        person -> ageuntilFilterField.getText().isEmpty() || Integer.parseInt(ageuntilFilterField.getText()) >= person.getAge(),
                ageuntilFilterField.textProperty()));

        statusFilter.bind(Bindings.createObjectBinding(() ->
                        statusFilterCheckCombo.getCheckModel().getCheckedItems().isEmpty() ?  person-> true : person->
                                statusFilterCheckCombo.getCheckModel().getCheckedItems().contains(person.getStatus()),
                statusFilterCheckCombo.getCheckModel().getCheckedItems()));

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

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

        Button clear = new Button("Clear Filters");
        clear.setOnAction(e -> {
            /*for (TablePosition<Person, ?> pos : table.getSelectionModel().getSelectedCells()) {
                TableColumn<Person, ?> column = pos.getTableColumn();
                ObservableValue<?> obs = column.getCellObservableValue(pos.getRow());
                Object value = obs.getValue();
                System.out.println(value);
                // process value...
            }*/
            nameFilterField.clear();
            datefromFilterField.setValue(null);
            dateuntilFilterField.setValue(null);

        //Here////////////////////////////////////////////////////////////
        int tp = table.getItems().size();
        totalperson.setText(tp + " " + "persons are found!");
        totalvip.setText("x VIP found!");
        totalyoungperson.setText("x are under or equal 25!");
        totaloldperson.setText("x are over 25!");
        });


        FlowPane filters = new FlowPane( nameFilterField, genderFilterCombo, datefromFilterField, dateuntilFilterField, genderFilterCheckCombo, agefromFilterField,
                ageuntilFilterField, statusFilterCheckCombo, clear);
        HBox labels = new HBox(40, totalperson, totalvip, totalyoungperson, totaloldperson);
        filters.setPadding(new Insets(5));
        labels.setPadding(new Insets(5));
        BorderPane root = new BorderPane(table, filters, null, labels, 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"),25, Person.Status.NONVIP),
                new Person("Jacob Smith", "jacob.smith@example.com", Person.Gender.MALE, LocalDate.parse("2018-08-26"),24, Person.Status.VIP),
                new Person("Jacob Smith", "jacob.smith@example.com", Person.Gender.FEMALE, LocalDate.parse("2018-08-26"),23, Person.Status.VIP),
                new Person("Jacob Smith", "jacob.smith@example.com", Person.Gender.MALE, LocalDate.parse("2018-08-25"),25, Person.Status.NONVIP),
                new Person("Isabella Johnson", "isabella.johnson@example.com", Person.Gender.FEMALE, LocalDate.parse("2018-08-21"),28, Person.Status.VIP),
                new Person("Ethan Williams", "ethan.williams@example.com", Person.Gender.MALE, LocalDate.parse("2018-08-21"),30, Person.Status.VIP),
                new Person("Emma Jones", "emma.jones@example.com", Person.Gender.FEMALE, LocalDate.parse("2018-08-29"),18, Person.Status.NONVIP),
                new Person("Michael Brown", "michael.brown@example.com", Person.Gender.MALE, LocalDate.parse("2018-08-05"),15, Person.Status.NONVIP)
        );
    }

    public 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}
        public enum Status {VIP, NONVIP}

        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<>();
        private final IntegerProperty age = new SimpleIntegerProperty();
        private final ObjectProperty<Status> status = new SimpleObjectProperty<>();

        public Person(String name, String email, Gender gender, LocalDate birthday, Integer age, Status status) {
            setName(name);
            setEmail(email);
            setGender(gender);
            setBirthday(birthday);
            setAge(age);
            setStatus(status);
        }

        public ObjectProperty<Status> statusProperty() { return status; }

        public Status getStatus() { return this.statusProperty().get(); }

        public void setStatus(Status status) { this.statusProperty().set(status); }

        public IntegerProperty ageProperty() { return age; }

        public final int getAge() { return this.ageProperty().get(); }

        public final void setAge(final  Integer age) {this.ageProperty().set(age); }

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

It should be possible to solve your problems using pure JavaFX bindings, using two constructions of the type:

<some-label>.textProperty().bind(Bindings.size(<observable-list>).asString(<some template>));

I tried to implement a minimal example (MCVE) to show the number of both all and filtered items in a table. I did not use your setup, because I considered it too complex for a MCVE. I hope the code is kinda self explanatory, please feel free to ask if something is confusing or if you have problems porting it to your code.

This application has a minimal UI that shows a table with two entries by default and two labels, one at top (showing the amount of all items) and one at the bottom (showing the amount of items passing the filter). Since items that do not pass the filter are not shown, it is not possible to edit them to pass the filter again, so it is necessary to restart the application once you checked the boxes or changed the names (depending on the active filter).

The example is a full JavaFX application, you should be able to run it either directly from command line or with any IDE (eg Eclipse: Run As -> Java Application):

public class FxApp extends Application {

    private ObservableList<PersonController> all = 
        FXCollections.observableArrayList(PersonController::extract);

    private FilteredList<PersonController> filtered = new FilteredList<>(all);

    private TableView<PersonController> table = new TableView<>();

    private TableColumn<PersonController, String> nameColumn = new TableColumn<>("Name");

    private TableColumn<PersonController, Boolean> checkColumn = new TableColumn<>("Check");

    private Label allLabel = new Label(), filteredLabel = new Label();

    @Override
    public void start(Stage stage) throws Exception {
        // Setup items
        all.add(new PersonController("John Doe"));
        all.add(new PersonController("Jane Doe"));
        // Setup UI
        table.setEditable(true);
        table.getColumns().addAll(Arrays.asList(nameColumn, checkColumn));
        nameColumn.setCellFactory(TextFieldTableCell.forTableColumn());
        nameColumn.setCellValueFactory(features -> features.getValue().nameProperty);
        checkColumn.setCellFactory(CheckBoxTableCell.forTableColumn(checkColumn));
        checkColumn.setCellValueFactory(features -> features.getValue().checkProperty);
        allLabel.textProperty().bind(Bindings.size(all).asString("All items: %s"));
        filteredLabel.textProperty().bind(Bindings.size(filtered).asString("Filtered Items: %s"));
        // You may need to add dependencies of your filter here
        filtered.predicateProperty().bind(Bindings.createObjectBinding(this::buildFilter));
        table.setItems(filtered);
        BorderPane pane = new BorderPane();
        pane.setCenter(table);
        pane.setTop(allLabel);
        pane.setBottom(filteredLabel);
        Scene scene = new Scene(pane);
        stage.setScene(scene);
        stage.show();
    }

    private Predicate<PersonController> buildFilter() {
        return person -> person.checkProperty.not().get();
        // OR
        // return person -> person.nameProperty.get().endsWith("Doe");
        // OR
        // some complex filter using other inputs
    }

    private static class PersonController {

        private StringProperty nameProperty = new SimpleStringProperty();

        private BooleanProperty checkProperty = new SimpleBooleanProperty();

        public PersonController(String name) {
            nameProperty.set(name);
        }

        public Observable[] extract() {
            return new Observable[] { nameProperty, checkProperty };
        }

    }

    public static void main(String[] args) {
        launch(args);
    }

}

So finally here is the answer. Thanks @James_D for your post at here

ObservableList<Person> items = table.getItems();

        // initialize counts (only needed once, and only if items is non-empty):
        int vip = 0 ;
        int total = table.getItems().size();
        for (Person item : items) {
            if (Person.Status.VIP.equals(item.getStatus())) vip++ ;
            if (Person.Status.NONVIP.equals(item.getStatus())) nonvip++ ;
        }

        IntegerProperty vipCount = new SimpleIntegerProperty(vip);
        IntegerProperty totalCount = new SimpleIntegerProperty(total);

        totalvip.textProperty().bind(vipCount.asString("VIP: %s"));
        totalperson.textProperty().bind(totalCount.asString("Total persons: %s"));

        items.addListener((
                ListChangeListener.Change<? extends Person> c) -> {
            while (c.next()) {

                if (c.wasAdded()) {
                    for (Person item : c.getAddedSubList()) {
                        totalCount.set(totalCount.get() + 1);
                        if (Person.Status.VIP.equals(item.getStatus())) vipCount.set(vipCount.get() + 1) ;
                    }
                }

                if (c.wasRemoved()) {
                    for (Person item : c.getRemoved()) {
                        totalCount.set(totalCount.get() - 1);
                        item.statusProperty().removeListener(listener);
                        if (Person.Status.VIP.equals(item.getStatus())) vipCount.set(vipCount.get() - 1) ;
                        if(vipCount.get() < 0 ) vipCount.set(0); //prevent negative
                    }
                }

            }
        });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM