如何在JavaFX8中为TableView的FilteredList的谓词创建通用化?

时间:2018-12-17 02:26:25

标签: generics javafx tableview javafx-8

来自经验丰富的程序员的问题,他还是Java的新手。

我创建了一个类的原型,该类将类似于Excel的数据过滤器添加到TableView中的列中。我现在将其设为通用,以便它可以与任何TableView一起使用,并停留在如何“通用化” Predicate的创建中。

在我的原型(已对所有内容进行硬编码)中,例如,我为createPredicate()“值”列调用String方法,如下所示:

createPredicate(TableViewModel::getTvValue);

,然后在createPredicate()方法中,像这样设置Predicate

private void createPredicate(Function<TableViewModel, String> columnGetter) {
    //...
    final String filter = "some value";
    Predicate<TableViewModel> predicate = tvm -> columnGetter.apply(tvm).contains(filter);
    //...
}

如何使对方法的调用和方法本身都具有通用性,以便它们可用于任何TableColumn

我被困在两个地方。首先,如何获取TableColumn的吸气剂,以便将其传递给createPredicate()方法...

//Get a column
TableColumn<S, T> col = (TableColumn<S, T>) table.getColumns().get(0);

//Set a predicate based on the column
//==>STUCK HERE:  How do I get the getter for the column?
createPredicate(S::<WHAT_GOES_HERE?>);

...,其次,如何使contains()通用。我目前遇到“找不到符号方法contains(T)”编译器错误。

private void createPredicate(Function<S, T> columnGetter) {

    final T filter = (T) "some value";
    //STUCK HERE:  How to genericise the contains?
    Predicate<S> predicate = tvm -> columnGetter.apply(tvm).contains(filter);

}

先谢谢了。如果有帮助,这里是MVCE。我正在使用JavaFX8(JDK1.8.0_181)和NetBeans 8.2。

(裸露的骨头!) TestColumnFilter.java 类:

import java.util.function.Function;
import java.util.function.Predicate;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;

public class TestColumnFilter<S, T> {

    public TestColumnFilter(TableView<S> table) {

        //Get a column
        TableColumn<S, T> col = (TableColumn<S, T>) table.getColumns().get(0);

        //Set a predicate based on the column
        //STUCK HERE:  How to get the getter for the column?
        createPredicate(S::<WHAT_GOES_HERE?>);

    }

    private void createPredicate(Function<S, T> columnGetter) {

        final T filter = (T) "some value";
        //STUCK HERE:  How to genericise the contains?  
        //Currently getting a "cannot find symbol method contains(T)" compiler error
        Predicate<S> predicate = tvm -> columnGetter.apply(tvm).contains(filter);

    }

    public static <S, T> TestColumnFilter<S, T> createColumnFiltersForTableView(TableView<S> table) {

        return new TestColumnFilter<>(table);

    }

}

Test55.java ,这是一个使用TestColumnFilter.java的虚拟类。

import java.util.Arrays;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class Test55 extends Application {

    private Parent createContent() {

        TableView<TableViewModel> table = new TableView<>();
        TableColumn<TableViewModel, String> valueCol = new TableColumn<>("Value");
        valueCol.setCellValueFactory(cb -> cb.getValue().tvValueProperty());

        table.getColumns().addAll(Arrays.asList(valueCol));

        TestColumnFilter.createColumnFiltersForTableView(table);

        BorderPane content = new BorderPane(table);

        return content;
    }

    private class TableViewModel {

        private final StringProperty tvValue;

        public TableViewModel(
            String tvValue
        ) {
            this.tvValue = new SimpleStringProperty(tvValue);
        }

        public String getTvValue() {return tvValue.get().trim();}
        public void setTvValue(String tvValue) {this.tvValue.set(tvValue);}
        public StringProperty tvValueProperty() {return tvValue;}

    }

    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new Scene(createContent()));
        stage.setTitle("Test");
        stage.show();
    }

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

}

1 个答案:

答案 0 :(得分:1)

我认为当前的设计最终不会奏效。

我假设您可以在多个列上设置过滤器,并且TableView应该基于 all 过滤器显示结果。

您可以尝试这种方法:

public class TableViewFilter<S> {
    private final TableView<S> tableView;
    private final FilteredList<S> filteredItems;
    private final ObservableList<Function<S, Boolean>> conditions;

    public TableViewFilter(TableView<S> tableView, ObservableList<S> items) {
        this.tableView = tableView;
        this.filteredItems = new FilteredList<>(items);
        this.tableView.setItems(filteredItems ).

        this.conditions = FXCollections.observableArrayList();

        this.filteredItems.predicateProperty().bind(
            Bindings.createObjectBinding(this::generatePredicate, this.conditions));
    }

    public static <S> TableViewFilter<S> forTableView(TableView<S> tableView) {
        return new TableViewFilter<>(tableView);
    }

    public void addCondition(Function<S, Boolean> condition) {
        conditions.add(condition);
    }

    private Predicate<S> generatePredicate() {
        return item -> {
            return conditions.stream().map(func -> func.apply(item)).allMatch(Boolean.TRUE::equals);
        };
    }
}

TableView<TableViewModel> table = new TableView<>();
    TableColumn<TableViewModel, String> valueCol = new TableColumn<>("Value");
    valueCol.setCellValueFactory(cb -> cb.getValue().tvValueProperty());

    table.getColumns().addAll(Arrays.asList(valueCol));

    ObservableList<TableViewModel> items = FXCollections.observableArrayList();
    items.addAll(/*items*/);

    //TestColumnFilter.createColumnFiltersForTableView(table);
    TableViewFilter filter = TableViewFilter.forTableView(table, items); // Keep a reference if you need to add more conditions later
    filter.addCondition(item -> item.getTvValue().contains("filtered string"));

    BorderPane content = new BorderPane(table);

    return content;
}

请注意,条件不取决于特定的TableColumn。使用基础数据(即TableViewModel)进行过滤比较容易。