Populating a tableview with strings

2019-03-21 21:21发布

I have read the API and examples, but am unable to understand how to populate a tableview.

Let us say I have a two column String array (String[][]) with "name, value"-pairs. I now simply want to create a tableview which shows the data in two columns, displaying the name in the first and value in the second column for all the rows in the original array.

What have I tried? Nothing, but it seems that you need to create observablelists, one for each column, bind it to its respective column and then add the column to the tableview. But this involves "factories" in all the examples I have seen which is an alien concept to me.

I'm guessing this is simple, but I'm unable to wrap my head around it. Please help.

标签: java javafx-2
3条回答
迷人小祖宗
2楼-- · 2019-03-21 21:45

Yeah, it was simple you dummy. Create a Row-class:

Row(String fName, String fValue){
    this.fieldName = new SimpleStringProperty(fName);
    this.fieldValue = new SimpleStringProperty(fValue);
}

For each row in the String-array you create a row-object which you add to an observableList.

private ObservableList<Row> observableList = FXCollections.observableArrayList(          
              new Row("The Unfun Cat", "Is a terrible programmer"), 
              new Row("Stack Overflow", "Rules!");

Then you create a tablecolumn for both columns in the array.

TableColumn columnName = new TableColumn("Name");
columnName.setCellValueFactory(
        new PropertyValueFactory<Row,String>("fieldName"));

(identical for Value)

Then you add the observableList to the tableView with tableView.setItems(observableList) and lastly you call the method tableView.getColumns.addAll(fieldName,fieldValue);

(This makes me wonder ho to do it for the general case where you don't know how many columns is in your original String[][] array? Could the row object have an ArrayList to represent arbitrarily many SimpleStringProperties? How would you connect it to the ValueFactories?)

Ps. if anyone creates a more pedagogical example I'll award their post with the "solved" mark.

查看更多
贼婆χ
3楼-- · 2019-03-21 21:50

Cell Value Factory

For each column in your table, you set/create a Cell Value Factory. Each row has a corresponding object in tableview.getItems(). To determine how this object is displayed across the columns, each column uses its own Cell Value Factory. The factories take in the object and return the value to be displayed.

Since String[][] is an array of String[]'s, we want the factories to take in a String[] and return the String corresponding to its column.

Example

Here is an example of creating the Cell Value Factories in this way. It is a little verbose but that can be cleaned up with lambdas! (see Lambdas section).

// ---------------------------------------Initialize the data
String[][] data = ...; // Get name/value pairs from somewhere

// ---------------------------------------Setup a TableView with two TableColumns 
    /* ... Code ... */

// ---------------------------------------Add Cell Value Factories
nameColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<String[], String>, ObservableValue<String>>() {
    @Override
    public ObservableValue<String> call(TableColumn.CellDataFeatures<String[], String> p) {
        String[] x = p.getValue();
        if (x != null && x.length>0) {
            return new SimpleStringProperty(x[0]);
        } else {
            return new SimpleStringProperty("<no name>");
        }
    }
});

valueColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<String[], String>, ObservableValue<String>>() {
    @Override
    public ObservableValue<String> call(TableColumn.CellDataFeatures<String[], String> p) {
        String[] x = p.getValue();
        if (x != null && x.length>1) {
            return new SimpleStringProperty(x[1]);
        } else {
            return new SimpleStringProperty("<no value>");
        }
    }
});

// ---------------------------------------Add Data to TableView
tableView.getItems().addAll(Arrays.asList(data));

Result

If you throw this example (via the Source Code), on a Stage, this is what you get. it works!

Lambdas

Java is a little verbose, especially when playing with anonymous classes. Thankfully there are lambdas, which bring a little readability back to Java. Here are the same Cell Value Factories from the example, re-written with lambdas.

nameColumn.setCellValueFactory((p)->{
        String[] x = p.getValue();
        return new SimpleStringProperty(x != null && x.length>0 ? x[0] : "<no name>");
});

valueColumn.setCellValueFactory((p)->{
        String[] x = p.getValue();
        return new SimpleStringProperty(x != null && x.length>1 ? x[1] : "<no value>");
});

Source Code

Here is a stand-alone JavaFX class that uses Cell Value Factories in this way.

import java.util.Arrays;
import java.util.List;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
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;

/**
 *
 * @author nonfrt
 */
public class TableStuff extends Application {

    @Override
    public void start(Stage primaryStage) {

        // Create the data structure
        String[][] data = new String[5][2];
        data[0] = new String[]{"Jon Skeet","876k"};
        data[1] = new String[]{"Darin Dimitrov","670k"};
        data[2] = new String[]{"BalusC","660k"};
        data[3] = new String[]{"Hans Passant","635k"};
        data[4] = new String[]{"Marc Gravell","610k"};

        // Create the table and columns
        TableView<String[]> tv = new TableView();
            TableColumn<String[],String> nameColumn = new TableColumn();
                nameColumn.setText("Name Column");

            TableColumn<String[],String> valueColumn = new TableColumn();
                valueColumn.setText("Value Column");
        tv.getColumns().addAll(nameColumn,valueColumn);

        // Add cell value factories
//        nameColumn.setCellValueFactory((p)->{
//                String[] x = p.getValue();
//                return new SimpleStringProperty(x != null && x.length>0 ? x[0] : "<no name>");
//        });
//
//        valueColumn.setCellValueFactory((p)->{
//                String[] x = p.getValue();
//                return new SimpleStringProperty(x != null && x.length>1 ? x[1] : "<no value>");
//        });
        nameColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<String[], String>, ObservableValue<String>>() {
            @Override
            public ObservableValue<String> call(TableColumn.CellDataFeatures<String[], String> p) {
                String[] x = p.getValue();
                if (x != null && x.length>0) {
                    return new SimpleStringProperty(x[0]);
                } else {
                    return new SimpleStringProperty("<no name>");
                }
            }
        });

        valueColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<String[], String>, ObservableValue<String>>() {
            @Override
            public ObservableValue<String> call(TableColumn.CellDataFeatures<String[], String> p) {
                String[] x = p.getValue();
                if (x != null && x.length>1) {
                    return new SimpleStringProperty(x[1]);
                } else {
                    return new SimpleStringProperty("<no value>");
                }
            }
        });

        // Add Data
        tv.getItems().addAll(Arrays.asList(data));

        // Finish setting the stage
        StackPane root = new StackPane();
        root.getChildren().add(tv);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("Cell Value Factory Example");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }
}

[NOTE: The answer to this other StackOverFlow question is another example of this. ]

查看更多
霸刀☆藐视天下
4楼-- · 2019-03-21 21:53

You can also try this with map without knowing how many columns you'll add to the table! so you can use the map key as the tableview header

TableView<Map<String, String>> tableView = new TableView<>();

ArrayList<Map<String, String>> valuesArray = new ArrayList<>();
for(Object object : filter.getEntities()) {
  Map<String, String> values = getValuesAsStringsForObject(object);
  valuesArray.add(values);
}

for(Object o : filter.getEntities()){
  List<String> headers = getHeaders(o);
  for (String header :headers){
    TableColumn<Map<String, String>, String> tableColumn = new TableColumn<>(header);
    tableColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().get(header)));

    tableView.getColumns().add(tableColumn);
  }
  break;
}

tableView.getItems().addAll(valuesArray);
return tableView;
查看更多
登录 后发表回答