I'm trying to create editable cells using Oracle poor tutorial. I figured out that their EditCell
class only updates when I click on the same row I currently edit or outside any row. In case when I click on another row, edit is cancelled. Here is the link to this tutorial and on the end of it you can find EditCell
class, but it's not the point of this question:
https://docs.oracle.com/javase/8/javafx/user-interface-tutorial/table-view.htm
This class creates TextField
for edit purposes. Clicking on another row launches cancel()
method. And there is this code line:
setText((String( getItem());
that blocks edit. I replaced it with:
setText((String) textField.getText());
and edit works now. But after editing this cell again old value is loaded to TextField
. I guess that ObservableList
is not updated after first edit.
Here is FXML code:
<GridPane fx:controller="sample.Controller"
xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
<TableView GridPane.columnIndex="0" GridPane.rowIndex="1" items="${controller.data}" editable="true">
<columns>
<TableColumn fx:id="colName" text="name">
<cellValueFactory>
<PropertyValueFactory property="Name"/>
</cellValueFactory>
</TableColumn>
<TableColumn fx:id="colSurname" text="surname">
<cellValueFactory>
<PropertyValueFactory property="Surname"/>
</cellValueFactory>
</TableColumn>
</columns>
</TableView>
</GridPane>
In controller I declare ObservableList
:
public class Controller {
@FXML
private TableColumn<Person, String> colName;
@FXML
private TableColumn<Person, String> colSurname;
@FXML
private ObservableList<Person> data;
public Controller(){
data = FXCollections.observableArrayList(
new Person("John", "S."),
new Person("Jane", "S.")
);
}
public TableColumn<Person, String> getColName() {
return colName;
}
public void setColName(TableColumn<Person, String> colName) {
this.colName = colName;
}
public TableColumn<Person, String> getColSurname() {
return colSurname;
}
public void setColSurname(TableColumn<Person, String> colSurname) {
this.colSurname = colSurname;
}
public ObservableList<Person> getData() {
return data;
}
public void setData(ObservableList<Person> data) {
this.data = data;
}
}
Person.java code:
public class Person {
private final SimpleStringProperty name;
private final SimpleStringProperty surname;
public Person(String name, String surname){
this.name = new SimpleStringProperty(name);
this.surname = new SimpleStringProperty(surname);
}
public String getName() {
return name.get();
}
public SimpleStringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public String getSurname() {
return surname.get();
}
public SimpleStringProperty surnameProperty() {
return surname;
}
public void setSurname(String surname) {
this.surname.set(surname);
}
}
In Main
I declare controller and editable column:
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
Parent root = (Parent) loader.load();
primaryStage.setScene(new Scene(root, 300, 275));
Controller controller = loader.getController();
TableColumn<Person, String> colName = controller.getColName();
Callback<TableColumn<Person, String>, TableCell<Person, String>> cellFactory =
(TableColumn<Person, String> p) -> new sample.EditCell();
colName.setCellFactory(cellFactory);
colName.setOnEditCommit(
(TableColumn.CellEditEvent<Person, String> t) -> {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setName(t.getNewValue());
});
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Do I need bind cell with ObservableList
? Or refresh it? How to update data
to have TextField always filled with actual value?
Here is whole EditCell
class:
class EditCell extends TableCell<Person, String> {
private TextField textField;
public EditCell() {
}
@Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
}
}
@Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
//setText((String) textField.getText());
//This line updates cell, but textField keeps old value after next edit.
setGraphic(null);
}
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
textField.focusedProperty().addListener(
(ObservableValue<? extends Boolean> arg0,
Boolean arg1, Boolean arg2) -> {
if (!arg2) {
commitEdit(textField.getText());
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}