Как обобщить создание предиката для FilteredList TableView в JavaFX8? - PullRequest
0 голосов
/ 17 декабря 2018

Вопрос от опытного программиста, который все еще относительно новичок в Java.

Я создал прототип класса, который добавляет Excel-подобные фильтры данных к столбцам в TableView.Сейчас я делаю его универсальным, чтобы он работал с любым TableView, и я застрял с тем, как "обобщить" создание Predicate s.

в моем прототипе (где язакодировал все), я вызываю метод 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().В настоящее время я получаю сообщение об ошибке «Не удается найти метод символа содержит (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 голосов
/ 17 декабря 2018

Я не думаю, что текущий дизайн в конечном итоге сработает.

Я собираюсь предположить, что вы можете установить фильтры для нескольких столбцов, и 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).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...