javafx tableview data from List
Asked Answered
A

2

3

I've got problem with TableView in JavaFX.

Creating tables from existing class with all fields defined is easy.

But I'm wondering if it is possible to make table and add data from List?

I've made something like this but it kinda doesn't work.

    @FXML
    private TableView<List<String>> testTable;

    ....


    List<String> list = new ArrayList<>();
    list.add("1hello");
    list.add("1hello2");

    List<String> list2 = new ArrayList<>();
    list2.add("2hello");
    list2.add("2hello2");

    ObservableList<List<String>> lists = FXCollections.observableArrayList();
    lists.add(list);
    lists.add(list2);

    TableColumn<List<String>, String> coll = new TableColumn<>("one");
    coll.setCellValueFactory(new PropertyValueFactory<List<String>,String>(""));
    TableColumn<List<String>, String> coll2 = new TableColumn<>("two");
    coll2.setCellValueFactory(new PropertyValueFactory<List<String>,String>(""));

    testTable.getColumns().add(coll);
    testTable.getColumns().add(coll2);

    lists.forEach(li -> {
        System.out.println("list: " +  li);
        testTable.getItems().add(li);

    }); 

It didn't insert any data. Is something like that even possible?

Agreed answered 12/9, 2017 at 8:11 Comment(3)
Why do you want to make a TableView of Lists?Atheling
Yeah, something like that. Each list in one row. And each one will got the same amount of Strings.Agreed
See also #25395516Immanent
C
2

You can do this by replacing String to StringProperty in the List:

@FXML
private TableView<List<StringProperty>> testTable;

then:

TableColumn<List<StringProperty>, String> coll = new TableColumn<>("one");

add the cellValueFactories:

col1.setCellValueFactory(data -> data.getValue().get(0));
col2.setCellValueFactory(data -> data.getValue().get(1));
.
.

and so on.

This means the first element of the list will be used in col1, the second element of the list will be used in col2.

Then you can populate the list like:

ObservableList<List<StringProperty>> data = FXCollections.observableArrayList();
List<StringProperty> firstRow = new ArrayList<>();
firstRow.add(0, new SimpleStringProperty("Andrew"));
firstRow.add(1, new SimpleStringProperty("Smith"));
.
.
.
data.add(firstRow);
.
.
.
and so on...
table.setItems(data);

It is doable this way but I would say it is a very bad practice.

Clair answered 12/9, 2017 at 9:56 Comment(9)
You can also do this with Strings; just use cellData -> new SimpleStringProperty(cellData.getValue().get(0)) as the cell value factory. If you are, e.g. creating a table displaying the result of parsing a tab-delimited or csv file, etc, this is your only real option.Immanent
But this won't create a new instance of SimpleStringProperty every time when it is called the value?Clair
Yes. So will, for example PropertyValueFactory if you have a "traditional " Java bean (no JavaFX properties). It works fine for static tables where the data is not changing.Immanent
Thank You Sunflame for answer. I was only curious is it possible. James_D thanks for comment and answer.Agreed
It might be a bad practice but sometimes we don't have choice. For example, I'm reading data from files and the data model changes from file to file. I thing the List here might be the best practice I can select.Orelle
@Orelle I cannot really agree, you can almost always change your data to be more convenient for displaying it on the UI. I think a list of list is not as transparent and easy to mentain as a list of rows. You can create a row of the data, with dynamic columns if you don't know the number of your data, and then handle it. Of course if it is a simple read only display, when you know you will never touch it, the list of list would do the job.Clair
@Clair It makes sense. Anyway, you answer here did help me, thx.Orelle
it's unfortunate that someone would write in bold "its a very bad idea" to want to vary the number of columns in a table at runtime. There are a million use cases where that makes sense. Ask the people who work on Excel. Or Pandas. Or Postgres. It's actually a "very bad" thing to provide such minimal support for doing so.Cist
Having dynamic columns in a TableView is ok i had a lot of tables with dynamic columns but in this answer I said that using that way as it was asked I think it is wrong since you have to take care of the indexes in a list which makes the maintenance complicated. I would use a Map<String,Property<?>> where you can have the column names/ids as key and as value whatever Property you want. But still i think the question was not about having dynamic columns but to have the data stored in a list instead in an object created for the data. Having a data class as a model makes the code cleaner.Clair
T
0

