Home > Software engineering >  Javafx: How to populate a TableView with a map?
Javafx: How to populate a TableView with a map?

Time:03-28

I'm having trouble trying to populate a TableView with a map<String, Double>. I've been looking at different kind of questions on stack overflow and other pages as well, but my brain doesn't seem to get it.

I have a class that holds a Map with product names as key, and normal prices as value. I've created a tableView with a column for names and a main column for prices divided in two columns normal and new, as you can see below.

public class CreatePricesPane extends GridPane {
    private TableView table = new TableView<>();
    private PriceList tempPriceList;


    public CreatePricesPane(){
        this.tempPriceList = Controller.getController().createPriceList();
        this.setPadding(new Insets(20));
        this.setHgap(70);
        this.setVgap(10);
        this.setGridLinesVisible(false);
//        this.setPrefSize(800, 500);
        this.setStyle("-fx-background-color: #fff");

        //All about table
        this.table.setEditable(true);
        this.table.setItems(this.generateData());
        TableColumn<Map,String> nameColumn = new TableColumn("Names");
        nameColumn.setMinWidth(300);

        TableColumn pricesColumn = new TableColumn("Prices");

        TableColumn<Map,Double> normalPriceColumn = new TableColumn("Normal price");
        normalPriceColumn.setMinWidth(150);
//        normalPriceColumn.setCellValueFactory(new MapValueFactory<>());

        TableColumn newPriceColumn = new TableColumn("New price");
        newPriceColumn.setMinWidth(150);

        pricesColumn.getColumns().addAll(normalPriceColumn,newPriceColumn);


        table.setEditable(true);
        table.getSelectionModel().setCellSelectionEnabled(true);
        table.getColumns().setAll(nameColumn, pricesColumn);

I've made this method to make the Map into a ObservableList.

    private ObservableList<Map<String, Double>> generateData(){
        ObservableList<Map<String,Double>> alldata = FXCollections.observableArrayList();

        Map<String, Double> map = new HashMap(this.tempPriceList.getPriceListMap());

        for (Map.Entry<String, Double> entry : map.entrySet()) {
            Map<String, Double> dataRow = new HashMap<>();
            dataRow.put(entry.getKey(), entry.getValue());
            alldata.add(dataRow);
        }
        return alldata;
    }

This is where I am stuck. I don't know how to populate each cell with this ObservableList. I do know I have to use "setCellFactory" in some way. Does anyone have any suggestions how to make the last code? Or send me in the right direction?

Thanks in advance!

This is what I have tried until now. But actually I'm having trouble understanding what's going on in this code beneath. I hoped it would populate the table with names at start. No luck.

        Callback<TableColumn<Map, String>, TableCell<Map, String>>
                cellFactoryForMap = new Callback<TableColumn<Map, String>,
                TableCell<Map, String>>() {
            @Override
            public TableCell call(TableColumn p) {
                return new TextFieldTableCell(new StringConverter() {
                    @Override
                    public String toString(Object t) {
                        return t.toString();
                    }
                    @Override
                    public Object fromString(String string) {
                        return string;
                    }
                });
            }
        };

nameColumn.setCellFactory(cellFactoryForMap);

CodePudding user response:

Make a true data type for your TableView items.

public class Item {
    private final StringProperty name;
    private final DoubleProperty price;
    private final DoubleProperty newPrice;

    public Item() {
        name = new SimpleStringProperty(this, "name");
        price = new SimpleDoubleProperty(this, "price");
        newPrice = new SimpleDoubleProperty(this, "newPrice");
    }

    public Item(String name,
                double price) {
        this();
        setName(name);
        setPrice(price);
    }

    public StringProperty nameProperty() {
        return name;
    }

    public String getName() {
        return name.get();
    }

    public void setName(String name) {
        this.name.set(name);
    }

    public DoubleProperty priceProperty() {
        return price;
    }

    public double getPrice() {
        return price.get();
    }

    public void setPrice(double price) {
        this.price.set(price);
    }

    public DoubleProperty newPriceProperty() {
        return newPrice;
    }

    public double getNewPrice() {
        return newPrice.get();
    }

    public void setNewPrice(double price) {
        this.newPrice.set(price);
    }
}

(Note: In real world applications, programmers should never use double to represent money amounts, since float and double are inexact numbers which can lose information. BigDecimal is the correct type for representing money.)

Now your TableColumns’ cell value factories become a lot easier:

nameColumn.setCellValueFactory(f -> f.getValue().nameProperty());
pricesColumn.setCellValueFactory(f -> f.getValue().priceProperty());
newPriceColumn.setCellValueFactory(f -> f.getValue().newPriceProperty());

Creating those items is pretty similar to what you’re already doing, albeit simpler:

private ObservableList<Item> generateData() {
    ObservableList<Item> alldata = FXCollections.observableArrayList();

    for (Map.Entry<String, Double> entry : map.entrySet()) {
        alldata.add(new Item(entry.getKey(), entry.getValue()));
    }

    return alldata;
}
  • Related