Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaFX TableView and ObservableList - How to auto-update the table?

Tags:

javafx

I know questions similar to this have been asked, and on different dates, but I'll put an SSCCE in here and try to ask this simply.

I would like to be able to update the data model, and have any views upon it automatically update, such that any caller updating the model is not aware of whatever views there presently are. This is what I learned/tried so far, and without calling TableView.refresh() it does not update. What am I missing?

main.java:

package application;

import javafx.application.Application;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;

public class Main extends Application {
    @Override
    public void start(Stage stage) {

        // data
        ObservableList<Crew> data = FXCollections.observableArrayList();
        data.addAll(new Crew(1, "A"), new Crew(2, "B"));

        // table
        TableColumn<Crew, Integer> crewIdCol = new TableColumn<Crew, Integer>("Crew ID");
        crewIdCol.setCellValueFactory(new PropertyValueFactory<Crew, Integer>("crewId"));
        crewIdCol.setMinWidth(120);
        TableColumn<Crew, String> crewNameCol = new TableColumn<Crew, String>("Crew Name");
        crewNameCol.setCellValueFactory(new PropertyValueFactory<Crew, String>("crewName"));
        crewNameCol.setMinWidth(180);
        TableView<Crew> table = new TableView<Crew>(data);
        table.getColumns().addAll(crewIdCol, crewNameCol);
        table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);

        // button
        Button button = new Button(" test ");
        button.setOnAction(ae -> {
            // test
            StringProperty nameProp = data.get(0).crewName();
            if(nameProp.get().equals("A")) {
                data.get(0).setCrewName("foo");
                // table.refresh();
                System.out.println("foo");
            } else {
                data.get(0).setCrewName("A");
                // table.refresh();
                System.out.println("A");
            }
        });

        VBox box = new VBox(10);
        box.setAlignment(Pos.CENTER);;
        box.getChildren().addAll(table, button); 
        Scene scene = new Scene(box);
        stage.setScene(scene);
        stage.show();
    }

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

}

Crew.java

package application;

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class Crew {
    private final IntegerProperty crewId = new SimpleIntegerProperty();
    private final StringProperty crewName = new SimpleStringProperty();

    Crew(int id, String name) {
        crewId.set(id);
        crewName.set(name);
    }

    public IntegerProperty crewId() { return crewId; }
    public final int getCrewId() { return crewId.get(); }
    public final void setCrewId(int id) { crewId.set(id); }

    public StringProperty crewName() { return crewName; }
    public final String getCrewName() { return crewName.get(); }
    public final void setCrewName(String name) { crewName.set(name); }

}
like image 686
Mark Meyers Avatar asked Dec 15 '22 05:12

Mark Meyers


1 Answers

Your model class Crew has the "wrong" name for the property accessor methods. Without following the recommended method naming scheme, the (somewhat legacy code) PropertyValueFactory will not be able to find the properties, and thus will not be able to observe them for changes:

package application;

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class Crew {
    private final IntegerProperty crewId = new SimpleIntegerProperty();
    private final StringProperty crewName = new SimpleStringProperty();

    Crew(int id, String name) {
        crewId.set(id);
        crewName.set(name);
    }

    public IntegerProperty crewIdProperty() { return crewId; }
    public final int getCrewId() { return crewId.get(); }
    public final void setCrewId(int id) { crewId.set(id); }

    public StringProperty crewNameProperty() { return crewName; }
    public final String getCrewName() { return crewName.get(); }
    public final void setCrewName(String name) { crewName.set(name); }

}

Alternatively, just implement the callback directly:

crewIdCol.setCellValueFactory(cellData -> cellData.getValue().crewIdProperty());

in which case the compiler will ensure that you use an existing method name for the property.

like image 177
James_D Avatar answered Apr 04 '23 17:04

James_D