JavaFX Table Cell Editing
Solution 1
I haven`t tried your example, but I think you just forgot to set the cellFactory for the specific column. Adding the folowing line should fix it:
usernameCol.setCellFactory(cellFactory);
Solution 2
I case someone needs a working example, I was able to get the code to work with this tutorial by adding a
usernameCol.setCellFactory(
TextFieldTableCell.forTableColumn());
and changing the usernameCol.setOnEditCommit
to
usernameCol.setOnEditCommit(
(TableColumn.CellEditEvent<Account, String> t) ->
( t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setFieldUsername(t.getNewValue())
);
Here is the complete testController
class that should be working (Other files stayed the same)
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import java.net.URL;
import java.util.ResourceBundle;
public class testController implements Initializable {
@FXML
private TableColumn<Account, String> usernameCol;
@FXML
private TableColumn<Account, String> balanceCol;
@FXML
private TableView<Account> accountTable;
@FXML
private TableColumn<Account, String> bookieCol;
@FXML
private TableColumn<Account, String> passwordCol;
private ObservableList<Account> dataList =
FXCollections.observableArrayList(
new Account("bookie", "username", "password", "0"));
@Override
public void initialize(URL location, ResourceBundle resources) {
bookieCol.setCellValueFactory(
new PropertyValueFactory<>("fieldBookie"));
usernameCol.setCellValueFactory(
new PropertyValueFactory<>("fieldUsername"));
usernameCol.setCellFactory(
TextFieldTableCell.forTableColumn());
usernameCol.setOnEditCommit(
(TableColumn.CellEditEvent<Account, String> t) ->
( t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setFieldUsername(t.getNewValue())
);
passwordCol.setCellValueFactory(
new PropertyValueFactory<Account, String>("fieldPassword"));
balanceCol.setCellValueFactory(
new PropertyValueFactory<Account, String>("fieldBalance"));
accountTable.setItems(dataList);
}
}
Solution 3
You can add the textField to editing cell using :TextFieldTableCell.forTableColumn();
If you need comboBox try : ComboBoxTableCell.forTableColumn(list);
// TextField:
usernameCol.setCellFactory(TextFieldTableCell.forTableColumn());
// ComboBox:
ObservableList<String> list = FXCollections.observableArrayList();
list.add("name 1");
list.add("name 2");
list.add("name 3");
list.add("name 4");
Callback<TableColumn<Account, String>, TableCell<Account, String>> cbtc =
ComboBoxTableCell.forTableColumn(list);
usernameCol.setCellFactory(cbtc);
Adam.J
Updated on April 25, 2021Comments
-
Adam.J about 3 years
I am trying to make a program in Java to manage my bookie accounts. I'm new to java, so I thought I would chose something simple to see how things work. I decided to use a tableview and make the individual cells editable. I've been following this tutorial http://java-buddy.blogspot.co.uk/2012/04/javafx-2-editable-tableview.html. It details how to do it using java code, and copying that into a new class works perfectly. I decided to try and tweak it to work with FXML, since I like Sceneviewer. My issue is, data is loaded in to the table, but when I click/doubleclick a cell, nothing happens. Here's my code.
testController.java
import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.*; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.util.Callback; import java.net.URL; import java.util.ResourceBundle; public class testController implements Initializable { @FXML private TableColumn<Account, String> usernameCol; @FXML private TableColumn<Account, String> balanceCol; @FXML private TableView<Account> accountTable; @FXML private TableColumn<Account, String> bookieCol; @FXML private TableColumn<Account, String> passwordCol; private ObservableList<Account> dataList = FXCollections.observableArrayList( new Account("bookie", "username", "password", "0")); @Override public void initialize(URL location, ResourceBundle resources) { Callback<TableColumn, TableCell> cellFactory = new Callback<TableColumn, TableCell>() { public TableCell call(TableColumn p) { return new EditingCell(); } }; bookieCol.setCellValueFactory( new PropertyValueFactory<>("fieldBookie")); usernameCol.setCellValueFactory( new PropertyValueFactory<>("fieldUsername")); usernameCol.setOnEditCommit( new EventHandler<TableColumn.CellEditEvent<Account, String>>() { @Override public void handle(TableColumn.CellEditEvent<Account, String> t) { ((Account)t.getTableView().getItems().get( t.getTablePosition().getRow())).setFieldUsername(t.getNewValue()); } }); passwordCol.setCellValueFactory( new PropertyValueFactory<Account, String>("fieldPassword")); balanceCol.setCellValueFactory( new PropertyValueFactory<Account, String>("fieldBalance")); accountTable.setItems(dataList); } class EditingCell extends TableCell<Account, String> { private TextField textField; public EditingCell() { } @Override public void startEdit() { super.startEdit(); if (textField == null) { createTextField(); } setGraphic(textField); setContentDisplay(ContentDisplay.GRAPHIC_ONLY); textField.selectAll(); } @Override public void cancelEdit() { super.cancelEdit(); setText(String.valueOf(getItem())); setContentDisplay(ContentDisplay.TEXT_ONLY); } @Override public void updateItem(String item, boolean empty) { super.updateItem(item, empty); if (empty) { setText(null); setGraphic(textField); } else { if (isEditing()) { if (textField != null) { textField.setText(getString()); } setGraphic(textField); setContentDisplay(ContentDisplay.GRAPHIC_ONLY); } else { setText(getString()); setContentDisplay(ContentDisplay.TEXT_ONLY); } } } private void createTextField() { textField = new TextField(getString()); textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2); textField.setOnKeyPressed(t -> { if (t.getCode() == KeyCode.ENTER) { commitEdit(textField.getText()); } else if (t.getCode() == KeyCode.ESCAPE) { cancelEdit(); } }); } private String getString() { return getItem() == null ? "" : getItem(); } } }
And here is my Account.java file.
import javafx.beans.property.SimpleStringProperty; public class Account { private SimpleStringProperty fieldBookie; private SimpleStringProperty fieldUsername; private SimpleStringProperty fieldPassword; private SimpleStringProperty fieldBalance; Account(String fbookie, String fusername, String fpassword, String fbalance){ this.fieldBookie = new SimpleStringProperty(fbookie); this.fieldUsername = new SimpleStringProperty(fusername); this.fieldPassword = new SimpleStringProperty(fpassword); this.fieldBalance = new SimpleStringProperty(fbalance); } public String getFieldBookie() { return fieldBookie.get(); } public String getFieldUsername() { return fieldUsername.get(); } public String getFieldPassword() { return fieldPassword.get(); } public String getFieldBalance() { return fieldBalance.get(); } public void setFieldBookie(String fBookie) { fieldBookie.set(fBookie); } public void setFieldUsername(String fUsername) { fieldUsername.set(fUsername); } public void setFieldPassword(String fPassword) { fieldUsername.set(fPassword); } public void setFieldBalance(String fBalance) { fieldUsername.set(fBalance); } }
Lastly, here is my fxml file.
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.*?> <?import javafx.scene.canvas.*?> <?import java.lang.*?> <?import javafx.scene.*?> <Group xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="testController"> <children> <TableView fx:id="accountTable" editable="true" prefHeight="291.0" prefWidth="302.0"> <columns> <TableColumn fx:id="bookieCol" prefWidth="75.0" text="Bookie" /> <TableColumn fx:id="usernameCol" prefWidth="75.0" text="Username" /> <TableColumn fx:id="passwordCol" prefWidth="75.0" text="Password" /> <TableColumn fx:id="balanceCol" prefWidth="75.0" text="Balance" /> </columns> </TableView> </children> </Group>
As I said earlier, nothing happens when I click on the Username cell. No errors, no textfield, just nothing. Any help would be greatly appreciated! Thanks
-
James_D almost 10 yearsThat's correct, but you will also need to fix the types (
Callback<TableColumn<Account, String>, TableCell<Account, String>>
etc). This approach is a bit legacy: you can dousernameCol.setCellFactory(TextFieldTableCell.forTableColumn());
and get rid of theEditingCell
-
Adam.J almost 10 yearsOkay thanks, I'll try it now. There aren't many tutorials around for javafx and editable tables unfortunately :(
-
James_D almost 10 yearsIt's not great, but the default Oracle tutorial covers the basics.
-
Adam.J almost 10 yearsIt worked perfectly, I removed the editing cell class too! Less code and is a lot easier for me to understand! Thanks both.
-
Pochmurnik about 5 yearsYes, but edit commit happens after pressing Enter key. I would like to save edit after changing cell or row. And I still don't know how.
-
crusam about 5 years@OrdinaryDraft you may want to have a look here: stackoverflow.com/questions/24694616/… . I was facing the same challenge and unfortunatly it is not an easy thing to achieve. Not sure if there is a better way yet, but kleopatra was able to help with decent workaround.
-
Pochmurnik about 5 yearsI already copied @Ogmios class from this thread and it works the way I want, thank you for link anyway.