JavaFX-当模型包含列表时如何动态绑定TableView

时间:2018-12-04 11:49:33

标签: java javafx

假设我们有以下情况;

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 :(得分:1)

您需要以多个步骤拆分问题:

  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;
}

这只是一种模拟。您当然可以访问您的模型或持久层以获取Person列表。

步骤2:获取要显示的列数

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

这里非常简单。我们循环并检索“最爱号码”列表的最大大小。

步骤3:创建所需的列并设置自定义单元格值工厂

我们需要为单元格值工厂实现我们自己的回调。一种实现方法是创建一个实现Callback的类(请注意,callback是一个功能接口,因此您可以根据需要使用lambda甚至匿名类,也可以使用..)。

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步:创建表格视图

现在,我们拥有创建表视图所需的一切:

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)

关于, 昆汀。

编辑:添加说明和格式