Peter Penzov
Peter Penzov

Reputation: 1658

Apply table filter

I have this example of Java table which generates values every second.

Short example:

MainApp.java

public class MainApp extends Application
{    
    private TableView<Employee> table;
    private TextField txtField;
    private ObservableList<Employee> data;

    MyService myService;

    @Override
    public void start(Stage stage) throws Exception
    {
        Label lbl = new Label("Enter text below to filter: ");
        initFilter();
        initTable();

        myService = new MyService();

        myService.setDelay(new Duration(300));
        myService.setPeriod(new Duration(1000));

        myService.start();

        VBox container = new VBox();
        container.getChildren().addAll(lbl, txtField, table);
        StackPane root = new StackPane();
        root.getChildren().add(container);
        Scene scene = new Scene(root, 500, 400);
        stage.setScene(scene);
        stage.show();
    }

    class MyService extends ScheduledService<Void>
    {    
        @Override
        protected Task<Void> createTask()
        {
            return new Task<Void>()
            {
                @Override
                protected Void call() throws Exception
                {
                    data = getTableData();
                    table.setItems(FXCollections.observableArrayList(data));
                    return null;
                }
            };
        }
    }

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

    private void initTable()
    {
        table = new TableView<>();
        table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        TableColumn<Employee, String> empIdCol = new TableColumn<>("Employee ID");
        empIdCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Employee, String>, ObservableValue<String>>()
        {

            @Override
            public ObservableValue<String> call(TableColumn.CellDataFeatures<Employee, String> p)
            {
                return p.getValue().empIdProperty();
            }
        });

        TableColumn<Employee, String> nameCol = new TableColumn<>("Name");
        nameCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Employee, String>, ObservableValue<String>>()
        {

            @Override
            public ObservableValue<String> call(TableColumn.CellDataFeatures<Employee, String> p)
            {
                return p.getValue().nameProperty();
            }
        });

        TableColumn<Employee, Number> ageCol = new TableColumn<>("Age");
        ageCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Employee, Number>, ObservableValue<Number>>()
        {

            @Override
            public ObservableValue<Number> call(TableColumn.CellDataFeatures<Employee, Number> p)
            {
                return p.getValue().ageProperty();
            }
        });

        TableColumn<Employee, String> cityCol = new TableColumn<>("City");
        cityCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Employee, String>, ObservableValue<String>>()
        {

            @Override
            public ObservableValue<String> call(TableColumn.CellDataFeatures<Employee, String> p)
            {
                return p.getValue().cityProperty();
            }
        });

        table.getColumns().setAll(empIdCol, nameCol, ageCol, cityCol);

    }

    private void initFilter()
    {
        txtField = new TextField();
        txtField.setPromptText("Filter");
        txtField.textProperty().addListener(new InvalidationListener()
        {
            @Override
            public void invalidated(Observable o)
            {
                if (txtField.textProperty().get().isEmpty())
                {
                    table.setItems(data);
                    return;
                }
                ObservableList<Employee> tableItems = FXCollections.observableArrayList();
                ObservableList<TableColumn<Employee, ?>> cols = table.getColumns();
                for (int i = 0; i < data.size(); i++)
                {

                    for (int j = 0; j < cols.size(); j++)
                    {
                        TableColumn col = cols.get(j);
                        String cellValue = col.getCellData(data.get(i)).toString();
                        cellValue = cellValue.toLowerCase();
                        if (cellValue.contains(txtField.textProperty().get().toLowerCase()))
                        {
                            tableItems.add(data.get(i));
                            break;
                        }
                    }

                }
                table.setItems(tableItems);
            }
        });
    }

    private ObservableList<Employee> getTableData()
    {
        ObservableList<Employee> list = FXCollections.observableArrayList();
        String[] name =
        {
            "Sriram", "Pete", "Eric", "Dawson", "John"
        };
        String[] city =
        {
            "New York", "Chicago", "Little Rock", "Los Angeles", "Oakland"
        };
        for (int i = 0; i < 5; i++)
        {
            Employee emp = new Employee();
            emp.setName(name[i]);
            emp.setAge((int) (Math.random() * 100));
            emp.setCity(city[i]);
            emp.setEmpId(String.valueOf(i + 1000));
            list.add(emp);
        }
        return list;
    }

}

Employee.java

 public class Employee {

        private SimpleStringProperty name = new SimpleStringProperty();
        private SimpleIntegerProperty age = new SimpleIntegerProperty();
        private SimpleStringProperty city = new SimpleStringProperty();
        private SimpleStringProperty empId = new SimpleStringProperty();

        public SimpleStringProperty nameProperty() {
            return name;
        }

        public void setName(String name) {
            this.name.set(name);
        }

        public String getName() {
            return name.get();
        }

        public SimpleIntegerProperty ageProperty() {
            return age;
        }

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

        p

ublic Integer getAge() {
        return age.get();
    }

    public SimpleStringProperty cityProperty() {
        return city;
    }

    public String getCity() {
        return city.get();
    }

    public void setCity(String city) {
        this.city.set(city);
    }

    public SimpleStringProperty empIdProperty() {
        return empId;
    }

    public void setEmpId(String empId) {
        this.empId.set(empId);
    }

    public String getEmpId() {
        return empId.get();
    }
    }