As a little piece for my work i need a TableView with dynamically filled data from a List. I looked at some other samples out there in StackOverflow and also at the Oracle tutorials. But nothing fits my needs.

To meet my necessity i puzzled the different solutions together. See the working sample below.

import java.util.ArrayList;
import javafx.util.Callback;
import javafx.util.StringConverter;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.application.Application;

/**
 *
 * @author Rob
 */
public class TableViewSample extends Application {

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

    @Override
    public void start(Stage stage) {
        Scene scene = new Scene(new Group());
        stage.setTitle("Table View Sample");
        stage.setWidth(400);
        stage.setHeight(500);

        final Label label = new Label("Student IDs");
        label.setFont(new Font("Arial", 20));

        TableView tableView = new TableView<>();
        tableView.setEditable(true);
        tableView.getSelectionModel().setCellSelectionEnabled(true);

        ObservableList<TableColumn> tableViewColumns = generateTableViewColumns();
        tableView.getColumns().setAll(tableViewColumns);

        ObservableList<TableColumn> tcs = tableView.getColumns();
        for (int i = 0; i < tcs.size(); i++) {
            TableColumn tc = tcs.get(i);

            if (i == 0) {
                Callback<TableColumn.CellDataFeatures<ArrayList, Color>, ObservableValue<Color>> cellValueFactory = buildCallbackColor(i);
                tc.setCellValueFactory(cellValueFactory);
            } else {
                Callback<TableColumn.CellDataFeatures<ArrayList, String>, ObservableValue<String>> cellValueFactory = buildCallbackString(i);
                tc.setCellValueFactory(cellValueFactory);
            }

        }

        ObservableList<ArrayList> tableViewRows = generateTableViewRows();
        tableView.getItems().setAll(tableViewRows);

        for (int i = 0; i < tcs.size(); i++) {
            TableColumn dataColumn = tcs.get(i);

            if (i == 0) {
                Callback<TableColumn<ArrayList, Color>, TableCell<ArrayList, Color>> cellFactoryPane = buildCallbackPane();
                dataColumn.setCellFactory(cellFactoryPane);
            } else {
                Callback<TableColumn<ArrayList, String>, TableCell<ArrayList, String>> cellFactoryTextFieldTableCell = buildCallbackTextFieldTableCell();
                dataColumn.setCellFactory(cellFactoryTextFieldTableCell);
            }

        }

        final VBox vbox = new VBox();

        vbox.setSpacing(5);
        vbox.setPadding(new Insets(10, 0, 0, 10));
        vbox.getChildren().addAll(label, tableView);

        ((Group) scene.getRoot()).getChildren().addAll(vbox);

        stage.setScene(scene);

        stage.show();
    }

    private ObservableList<TableColumn> generateTableViewColumns() {
        ObservableList<TableColumn> tableViewColumns = FXCollections.observableArrayList();
        TableColumn firstDataColumn = new TableColumn<>("Activity");
        TableColumn secondDataColumn = new TableColumn<>("Class A");
        TableColumn thirdDataColumn = new TableColumn<>("Class B");

        firstDataColumn.setMinWidth(80);
        secondDataColumn.setMinWidth(130);
        thirdDataColumn.setMinWidth(130);

        tableViewColumns.add(firstDataColumn);
        tableViewColumns.add(secondDataColumn);
        tableViewColumns.add(thirdDataColumn);        

        return tableViewColumns;
    }

