How to make a table column with Integer datatype editable without changing it to String

Just use a cell factory that creates a text field in the cell when editing. To commit the edit, just parse the text from the text field.

Example:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.regex.Pattern;

import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class EditableTableColumnWithInteger extends Application {

    @Override
    public void start(Stage primaryStage) {
        TableView<Item> table = new TableView<>();
        table.setEditable(true);
        table.getItems().addAll(createData());

        TableColumn<Item, String> nameCol = new TableColumn<>("Name");
        nameCol.setCellValueFactory(cellData -> cellData.getValue().nameProperty());

        TableColumn<Item, Number> valueCol = new TableColumn<>("Value");
        valueCol.setCellValueFactory(cellData -> cellData.getValue().valueProperty());

        valueCol.setCellFactory(col -> new IntegerEditingCell());

        table.getColumns().add(nameCol);
        table.getColumns().add(valueCol);

        Button dataDumpButton = new Button("Dump data");
        dataDumpButton.setOnAction( e -> 
            table.getItems().stream().map(item -> item.getName()+":"+item.getValue()).forEach(System.out::println));
        HBox controls = new HBox(5, dataDumpButton);
        controls.setPadding(new Insets(10));
        controls.setAlignment(Pos.CENTER);

        primaryStage.setScene(new Scene(new BorderPane(table, null, null, controls, null), 600, 400));
        primaryStage.show();
    }

    public class IntegerEditingCell extends TableCell<Item, Number> {

        private final TextField textField = new TextField();
        private final Pattern intPattern = Pattern.compile("-?\\d+");

        public IntegerEditingCell() {
            textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
                if (! isNowFocused) {
                    processEdit();
                }
            });
            textField.setOnAction(event -> processEdit());
        }

        private void processEdit() {
            String text = textField.getText();
            if (intPattern.matcher(text).matches()) {
                commitEdit(Integer.parseInt(text));
            } else {
                cancelEdit();
            }
        }

        @Override
        public void updateItem(Number value, boolean empty) {
            super.updateItem(value, empty);
            if (empty) {
                setText(null);
                setGraphic(null);
            } else if (isEditing()) {
                setText(null);
                textField.setText(value.toString());
                setGraphic(textField);
            } else {
                setText(value.toString());
                setGraphic(null);
            }
        }

        @Override
        public void startEdit() {
            super.startEdit();
            Number value = getItem();
            if (value != null) {
                textField.setText(value.toString());
                setGraphic(textField);
                setText(null);
            }
        }

        @Override
        public void cancelEdit() {
            super.cancelEdit();
            setText(getItem().toString());
            setGraphic(null);
        }

        // This seems necessary to persist the edit on loss of focus; not sure why:
        @Override
        public void commitEdit(Number value) {
            super.commitEdit(value);
            ((Item)this.getTableRow().getItem()).setValue(value.intValue());
        }
    }

    private List<Item> createData() {
        Random rng = new Random();
        List<Item> items = new ArrayList<>();
        for (int i=1; i<=20; i++) {
            items.add(new Item("Item "+i, rng.nextInt(20)));
        }
        return items ;
    }

    public static class Item {
        private final StringProperty name = new SimpleStringProperty();
        private final IntegerProperty value = new SimpleIntegerProperty();
        public Item(String name, int value) {
            this.setName(name);
            this.setValue(value);
        }
        public final StringProperty nameProperty() {
            return this.name;
        }
        public final java.lang.String getName() {
            return this.nameProperty().get();
        }
        public final void setName(final java.lang.String name) {
            this.nameProperty().set(name);
        }
        public final IntegerProperty valueProperty() {
            return this.value;
        }
        public final int getValue() {
            return this.valueProperty().get();
        }
        public final void setValue(final int value) {
            this.valueProperty().set(value);
        }

    }

    public static void main(String[] args) {
        launch(args);
    }
}

Tags:

Javafx