如何在 JavaFX8 中泛型化 TableView 的 FilteredList 谓词的创建?



一位对Java还比较陌生的经验丰富的程序员提出的问题。

我创建了一个类的原型,该类将类似Excel的数据过滤器添加到TableView中的列中。我现在正在使它通用化,以便它可以与任何TableView一起工作,并一直致力于如何"通用化"Predicates的创建。

在我的原型中(我已经对所有内容进行了硬编码(,我为String"Value"列调用了一个createPredicate()方法,例如:

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的getter,以便将其传递给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()具有通用性。我当前收到一个"找不到包含(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);
}
}

我认为目前的设计最终不会成功。

我假设您可以在多个列上设置筛选器,TableView应该显示基于所有筛选器的结果。

你可以尝试这种方法:

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(进行过滤更容易。

最新更新