    private ObservableList<ArrayList> generateTableViewRows() {
        int max = 6;
        ObservableList<ArrayList> tableViewRows = FXCollections.observableArrayList();
        for (int i = 1; i < max; i++) {
            ArrayList dataRow = new ArrayList<>();

            Color value1 = Color.GREEN;
            String value2 = "A" + i;
            String value3 = "B" + i;

            dataRow.add(value1);
            dataRow.add(value2);
            dataRow.add(value3);

            tableViewRows.add(dataRow);
        }
        return tableViewRows;
    }

    private Callback<TableColumn.CellDataFeatures<ArrayList, Color>, ObservableValue<Color>> buildCallbackColor(int index) {
        Callback<TableColumn.CellDataFeatures<ArrayList, Color>, ObservableValue<Color>> cellValueFactory = new Callback<TableColumn.CellDataFeatures<ArrayList, Color>, ObservableValue<Color>>() {
            @Override
            public ObservableValue<Color> call(TableColumn.CellDataFeatures<ArrayList, Color> param) {
                return new SimpleObjectProperty(param.getValue().get(index));
            }
        };
        return cellValueFactory;
    }

    private Callback<TableColumn.CellDataFeatures<ArrayList, String>, ObservableValue<String>> buildCallbackString(int index) {
        Callback<TableColumn.CellDataFeatures<ArrayList, String>, ObservableValue<String>> cellValueFactory = new Callback<TableColumn.CellDataFeatures<ArrayList, String>, ObservableValue<String>>() {
            @Override
            public ObservableValue<String> call(TableColumn.CellDataFeatures<ArrayList, String> param) {
                return new SimpleStringProperty((String) param.getValue().get(index));
            }
        };
        return cellValueFactory;
    }

    private Callback<TableColumn<ArrayList, Color>, TableCell<ArrayList, Color>> buildCallbackPane() {
        Callback<TableColumn<ArrayList, Color>, TableCell<ArrayList, Color>> cellFactory = new Callback<TableColumn<ArrayList, Color>, TableCell<ArrayList, Color>>() {
            @Override
            public TableCell call(TableColumn tableColumn) {
                double cellWidth = tableColumn.getMinWidth();
                double cellHeight = 35;

                TableCell tableCell = new TableCell<Object, Color>() {
                    @Override
                    protected void updateItem(Color item, boolean empty) {
                        super.updateItem(item, empty);

                        if (empty) {
                            setText(null);
                            setGraphic(null);
                        } else {
                            Pane p = new Pane();
                            p.setPrefSize(cellWidth, cellHeight);
                            Canvas canvasRectLayerColor = new Canvas();
                            p.getChildren().add(canvasRectLayerColor);
                            canvasRectLayerColor.setWidth(20);
                            canvasRectLayerColor.setHeight(20);
                            GraphicsContext gc = canvasRectLayerColor.getGraphicsContext2D();      
                            gc.setFill(item);
                            gc.fillRect(0, 0, canvasRectLayerColor.getWidth(), canvasRectLayerColor.getHeight());
                            setGraphic(p);
                        }
                    }
                };

                return tableCell;
            }
        };

        return cellFactory;
    }

    private Callback<TableColumn<ArrayList, String>, TableCell<ArrayList, String>> buildCallbackTextFieldTableCell() {
        Callback<TableColumn<ArrayList, String>, TableCell<ArrayList, String>> cellFactory = new Callback<TableColumn<ArrayList, String>, TableCell<ArrayList, String>>() {
            @Override
            public TableCell call(TableColumn tc) {
                TextFieldTableCell tftc = new TextFieldTableCell(new StringConverter() {
                    @Override
                    public String toString(Object t) {
                        return t.toString();
                    }

                    @Override
                    public Object fromString(String string) {
                        return string;
                    }
                });

                return tftc;
            }
        };
        return cellFactory;
    }

}

Hope this one help even it is quite late.

Triny answered 23/7, 2018 at 19:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.