Editing a Number cell in a TableView
Asked Answered
P

3

9

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.

Paneling answered 16/11, 2013 at 15:17 Comment(0)
I
15

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).

Idolum answered 16/11, 2013 at 16:18 Comment(3)
Thanks for the suggestion, I tried the method you suggested but the <BMIRecord, Number> part gets rejected. Without that, I get an error similar to before: "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,Number>,TableCell<Object,Number>>)"Paneling
I'm sorry, my Java is a bit rusty (I use Scala). I've fixed the syntax.Idolum
That worked perfectly. Thank you for taking the time to improve the answer :)Paneling
I
1

use this

age.setCellFactory(TextFieldTableCell.forTableColumn(new IntegerStringConverter()));
Infamy answered 8/5, 2017 at 17:41 Comment(0)
F
0

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);
Fabrin answered 1/7, 2019 at 12:22 Comment(2)
no reason to go complex ... for simple enough contexts a converter is very straightforward to use. If you think that your library is far far better than the available default support (the question is very old, the default improved over the years), you probably should use an example that demonstrates that superiority :)Babushka
I don't think that my library is better or worse. It solve problem of boilerplate code in my applications. Application has a few tables with similar columns. For instance, BigDecimal column which has minimum and maximum allowed values. By the way, it prevents user from invalid-symbol inputFabrin

© 2022 - 2024 — McMap. All rights reserved.