In the model class there are two types of Property : SimpleStringProperty and SimpleIntegerProperty . It is necessary to display them in the form of a TableView , where the first column is the name of the value (description), and the second is the value itself. Something like this:

enter image description here

The data of the model class is packaged in Map<String,Property<?>> , where key is the name of the value, and value is Property with the value.

Interested in the following:

1) What should CellValueFactory look like so that the values ​​are correctly "zabindeny", i.e. if necessary, they could be changed in the table and the changes were reflected in the class-model.

2) Are the generics correct in the map-package?

UPD: Added an intermediate class to resolve this issue, but I think that the “crutch” turned out:

 public class PropertiesContainer { private String propName; private SimpleIntegerProperty concreteIntSourceProperty=null; private SimpleStringProperty concreteStrSourceProperty=null; private SimpleStringProperty destProperty; public PropertiesContainer(String propName, Property<?> property) { this.propName = propName; if (property.getClass().equals(SimpleStringProperty.class)){ concreteStrSourceProperty = (SimpleStringProperty) property; destProperty = new SimpleStringProperty(concreteStrSourceProperty.get()); } else if (property.getClass().equals(SimpleIntegerProperty.class)) { concreteIntSourceProperty = (SimpleIntegerProperty) property; destProperty = new SimpleStringProperty(Integer.toString(concreteIntSourceProperty.get())); } destProperty.addListener((observable, oldValue, newValue) -> { if (concreteIntSourceProperty!=null) concreteIntSourceProperty.set(Integer.valueOf(newValue)); else concreteStrSourceProperty.set(newValue); }); } public String getDestProperty() { return destProperty.get(); } public SimpleStringProperty destProperty() { return destProperty; } public String getPropName() { return propName; } } 

    1 answer 1

    I will begin with the example I expanded from the documentation :

     public class TableViewSample extends Application { public static final String Column1MapKey = "String"; public static final String Column2MapKey = "Integer"; public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) { Scene scene = new Scene(new Group()); stage.setTitle("Table View"); stage.setWidth(300); stage.setHeight(500); final Label label = new Label("TODO List"); label.setFont(new Font("Arial", 20)); TableColumn<Map, String> firstDataColumn = new TableColumn<>("Description"); TableColumn<Map, String> secondDataColumn = new TableColumn<>("Value"); firstDataColumn.setCellValueFactory(new MapValueFactory(Column1MapKey)); firstDataColumn.setMinWidth(130); secondDataColumn.setCellValueFactory(new MapValueFactory(Column2MapKey)); secondDataColumn.setMinWidth(130); TableView table_view = new TableView<>(generateDataInMap()); table_view.setEditable(true); table_view.getSelectionModel().setCellSelectionEnabled(true); table_view.getColumns().setAll(firstDataColumn, secondDataColumn); firstDataColumn.setCellFactory(new Callback<TableColumn<Map, String>, TableCell<Map, String>>() { @Override public TableCell call(TableColumn p) { return new TextFieldTableCell(new DefaultStringConverter()); } }); secondDataColumn.setCellFactory(new Callback<TableColumn<Map, String>, TableCell<Map, String>>() { @Override public TableCell call(TableColumn p) { return new TextFieldTableCell(new IntegerStringConverter()); } }); final VBox vbox = new VBox(); vbox.setSpacing(5); vbox.setPadding(new Insets(10, 0, 0, 10)); vbox.getChildren().addAll(label, table_view); ((Group) scene.getRoot()).getChildren().addAll(vbox); stage.setScene(scene); stage.show(); } private ObservableList<Map> generateDataInMap() { int max = 10; ObservableList<Map> allData = FXCollections.observableArrayList(); for (int i = 1; i < max; i++) { Map<String, Property<?>> dataRow = new HashMap<>(); SimpleStringProperty value1 = new SimpleStringProperty("string " + i); SimpleIntegerProperty value2 = new SimpleIntegerProperty(i); dataRow.put(Column1MapKey, value1); dataRow.put(Column2MapKey, value2); allData.add(dataRow); } return allData; } } 

    Well, the points of the question:

    Notice the CellValueFactory method — it fills the cells, but the dynamic change is the setCellFactory method — which, through a long chain of wrappers, is responsible for interacting with the Property object — the original example shows the work with the implementation of the StringConverter abstract class — an excellent way out of many situations.

    Yes - a great generic.

    UPD: As an example of working with SimpleObjectProperty

    class wrapper:

     class Wrapper { class InnerWrapper<T> extends SimpleObjectProperty<T> { @Override public void set(T newValue) { T temp = null; if (newValue.getClass().equals(Integer.class)) temp = newValue; else temp = (T) (((String) newValue).matches("\\d+") ? Integer.valueOf((String) newValue) : newValue.toString()); super.set(temp); } } private InnerWrapper inner = new InnerWrapper(); public Object getNut() { return nutProperty().get(); } public final SimpleObjectProperty nutProperty() { return inner; } public void setNut(Object candiate) { nutProperty().set(candiate); } } 

    Factory example:

      firstDataColumn.setCellValueFactory(new Callback<CellDataFeatures<Map, String>, ObservableValue<String>>() { public ObservableValue<String> call(CellDataFeatures<Map, String> p) { return ((Wrapper)p.getValue().get(Column1MapKey)).nutProperty(); } }); 
    • Your example works, but not as needed. The result is that in the first column there is always a String value ("string 1", "string2" ...), but the second is only a numerical value (1,2,3 ...). I, in turn, need to have both String and Integer values ​​in the second column and can be edited and bind them. I solved the situation in general, but rather clumsily: I introduced an intermediate object with two Property. The first source, the second for transmission to the table. On the second hangs a listener who, when the value changes, changes the acc. in the first Property - I. Perevoz
    • Yes, it smacks of a crutch. By the way, why didn't you use SimpleObjectProperty <Object>? Consider a universal container and less code ...? Exactly the same shell and type checking / casting in it is worth it - Peter Slusar
    • If it's not difficult, could you set an example of using SimpleObjectProperty, otherwise I didn’t figure it out with him - I. Perevoz
    • Yes, of course, I will bring it. Just explain what exactly is not clear? In essence, it is absolutely no different from all other types. Property stores any type of objects extends the abstract class ObjectPropertyBase <T> that in turn extends the implementation of ObjectProperty <T> half way to the Object superclass hangs listeners on the observed object implementing Observable - Peter Slusar
    • I do not understand the initialization of the TableView for this case and where the check / cast should be carried out. - I. Perevoz