JavaFX Table Cell Editing

33,728

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);
Share:
33,728
Adam.J
Author by

Adam.J

Updated on April 25, 2021

Comments

  • Adam.J
    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
    James_D almost 10 years
    That'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 do usernameCol.setCellFactory(TextFieldTableCell.forTableColumn‌​()); and get rid of the EditingCell
  • Adam.J
    Adam.J almost 10 years
    Okay thanks, I'll try it now. There aren't many tutorials around for javafx and editable tables unfortunately :(
  • James_D
    James_D almost 10 years
    It's not great, but the default Oracle tutorial covers the basics.
  • Adam.J
    Adam.J almost 10 years
    It worked perfectly, I removed the editing cell class too! Less code and is a lot easier for me to understand! Thanks both.
  • Pochmurnik
    Pochmurnik about 5 years
    Yes, 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
    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
    Pochmurnik about 5 years
    I already copied @Ogmios class from this thread and it works the way I want, thank you for link anyway.