I noticed that the filter that I use to filter the content is applied only for the current Service run. For next run the filter is not applied.

Upvotes: 0

Views: 741

Answers (1)

James_D
James_D

Reputation: 209245

Use a FilteredList to manage the filtering. Instead of updating the list directly, replace the contents of its source list from the service. When the text in the text field changes, just update the predicate for the filtered list.

As an aside, your code updates the TableView from a background thread, which violates the threading rules of JavaFX. This is fixed in the example below.

SSCCE:

import java.util.ArrayList;
import java.util.List;

import javafx.application.Application;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;

public class FilteredTableViewExample extends Application {
    private TableView<Employee> table;
    private TextField txtField;
    private FilteredList<Employee> tableData;
    private ObservableList<Employee> data;

    MyService myService;

    @Override
    public void start(Stage stage) throws Exception {
        Label lbl = new Label("Enter text below to filter: ");
        initFilter();
        initTable();

        myService = new MyService();

        myService.setDelay(new Duration(300));
        myService.setPeriod(new Duration(1000));

        myService.start();

        VBox container = new VBox();
        container.getChildren().addAll(lbl, txtField, table);
        StackPane root = new StackPane();
        root.getChildren().add(container);
        Scene scene = new Scene(root, 500, 400);
        stage.setScene(scene);
        stage.show();
    }

    class MyService extends ScheduledService<List<Employee>> {
        @Override
        protected Task<List<Employee>> createTask() {
            Task<List<Employee>> task = new Task<List<Employee>>() {
                @Override
                protected List<Employee> call() throws Exception {
                    return getTableData();
                }
            };
            task.setOnSucceeded(e -> data.setAll(task.getValue()));
            return task ;
        }
    }

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

    private void initTable() {
        table = new TableView<>();
        table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        TableColumn<Employee, String> empIdCol = new TableColumn<>("Employee ID");
        empIdCol.setCellValueFactory(p -> p.getValue().empIdProperty());

        TableColumn<Employee, String> nameCol = new TableColumn<>("Name");
        nameCol.setCellValueFactory(p -> p.getValue().nameProperty());

        TableColumn<Employee, Number> ageCol = new TableColumn<>("Age");
        ageCol.setCellValueFactory(p -> p.getValue().ageProperty());

        TableColumn<Employee, String> cityCol = new TableColumn<>("City");
        cityCol.setCellValueFactory(p -> p.getValue().cityProperty());

        table.getColumns().setAll(empIdCol, nameCol, ageCol, cityCol);

        data = FXCollections.observableArrayList();
        tableData = new FilteredList<>(data);
        table.setItems(tableData);
    }

    private void initFilter() {
        txtField = new TextField();
        txtField.setPromptText("Filter");
        txtField.textProperty().addListener((obs, oldText, newText) -> {
            if (txtField.textProperty().get().isEmpty()) {
                tableData.setPredicate(employee -> true);
                return;
            }

            tableData.setPredicate(employee -> {
                String text = newText.toLowerCase();
                for (TableColumn<Employee, ?> col : table.getColumns()) {
                    String cellValue = col.getCellData(employee).toString();
                    cellValue = cellValue.toLowerCase();
                    if (cellValue.contains(text)) {
                        return true;
                    }
                }
                return false;
            });

        });
    }

    private List<Employee> getTableData() {
        List<Employee> list = new ArrayList<>();
        String[] name = { "Sriram", "Pete", "Eric", "Dawson", "John" };
        String[] city = { "New York", "Chicago", "Little Rock", "Los Angeles", "Oakland" };
        for (int i = 0; i < 5; i++) {
            Employee emp = new Employee();
            emp.setName(name[i]);
            emp.setAge((int) (Math.random() * 100));
            emp.setCity(city[i]);
            emp.setEmpId(String.valueOf(i + 1000));
            list.add(emp);
        }
        return list;
    }

    public static class Employee {

        private SimpleStringProperty name = new SimpleStringProperty();
        private SimpleIntegerProperty age = new SimpleIntegerProperty();
        private SimpleStringProperty city = new SimpleStringProperty();
        private SimpleStringProperty empId = new SimpleStringProperty();

        public SimpleStringProperty nameProperty() {
            return name;
        }

        public void setName(String name) {
            this.name.set(name);
        }

        public String getName() {
            return name.get();
        }

        public SimpleIntegerProperty ageProperty() {
            return age;
        }

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

        public Integer getAge() {
            return age.get();
        }

        public SimpleStringProperty cityProperty() {
            return city;
        }

        public String getCity() {
            return city.get();
        }

        public void setCity(String city) {
            this.city.set(city);
        }

        public SimpleStringProperty empIdProperty() {
            return empId;
        }

        public void setEmpId(String empId) {
            this.empId.set(empId);
        }

        public String getEmpId() {
            return empId.get();
        }
    }
}

Upvotes: 1

Related Questions