在 TableView (JavaFX) 中显示 ArrayList<List<String>> 的值

Showing the values of an ArrayList<List<String>> in a TableView (JavaFX)

我正在尝试从由带字符串值的双嵌套 ArrayList 组成的数据集创建 TableView。不幸的是,我无法在 TableView 中显示所有值。行没有根据数据集编号。

互联网上的大多数解决方案都表明,我应该使用特定的对象实例。但是,我不那样工作。相反,我使用了一个大小不定的字符串矩阵。数据结构如下:

List<List<String>> values = new ArrayList<List<String>>();

int rows = 10;
int cols = 4;
             
for(int r=0; r<rows; r++) {
    List<String> line = new ArrayList<String>();
                     
        for(int c=0; c<cols; c++)
            line.add("r: "+r+", c: "+c);
                     
    values.add(line);
}

我最好的(但错误的)结果是使用以下代码实现的:

import java.util.ArrayList;
import java.util.List;

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;

public class TestApplication extends Application {

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

    @Override
    public void start(Stage stage) throws Exception {

        // create values
        List<List<String>> values = new ArrayList<List<String>>();

        int rows = 10;
        int cols = 4;

        for (int r = 0; r < rows; r++) {
            List<String> line = new ArrayList<String>();

            for (int c = 0; c < cols; c++)
                line.add("r: " + r + ", c: " + c);

            values.add(line);
        }

        // show values in table
        TableView<List<StringProperty>> tableView = new TableView<List<StringProperty>>();
        ObservableList<List<StringProperty>> data = FXCollections.observableArrayList();

        for (int c = 0; c < cols; c++) {
            TableColumn<List<StringProperty>, String> col = new TableColumn<>("Col: " + c);
            tableView.getColumns().add(col);
        }

        for (int r = 0; r < values.size(); r++) {
            List<StringProperty> row = new ArrayList<StringProperty>();

            for (int c = 0; c < values.get(r).size(); c++) {
                TableColumn<List<StringProperty>, String> col = (TableColumn<List<StringProperty>, String>) tableView.getColumns().get(c);
                String val = values.get(r).get(c);

                col.setCellValueFactory(cl -> new SimpleStringProperty(new String(val)));
            }

            data.add(row);
        }

        tableView.setItems(data);

        stage.setScene(new Scene(tableView, 300, 500));
        stage.show();
    }
}

结果如下:

但是,table 不显示编号的行。我已经尝试了很多东西。但是我没有得到满意的结果。我的错误是什么?

这似乎是这样工作的...我一直在指导 this.

@Override
public void start(Stage stage) throws Exception {

    // create values
    List<List<String>> values = new ArrayList<List<String>>();

    int rows = 10;
    int cols = 4;

    for (int r = 0; r < rows; r++) {
        List<String> line = new ArrayList<String>();

        for (int c = 0; c < cols; c++)
            line.add("r: " + r + ", c: " + c);

        values.add(line);
    }

    
    List<String> colHeads = new ArrayList<String>();
    for (int c = 0; c < cols; c++) {
        String ch = "Col: " + c; 
        colHeads.add(ch);
    }
    
    
    // show values in table 
    List<TableColumn<Map<String, String>, String>> tcs = new ArrayList<TableColumn<Map<String, String>, String>>();
    for (int c = 0; c < cols; c++) {
        String ch =  colHeads.get(c);
        
        TableColumn<Map<String, String>, String> col = new TableColumn<>(ch);
        col.setCellValueFactory(new MapValueFactory(ch)); 
        
        tcs.add(col); 
    }
    
    TableView<Map<String, String>> tableView = new TableView<>(generateDataInMap(colHeads, values));
    tableView.getColumns().setAll(tcs);
     

    stage.setScene(new Scene(tableView, 300, 500));
    stage.show();

}

private ObservableList<Map<String, String>> generateDataInMap(List<String> colHeads, List<List<String>> values ) {

    ObservableList<Map<String, String>> allData = FXCollections.observableArrayList();
    
    for(int r=0; r<values.size(); r++) {
        Map<String, String> dataRow = new HashMap<String, String>();
        
        for(int c=0; c<values.get(r).size(); c++) {
            String val = values.get(r).get(c);
            dataRow.put(colHeads.get(c), val);
        }
        allData.add(dataRow);
    }
    return allData;
}

给定列的 cellValueFactory 是一个函数,它采用包含行数据的包装器(即 List<StringProperty>)并生成 ObservableValue(例如 Property) 其值将显示在该行和列中。

在您的代码中,您每次添加一行时都会更改 cellValueFactory,因此生成的 cellValueFactory 只是您添加的最后一行中的那个,您只能看到来自最后一行。

您应该只为每列设置一次 cellValueFactory,它应该是一个将行数据映射到该列的特定值的函数。

例如,以下将为您提供您所需要的:

@Override
public void start(Stage stage) throws Exception {

    // create values
    List<List<String>> values = new ArrayList<List<String>>();

    int rows = 10;
    int cols = 4;

    for (int r = 0; r < rows; r++) {
        List<String> line = new ArrayList<String>();

        for (int c = 0; c < cols; c++)
            line.add("r: " + r + ", c: " + c);

        values.add(line);
    }

    // show values in table
    TableView<List<StringProperty>> tableView = new TableView<>();
    ObservableList<List<StringProperty>> data = FXCollections.observableArrayList();

    for (int c = 0; c < cols; c++) {
        TableColumn<List<StringProperty>, String> col = new TableColumn<>("Col: " + c);
        
        final int colIndex = c ;
        col.setCellValueFactory(cd -> cd.getValue().get(colIndex));
        
        tableView.getColumns().add(col);
    }

    for (int r = 0; r < values.size(); r++) {
        List<StringProperty> row = new ArrayList<StringProperty>();

        for (int c = 0; c < values.get(r).size(); c++) {
            row.add(new SimpleStringProperty(values.get(r).get(c)));
        }

        data.add(row);
    }

    tableView.setItems(data);

    stage.setScene(new Scene(tableView, 300, 500));
    stage.show();
}

如果数据不会改变,您可能更愿意使用比 StringProperty 更轻的东西,尽管这种小的性能节省的好处值得商榷:

@Override
public void start(Stage stage) throws Exception {

    // create values
    List<List<String>> values = new ArrayList<List<String>>();

    int rows = 10;
    int cols = 4;

    for (int r = 0; r < rows; r++) {
        List<String> line = new ArrayList<String>();

        for (int c = 0; c < cols; c++)
            line.add("r: " + r + ", c: " + c);

        values.add(line);
    }

    // show values in table
    TableView<List<String>> tableView = new TableView<>();

    for (int c = 0; c < cols; c++) {
        TableColumn<List<String>, String> col = new TableColumn<>("Col: " + c);
        
        final int colIndex = c ;
        col.setCellValueFactory(cd -> new ObservableValue<String>() {

            // If data are immutable, there's nothing for a listener to do, so we ignore them:
            @Override
            public void addListener(InvalidationListener listener) {}
            @Override
            public void removeListener(InvalidationListener listener) {}
            @Override
            public void addListener(ChangeListener<? super String> listener) {}
            @Override
            public void removeListener(ChangeListener<? super String> listener) {}

            @Override
            public String getValue() {
                return cd.getValue().get(colIndex);
            }
            
        });
        
        tableView.getColumns().add(col);
    }

    

    tableView.setItems(FXCollections.observableList(values));

    stage.setScene(new Scene(tableView, 300, 500));
    stage.show();
}