JavaFX - Как динамически связывать TableView, когда Model содержит список - PullRequest
0 голосов
/ 04 декабря 2018

Предположим, у нас есть следующий сценарий:

TableView<Person> table = new TableView<>();

TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setCellValueFactory(new PropertyValueFactory<>("firstName"));

TableColumn lastNameCol = new TableColumn("Last Name");
lastNameCol.setCellValueFactory(new PropertyValueFactory<>("lastName"));

table.getColumns().addAll(firstNameCol,lastNameCol);

ObservableList<Person> data = // get data
table.getItems().addAll()

И модель, которая выглядит следующим образом;

class Person{

    private String firstName; // or SimpleStringProperty
    private String lastName;
    private List<Integer> favouriteNumbers; // or ListProperty etc

    public String getFirstName(){
          return firstName;
    }

    public String getLastName(){
          return lastName;
    }

    public List<Integer> getFavouriteNumbers(){
          return favouriteNumbers;
    }
}

Человек может иметь много любимых чисел, определенных во время выполнения.Мне нужно динамически создать такую ​​таблицу:

Имя |Фамилия |Фаворит № 1 |Фаворит № 2 |Любимый номер 3 |и т.д ..

Я могу динамически создавать и добавлять столбцы, перебирая список getFavouriteNumbers().Таким образом, количество столбцов, которые будут созданы, будет размером самого большого списка.У строк с меньшими списками будут лишние ячейки.

Проблема в том, что я не могу понять, как применить привязку для этих столбцов.

Есть идеи?Большое спасибо.

1 Ответ

0 голосов
/ 07 декабря 2018

Вам нужно разделить ваши вопросы по шагам:

  1. Получить список людей для отображения в таблице
  2. Получить максимальное количество избранных номеров для отображения
  3. Создание необходимых столбцов и установка фабрики значений пользовательских ячеек
  4. Создание просмотра таблицы

Пошаговый код:

Шаг 1: Получить список людейдля отображения

Я использовал более простую версию класса Person, чтобы сосредоточиться на вашей проблеме.

class Person {

    private final List<Integer> numbers;

    public Person( Integer... numbers){
        this.numbers = Arrays.asList(numbers);
    }

    public List<Integer> getNumbers(){
        return numbers;
    }
}

И мы можем написать эти методы в нашем приложении:

private ObservableList<Person> createItems() {
    ObservableList items = FXCollections.observableArrayList();
    items.add(new Person(0,1,2,3,4));
    items.add(new Person(2,3));
    return items;
}

Это просто какая-то шутка.Вы непременно получите доступ к своей модели или постоянному слою, чтобы получить список людей.

Шаг 2: Получить номер столбца для отображения

private Integer getNumberOfFavoriteColumn(ObservableList<Person> persons){
    int maxNumberOfFavorite = 0;
    for(Person person : persons){
        maxNumberOfFavorite = Math.max(maxNumberOfFavorite, person.getNumbers().size());
    }
    return maxNumberOfFavorite;     
}

Довольно просто здесь.Мы зациклим и получим максимальный размер списка избранных номеров.

Шаг 3: Создайте необходимые столбцы и установите фабрику пользовательских значений ячеек

Нам нужно реализовать наш собственный обратный вызов для фабрики значений ячеек,Один из способов сделать это - создать класс, который реализует функцию обратного вызова (обратите внимание, что функция обратного вызова является функциональным интерфейсом, поэтому вы можете использовать лямбду, если хотите, или даже анонимный класс, или ..).

public class PersonCallback implements Callback<TableColumn.CellDataFeatures<Person, String>, ObservableValue<String>> {

    private Function<Person,Object> extractor;

    public PersonCallback(Function<Person,Object> extractorFunction) {
        extractor = extractorFunction;
    }

    @Override
    public ObservableValue<String> call(TableColumn.CellDataFeatures<Person, String> cellData) {
        return new SimpleObjectProperty(extractor.apply(cellData.getValue()));
    }
}

Итогда мы можем создать столбцы:

   private List<TableColumn> createColumns(ObservableList<Person> persons) {
        List<TableColumn> columns = new ArrayList();

        for(int i = 0; i < getNumberOfFavoriteColumn(persons); i++) {
            final int index = i;
            TableColumn column = new TableColumn(String.format("Favorite Number %d", i + 1));
            column.setCellValueFactory(new PersonCallback((person) -> { return person.getNumbers().size() > index ? person.getNumbers().get(index) : "";}));
            columns.add(column);
        }

        return columns;
    }

Я хочу вернуть пустую строку, если у человека нет этого номера, но вы можете адаптироваться к вашим потребностям.

Шаг 4: Создать таблицуview

Теперь у нас есть все необходимое для создания табличного представления:

private TableView<Person> createTableView(){
    ObservableList<Person> persons = createItems();
    List<TableColumn> columns = createColumns(persons);

    TableView<Person> tableView = new TableView();
    tableView.getColumns().addAll(columns.toArray(new TableColumn[0]));
    tableView.setItems(persons);
    return tableView;
}

Пример полного кода

Если собрать все вместе, мы получим:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;


public class JavaFXApplication extends Application {

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

    @Override
    public void start(Stage primaryStage) {
        StackPane root = new StackPane();
        root.getChildren().add(createTableView());
        Scene scene = new Scene(root, 300, 250);
        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private TableView<Person> createTableView(){
        ObservableList<Person> persons = createItems();
        List<TableColumn> columns = createColumns(persons);

        TableView<Person> tableView = new TableView();
        tableView.getColumns().addAll(columns.toArray(new TableColumn[0]));
        tableView.setItems(persons);
        return tableView;
    }

    private List<TableColumn> createColumns(ObservableList<Person> persons) {
        List<TableColumn> columns = new ArrayList();

        for(int i = 0; i < getNumberOfFavoriteColumn(persons); i++) {
            final int index = i;
            TableColumn column = new TableColumn(String.format("Favorite Number %d", i + 1));
            column.setCellValueFactory(new PersonCallback((person) -> { return person.getNumbers().size() > index ? person.getNumbers().get(index) : "";}));
            columns.add(column);
        }

        return columns;
    }


    private ObservableList<Person> createItems() {
        ObservableList items = FXCollections.observableArrayList();
        items.add(new Person(0,1,2,3,4));
        items.add(new Person(2,3));
        return items;
    }

    private Integer getNumberOfFavoriteColumn(ObservableList<Person> persons){
        int maxNumberOfFavorite = 0;
        for(Person person : persons){
            maxNumberOfFavorite = Math.max(maxNumberOfFavorite, person.getNumbers().size());
        }
        return maxNumberOfFavorite;     
    }

    private class PersonCallback implements Callback<TableColumn.CellDataFeatures<Person, String>, ObservableValue<String>> {

        private Function<Person,Object> extractor;

        public PersonCallback(Function<Person,Object> extractorFunction) {
            extractor = extractorFunction;
        }

        @Override
        public ObservableValue<String> call(TableColumn.CellDataFeatures<Person, String> cellData) {
            return new SimpleObjectProperty(extractor.apply(cellData.getValue()));
        }
    }

    private class Person {

        private final List<Integer> numbers;

        public Person( Integer... numbers){
            this.numbers = Arrays.asList(numbers);
        }

        public List<Integer> getNumbers(){
            return numbers;
        }
    }
}

Это решение, безусловно, может быть улучшено до совершенства, но я думаю, что оно поможет вам понять концепцию и даст вам указание для решения вашей проблемы (и я только начал JavaFX очень скоро)

С уважением, Квентин.

Редактировать: Добавить объяснения и форматирование

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