Editing a Number cell in a TableView


Question

I have a TableView control which consists of several columns containg different types including Strings and Numbers. I have been trying to write a suitable callback function for an editable Number cell, but I can't get anywhere with it as I get a variety of issues ranging from empty cells to exceptions.

I have read through http://docs.oracle.com/javafx/2/ui_controls/table-view.htm but this only covers String values in cells. The sticking point seems to be lastNameCol.setCellFactory(TextFieldTableCell.forTableColumn());. This line seems to be tailored for Text fields and not for Number fields.

Also, I wish to perform validation on the numbers entered. Does this require a custom callback for the CellFactory in order to do this? If so, how can I develop a callback that accepts Number types and validates them?

Here's a code snippet of what I currently have in my project:

@FXML private TableView<BMIRecord> fxBMITable;
@FXML private TableColumn<BMIRecord, String> fxBMITableDate;
@FXML private TableColumn<BMIRecord, String> fxBMITableTime;
@FXML private TableColumn<BMIRecord, Number> fxBMITableHeight;
@FXML private TableColumn<BMIRecord, Number> fxBMITableWeight;
@FXML private TableColumn<BMIRecord, Number> fxBMITableBMI;

// ...

private void someFunc() {
    fxBMITable.setEditable(true);

    /* BMI table callback configuration */
    fxBMITableHeight.setCellValueFactory(new Callback<CellDataFeatures<BMIRecord, String>, ObservableValue<String>>() {
        public ObservableValue<String> call(CellDataFeatures<BMIRecord, String> p) {
            return new SimpleStringProperty(p.getValue().getDateString());
        }
    });

    /*
     * ERROR:
     * The method setCellFactory(Callback<TableColumn<BMIRecord,Number>,TableCell<BMIRecord,Number>>)
     * in the type TableColumn<BMIRecord,Number> is not applicable for the arguments
     * (Callback<TableColumn<Object,String>,TableCell<Object,String>>)
     */
    fxBMITableHeight.setCellFactory(TextFieldTableCell.forTableColumn());
    fxBMITableHeight.setOnEditCommit(new EventHandler<CellEditEvent<BMIRecord, Number>>() {
        @Override
        public void handle(CellEditEvent<BMIRecord, Number> t) {
            ((BMIRecord)t.getTableView().getItems().get(t.getTablePosition().getRow())).setHeight(t.getNewValue().doubleValue());
        }
    });
}

Thanks for any help in advance.

1
7
11/16/2013 3:17:57 PM

Accepted Answer

TextFieldTableCell is type parameterized and has a stringConverter property that you can use to convert to/from String and your desired type.

Try something like:

TextFieldTableCell.<BMIRecord, Number>forTableColumn(new NumberStringConverter())

NumberStringConverter has some additional constructors for specifying the formatting, see the javadocs.

Here's a more complete example:

public class Person {

    public Person(String name0, int age0) {
        name = name0;
        age = age0;
    }
    public String name;
    public int age;
}        

TableView<Person> personTable = new TableView<>();

TableColumn<Person, Number> age = new TableColumn<>();

age.setCellValueFactory(new Callback<CellDataFeatures<Person, Number>, ObservableValue<Number>>() {
    @Override
    public ObservableValue<Number> call(CellDataFeatures<Person, Number> p) {
        return new SimpleIntegerProperty(p.getValue().age);
} 
});

age.setCellFactory(TextFieldTableCell.<Person, Number>forTableColumn(new NumberStringConverter()));

This does not work well, though, because NumberStringConverter is so, to be blunt, badly implemented that it simply throws a ParseException at you if you happen to enter a string instead of a number in the cell.

However it should be relatively trivial to implement your own string converter, where you could also do some simple validation (e.g. value should be between 0 and 100).

14
11/16/2013 5:37:00 PM

jalvafx library:

Solution prevent user from invalid input. Only 0-9, coma and point symbols are allowed. You can set minimun and maximum number value.

enter image description here

Method to use signature:

TableViewUtils.setEditNumberCellFactory(
        TableColumn<T, K> cell,
        BiConsumer<T, Double> changeHandler, 
        String editControlHeader, 
        Double minAllowedValue,
        Double maxAllowedValue, 
        Function<T, Boolean> ignoreEdit)

Mock table:

TableView<String> tableView = new TableView<>();
tableView.getItems().addAll("USA", "Canada", "Brazil");

TableColumn<String, String> country = new TableColumn<>("Country");
TableColumn<String, Integer> column = new TableColumn<>("Some value");

tableView.getColumns().add(country);
tableView.getColumns().add(column);

country.setCellValueFactory(
                data -> new SimpleObjectProperty<>(data.getValue()));
column.setCellValueFactory(
                data -> new SimpleObjectProperty<>(800));

Solution:

BiConsumer<String, Double> changeHandler = (item, newValue) -> {
        System.out.println("New value is " + newValue);
        // Your code
};

Function<String, Boolean> ignoreEdit = item -> item.equalsIgnoreCase("USA");

TableViewUtils.setEditNumberCellFactory(column, changeHandler, "New value:", 200.0, 800.0, ignoreEdit);

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon