I have read several threads on using Properties to display data in a JavaFX TableView and I can get all of these to work. Up until now I have been displaying data without problem in my TableView without Properties. However, moving forward I think I need to start using Properties, particularly as I want my TableView to be editable. I suspect that the problem is because all the examples I have seen use one object to display 1 row of data, whereas in my model I have one object (a Field object) per cell, though I can't think why this should be a problem.
Below is a VERY cut-down version of my Field class, enough for this test, but not enough to show its real use. I also have a test class TableViewDemo. Any help with this irksome problem would be much appreciated. Either it will be a silly mistake, or a fundamental problem with my design.
package javafxtest;
import java.sql.SQLException;
import javafx.beans.property.SimpleObjectProperty;
public class Field {
private final SimpleObjectProperty objectValue; //All the data is stored as an object, then cast to appropriate data type
public Field(Object objValue) throws SQLException {
if (objValue == null) {
throw new SQLException("Field value is null");
}
this.objectValue = new SimpleObjectProperty(objValue);
}
public final String getStringValue() {
return (String) this.objectValue.get();
}
public final int getIntValue() {
return (Integer) this.objectValue.get();
}
public final double getDoubleValue() {
return (Double) this.objectValue.get();
}
public final boolean getBooleanValue() {
return (Boolean) this.objectValue.get();
}
}
package javafxtest;
import java.sql.SQLException;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class TableViewDemo extends Application {
@Override
public void start(Stage myStage) {
// Set up the stage
myStage.setTitle("Demonstrate TableView");
FlowPane rootNode = new FlowPane(10, 10);
rootNode.setAlignment(Pos.CENTER);
Scene myScene = new Scene(rootNode, 600, 450);
myStage.setScene(myScene);
//Set up some test data - 4 rows of 4 columns
ObservableList<Field[]> dataModel = FXCollections.observableArrayList();
ObservableList<Field[]> dataModel2 = FXCollections.observableArrayList();
Field[] fields1;
Field[] fields2;
Field[] fields3;
Field[] fields4;
try {
fields1 = new Field[4];
fields1[0] = new Field("R1C1");
fields1[1] = new Field(25);
fields1[2] = new Field(100.00);
fields1[3] = new Field(true);
fields2 = new Field[4];
fields2[0] = new Field("R2C1");
fields2[1] = new Field(30);
fields2[2] = new Field(150.00);
fields2[3] = new Field(false);
fields3 = new Field[4];
fields3[0] = new Field("R3C1");
fields3[1] = new Field(397);
fields3[2] = new Field(1005.99);
fields3[3] = new Field(true);
fields4 = new Field[4];
fields4[0] = new Field("R4C1");
fields4[1] = new Field(1085);
fields4[2] = new Field(1.00);
fields4[3] = new Field(false);
//Add the data to the dataModel
dataModel.addAll(fields1, fields2, fields3, fields4);
//Set up the columns ===== This is where the likely problem is, or at least part of the problem ====
TableColumn<Field[], String> colString = new TableColumn<>("String");
colString.setCellValueFactory(new PropertyValueFactory<>("stringValue"));
colString.setPrefWidth(150);
TableColumn<Field[], Integer> colInt = new TableColumn<>("int");
colInt.setCellValueFactory(new PropertyValueFactory<>("intValue"));
colInt.setPrefWidth(100);
TableColumn<Field[], Double> colDouble = new TableColumn<>("double");
colDouble.setCellValueFactory(new PropertyValueFactory<>("doubleValue"));
colDouble.setPrefWidth(100);
TableColumn<Field[], Boolean> colBoolean = new TableColumn<>("boolean");
colBoolean.setCellValueFactory(new PropertyValueFactory<>("booleanValue"));
colBoolean.setPrefWidth(75);
//Set up the TableView - it takes an Array of Field objects. Add the data and columns to it
TableView<Field[]> tableView = new TableView<Field[]>();
tableView.setItems(dataModel);
tableView.getColumns().addAll(colString, colInt, colDouble, colBoolean);
//Print out the datamodel
dataModel2 = tableView.getItems();
for (int i = 0; i < dataModel2.size(); i++) {
Field[] fields = new Field[4];
fields = dataModel2.get(i);
for (int j = 0; j < fields.length; j++) {
Field field = fields[j];
switch (j) {
case 0: //Column is a String data type
System.out.println(field.getStringValue());
break;
case 1: //Column is a int data type
System.out.println(field.getIntValue());
break;
case 2: //Column is a double data type
System.out.println(field.getDoubleValue());
break;
case 3: //Column is a boolean data type
System.out.println(field.getBooleanValue());
break;
default:
break;
}
}
}
// Set the tableView size
tableView.setPrefWidth(500);
tableView.setPrefHeight(300);
rootNode.getChildren().add(tableView);
} catch (SQLException e) {
System.out.println(e.getLocalizedMessage());
}
myStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
This is what I currently use to populate a cell with a String value:
final int x = i;
tableColumn<Field[], String> columnString = (TableColumn<Field[], String>) this.columnModel.get(i);
columnString.setCellValueFactory((TableColumn.CellDataFeatures<Field[], String> fieldArray) -> {
Field arrayField = fieldArray.getValue()[x];
return new ReadOnlyObjectWrapper(arrayField.getStringValue());
});