So I've been trying to implement a TableView where you can edit a column just by clicking on it, and then save the edit by pressing the enter key. I've taken at lot of the code from the answer in this thread. This is the result:
import com.sun.javafx.collections.ObservableListWrapper;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.stage.Stage;
public class TestEditableTable extends Application {
public void start(Stage stage) {
TableView<ObservableList<String>> tableView = new TableView<ObservableList<String>>();
tableView.setEditable(true);
// Some dummy data.
ObservableList<ObservableList<String>> dummyData = FXCollections.observableArrayList();
ObservableList<String> firstRow = FXCollections.observableArrayList("Jack", "Smith");
dummyData.add(firstRow);
ObservableList<String> secondRow = FXCollections.observableArrayList("Peter", "Smith");
dummyData.add(secondRow);
TableColumn<ObservableList<String>, String> firstCol = new TableColumn<ObservableList<String>, String>(
"First name");
firstCol.setCellValueFactory(
(TableColumn.CellDataFeatures<ObservableList<String>, String> param) -> new SimpleStringProperty(
param.getValue().get(0)));
TableColumn<ObservableList<String>, String> secondCol = new TableColumn<ObservableList<String>, String>(
"Last name");
secondCol.setCellValueFactory(
(TableColumn.CellDataFeatures<ObservableList<String>, String> param) -> new SimpleStringProperty(
param.getValue().get(1)));
secondCol.setCellFactory(cell -> new EditableCell());
tableView.getColumns().addAll(firstCol, secondCol);
tableView.getItems().addAll(dummyData);
Scene scene = new Scene(tableView);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
class EditableCell extends TableCell<ObservableList<String>, String> {
private TextField textfield = new TextField();
// When the user presses the enter button the edit is saved.
public EditableCell() {
setOnKeyPressed(e -> {
if (e.getCode().equals(KeyCode.ENTER)) {
commitEdit(textfield.getText());
}
});
}
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (isEmpty()) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
textfield.setText(item);
setGraphic(textfield);
setText(null);
} else {
setText(item);
setGraphic(null);
}
}
}
@Override
public void startEdit() {
super.startEdit();
textfield.setText(getItem());
setGraphic(textfield);
setText(null);
}
@Override
public void cancelEdit() {
super.cancelEdit();
setGraphic(null);
setText(getItem());
}
@Override
public void commitEdit(String value) {
super.commitEdit(value);
// This works. But gives me a "Discouraged access: The type 'ObservableListWrapper<String>' is not API (restriction on required library 'C:\Program Files\Java\jre1.8.0_60\lib\ext\jfxrt.jar')".
ObservableListWrapper<String> ob = ((ObservableListWrapper<String>) this.getTableRow().getItem());
ob.set(1, value);
// I had to put this in a Platform.runLater(), otherwise the textfield remained open.
Platform.runLater(() -> {
setText(value);
setGraphic(null);
});
}
}
}
And this program works OK. The cell is edited when you press the enter button. But there are two main issues with this:
Once I've pressed enter and a cell has been edited. I can't edit it again by clicking on it. I need to press another row to be able to edit it again. Setting to focus on the parent of the row didn't do the trick.
The code inside
commitEdit()
works. But it is ugly and using aObservableListWrapper
gives me the warning "Discouraged access: The type 'ObservableListWrapper' is not API (restriction on required library 'C:\Program Files\Java\jre1.8.0_60\lib\ext\jfxrt.jar')". Also the cell index is hard coded, which wont work if I use this in many different columns.
None of the issues mentioned above is acceptable.
The final implementation must support restriction of the input in the textfield. As far as I understand it this means that I need to have access to the TextField
object displayed in the cell, as in my current implementation.