1

Looking for help in diagnosing a behavior, as I'm at a loss for where it may be occurring.

On table edit, the labels update, but the values don't ever seem to, as whenever the table is updated the values revert. This happens with sorting, and can be shown to be true via:

for(Row r : elementsTable.getItems().get(0))
   System.out.println(r.getSymbol() + r.getWeight() + r.getAtom());

GUI:

@FXML
private TextField row1;
@FXML
private TextField row2;
@FXML
private TextField row3;
@FXML
private TableView<Row> elementsTable;
private List<Row> row;
private ObservableList<Row> oRow;

@Override
public void initialize(URL url, ResourceBundle rb) {
    // TODO
    row = new ArrayList();
}  

private void addRow(ActionEvent event) {    
 elementsTable.getItems().add(new Row(
         row1.getText(),
         row2.getText(),
         row3.getText()));
}

public void
construct(List<String> z, List<String> w, List<String> z)
throws Exception{
    //populate table rows
    for(int i = 0; i < z.size(); i++){
        row.add(new Row(z.get(i), w.get(i), a.get(i)));
    }
    oRow = FXCollections.observableArrayList(row);
    elementsTable.setItems(oRow);

}

FXML

  <TableView fx:id="elementsTable" editable="true" layoutX="67.0" layoutY="124.0" prefHeight="237.0" prefWidth="523.0">
     <columns>
        <TableColumn fx:id="elementCol" editable="true" prefWidth="138.0" text="Element">
           <cellFactory>
              <TextFieldTableCell fx:factory="forTableColumn" />
           </cellFactory>
           <cellValueFactory>
              <PropertyValueFactory property="symbol" />
           </cellValueFactory>
        </TableColumn>
        <TableColumn fx:id="weightCol" editable="true" prefWidth="191.0" text="Weight Fraction">
           <cellFactory>
              <TextFieldTableCell fx:factory="forTableColumn" />
           </cellFactory>
           <cellValueFactory>
              <PropertyValueFactory property="weight" />
           </cellValueFactory>
        </TableColumn>
        <TableColumn fx:id="atomCol" editable="true" prefWidth="189.0" text="Atom Fraction">
           <cellFactory>
              <TextFieldTableCell fx:factory="forTableColumn" />
           </cellFactory>
           <cellValueFactory>
              <PropertyValueFactory property="atom" />
           </cellValueFactory>
        </TableColumn>
     </columns>
     <items>
        <FXCollections fx:factory="observableArrayList">
           <Row atom="" symbol="" weight="" />
        </FXCollections>
     </items>
  </TableView>

Table model

public class Row{
    private final SimpleStringProperty symbol = new SimpleStringProperty("");
    private final SimpleStringProperty weight = new SimpleStringProperty("");
    private final SimpleStringProperty atom = new SimpleStringProperty("");

    /**
     * Default constructor, defaults to empty strings
     */
    public Row(){
        this("","","");
    }
    /**
     * 
     * @param s symbol(or ZA)
     * @param w weight fraction
     * @param a atom fraction (or atom count ending with "#")
     */
    public Row(String s, String w, String a){
        setSymbol(s);
        setWeight(w);
        setAtom(a);
    }

    public String getSymbol(){return symbol.get();}
    public String getWeight(){return weight.get();}
    public String getAtom(){return atom.get();}

    public void setSymbol(String s){symbol.set(s);}
    public void setWeight(String w){weight.set(w);}
    public void setAtom(String a){atom.set(a);}
}

State 1: before doing anything enter image description here

State 2: edited cells enter image description here

State 3: adding row to table, data reverts edits enter image description here

Captain Prinny
  • 459
  • 8
  • 23

1 Answers1

1

The issue is that you don't provide "property accessor" methods in your Row class. Without these, the PropertyValueFactory and the TextFieldTableCell cannot access the actual properties and bind/listen to them for changes.So your Row class should look like

public class Row{
    private final SimpleStringProperty symbol = new SimpleStringProperty("");
    private final SimpleStringProperty weight = new SimpleStringProperty("");
    private final SimpleStringProperty atom = new SimpleStringProperty("");

    /**
     * Default constructor, defaults to empty strings
     */
    public Row(){
        this("","","");
    }
    /**
     * 
     * @param s symbol(or ZA)
     * @param w weight fraction
     * @param a atom fraction (or atom count ending with "#")
     */
    public Row(String s, String w, String a){
        setSymbol(s);
        setWeight(w);
        setAtom(a);
    }

    public String getSymbol(){return symbol.get();}
    public String getWeight(){return weight.get();}
    public String getAtom(){return atom.get();}

    public void setSymbol(String s){symbol.set(s);}
    public void setWeight(String w){weight.set(w);}
    public void setAtom(String a){atom.set(a);}

    public StringProperty symbolProperty() {
        return symbol ;
    }

    public StringProperty weightProperty() {
        return weight ;
    }

    public StringProperty atomProperty() {
        return atom ;
    } 
}

For more details, check the API documentation for PropertyValueFactory and the tutorial section on properties (which is just general background on the JavaFX property pattern).

If, for some reason, you can't provide these methods (e.g. you are using existing classes you cannot change as your model), you can provide the "wiring" between the table column editing and the model by hand, using TableColumn.setOnEditCommit(...) in your controller class. The following should make things work with your existing Row class, though I recommend implementing the model with the "property accessors" whenever possible.

public void initialize(URL url, ResourceBundle rb) {
    // ...
    elementCol.setOnEditCommit(e -> {
        int rowIndex = e.getTablePosition().getRow();
        Row row = elementsTable.getItems().get(rowIndex);
        row.setElement(e.getNewValue());
    });

    // similarly for other columns...
}
James_D
  • 201,275
  • 16
  • 291
  • 322
  • I god the code from another tutorial that didn't offer those. I ended up just hacking in normal FX listeners for the onEdit of the table and those work. For future whatever, is that literally all that it takes? – Captain Prinny Jul 07 '15 at 15:16
  • 1
    Yup, there are some badly-written tutorials out there on this stuff (some of them even on the Oracle site itself). – James_D Jul 07 '15 at 15:17
  • Everything works much more smoothly if you define your model classes following that pattern (i.e. `getX()`, `setX(...)` and `xProperty()` methods). In cases where you can't, the recommended approach for a `TableView` is to register an `onEditCommit` handler on the table column: I updated the answer to include this case. If you use a `ListView` and you want the cells to update when properties belonging to the elements in the list change, you may need to create the list with an "extractor", see http://stackoverflow.com/questions/23822550/listview-is-not-reflecting-changes. – James_D Jul 07 '15 at 15:30
  • That's what I ended up doing, I hacked it out of the Oracle tutorial. – Captain Prinny Jul 07 '15 at 15:55