如何在 JavaFX8 中通用化为 TableView 的 FilteredList 创建谓词?
How to genericise the creation of a Predicate for a TableView's FilteredList in JavaFX8?
一位对 Java 还比较陌生的经验丰富的程序员提出的问题。
我创建了 class 的原型,它向 TableView
中的列添加了类似于 Excel 的数据过滤器。我现在使它通用,以便它可以与任何 TableView
一起使用,并且我坚持如何 "genericise" 创建 Predicate
s.
在我的原型中(我对所有内容都进行了硬编码),我为 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()
通用。我目前遇到 "cannot find symbol method 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 class:
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.
的虚拟class
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
应该显示基于 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
)进行过滤更容易。
一位对 Java 还比较陌生的经验丰富的程序员提出的问题。
我创建了 class 的原型,它向 TableView
中的列添加了类似于 Excel 的数据过滤器。我现在使它通用,以便它可以与任何 TableView
一起使用,并且我坚持如何 "genericise" 创建 Predicate
s.
在我的原型中(我对所有内容都进行了硬编码),我为 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()
通用。我目前遇到 "cannot find symbol method 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 class:
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.
的虚拟classimport 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
应该显示基于 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
)进行过滤更容易。