Javafx Editable Table cell


Question

I have tables with editable fields firstName,lastName and fullName. I am creating a cellFactory and Column like this

public class SampleFX1 extends Application {
private TableView table = new TableView();
private final ObservableList<Person> data =
        FXCollections.observableArrayList( new Person("balu", "Smith","1"), new Person("Isabella", "john","1"), 
        new Person("Ethan", "Williams","1"), new Person("Emma", "Jones","1"), new Person("Michael", "Brown","1"));

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

@Override
public void start(Stage stage) {
    Scene scene = new Scene(new Group());

    TableColumn firstNameCol = createSimpleFirstNameColumn();
    TableColumn lastNameCol = createLastNameColumn();
    TableColumn fullNameCol = createfullNameColumn();
    table.setItems(data);
    table.getColumns().addAll(firstNameCol, lastNameCol,fullNameCol);
    table.setEditable(true);

    ((Group) scene.getRoot()).getChildren().addAll(table);
    stage.setScene(scene);
    stage.show();
}

private TableColumn createSimpleFirstNameColumn() {
    TableColumn firstNameCol = new TableColumn("First Name");
    firstNameCol.setMinWidth(100);
    firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
    firstNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
    firstNameCol.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Person, String>>() {
        @Override
        public void handle(TableColumn.CellEditEvent<Person, String> t) {
            t.getRowValue().setFirstName(t.getNewValue());
        }
    });

    return firstNameCol;
}

private TableColumn createLastNameColumn() {
    Callback<TableColumn, TableCell> editableFactory = new Callback<TableColumn, TableCell>() {
        @Override
        public TableCell call(TableColumn p) {
            return new EditingCell();
        }
    };

    TableColumn lastNameCol = new TableColumn("Last Name");
    lastNameCol.setMinWidth(100);
    lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
    lastNameCol.setCellFactory(editableFactory);
    lastNameCol.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Person, String>>() {
        @Override
        public void handle(TableColumn.CellEditEvent<Person, String> t) {
          System.out.println( "Commiting last name change. Previous: " + t.getOldValue() + "   New: " + t.getNewValue() );
            t.getRowValue().setLastName(t.getNewValue());
        }
    });

    return lastNameCol;
}   

private TableColumn createfullNameColumn() {
    TableColumn firstNameCol = new TableColumn("full Name");
    firstNameCol.setMinWidth(100);
    firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("fullName"));
    firstNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
    firstNameCol.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Person, String>>() {
        @Override
        public void handle(TableColumn.CellEditEvent<Person, String> t) {
            t.getRowValue().setfullName(t.getNewValue());
        }
    });

    return firstNameCol;

}
}

Editing Cell :

public class EditingCell extends TableCell<Person, String> {
private TextField textField;

public EditingCell() {
}

@Override
public void startEdit() {
    super.startEdit();

    if( textField == null ) {
        createTextField();
    }
    setText(null);
    setGraphic(textField);
    textField.selectAll();
}

@Override
public void cancelEdit() {
    super.cancelEdit();
    setText((String) getItem());
    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(new ChangeListener<Boolean>() {
        @Override
        public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2) {
            if (!arg2) { commitEdit(textField.getText()); }
        }
    });

    textField.setOnKeyReleased(new EventHandler<KeyEvent>() {
        @Override
        public void handle(KeyEvent t) {
            if (t.getCode() == KeyCode.ENTER) {
                String value = textField.getText();
                if (value != null) { commitEdit(value); } else { commitEdit(null); }
            } else if (t.getCode() == KeyCode.ESCAPE) {
                cancelEdit();
            }
        }
    });
}

private String getString() {
    return getItem() == null ? "" : getItem().toString();
}
}

Person Class:

import javafx.beans.property.SimpleStringProperty;

public class Person {
private  SimpleStringProperty firstName;
private  SimpleStringProperty lastName;
private  SimpleStringProperty fullName;



public Person(String firstName, String lastName ,String fullName) {

    this.firstName = new SimpleStringProperty(firstName);
    this.lastName = new SimpleStringProperty(lastName);
    this.fullName=new SimpleStringProperty(fullName);


}

public String getFirstName() { return firstName.get(); }
public void setFirstName(String firstName) { this.firstName.set(firstName); }
public SimpleStringProperty firstNameProperty() { return firstName; }

public String getLastName() { return lastName.get(); }
public void setLastName(String lastName) { this.lastName.set(lastName); }
public SimpleStringProperty lastNameProperty() { return lastName; }

public String getfullName() { return fullName.get(); }
public void setfullName(String lastName) { this.fullName.set(lastName); }
public SimpleStringProperty fullNameProperty() { return fullName; }

}

Question: How to Update fullName Column when I update firstName Column or Lastname Column (editable cells) without insert a row?

1
1
10/2/2013 7:09:46 AM

Accepted Answer

Use binding to derive the full name from first name and the last name.

fullName.bind(Bindings.concat(this.firstName, " ", this.lastName));

Person.java

import javafx.beans.binding.Bindings;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleStringProperty;

public class Person {
    private SimpleStringProperty firstName;
    private SimpleStringProperty lastName;
    private ReadOnlyStringWrapper fullName = new ReadOnlyStringWrapper();

    public Person(String firstName, String lastName) {
        this.firstName = new SimpleStringProperty(firstName);
        this.lastName  = new SimpleStringProperty(lastName);

        fullName.bind(Bindings.concat(this.firstName, " ", this.lastName));
    }

    public String getFirstName() {
        return firstName.get();
    }

    public void setFirstName(String firstName) {
        this.firstName.set(firstName);
    }

    public SimpleStringProperty firstNameProperty() {
        return firstName;
    }

    public String getLastName() {
        return lastName.get();
    }

    public void setLastName(String lastName) {
        this.lastName.set(lastName);
    }

    public SimpleStringProperty lastNameProperty() {
        return lastName;
    }

    public String getFullName() {
        return fullName.get();
    }

    public ReadOnlyStringProperty fullNameProperty() {
        return fullName.getReadOnlyProperty();
    }
}

Redefine your createfullNameColumn() function. Now that fullName that it is a derived value from concatenating the firstName and the lastName, there is no need to allow a user to explicitly edit it.

private TableColumn createfullNameColumn() {
  TableColumn fullNameCol = new TableColumn("Full Name");
  fullNameCol.setMinWidth(100);
  fullNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("fullName"));

  return fullNameCol;
}

SampleFX1.java

Application code:

import javafx.application.Application;
import javafx.collections.*;
import javafx.event.EventHandler;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.control.cell.*;
import javafx.stage.Stage;
import javafx.util.Callback;

public class SampleFX1 extends Application {
    private TableView table = new TableView();
    private final ObservableList<Person> data =
            FXCollections.observableArrayList(
                    new Person("balu", "Smith"),
                    new Person("Isabella", "john"),
                    new Person("Ethan", "Williams"),
                    new Person("Emma", "Jones"),
                    new Person("Michael", "Brown")
            );

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

    @Override
    public void start(Stage stage) {
        Scene scene = new Scene(new Group());

        TableColumn firstNameCol = createSimpleFirstNameColumn();
        TableColumn lastNameCol = createLastNameColumn();
        TableColumn fullNameCol = createFullNameColumn();
        table.setItems(data);
        table.getColumns().addAll(firstNameCol, lastNameCol, fullNameCol);
        table.setEditable(true);

        ((Group) scene.getRoot()).getChildren().addAll(table);
        stage.setScene(scene);
        stage.show();
    }

    private TableColumn createSimpleFirstNameColumn() {
        TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
        firstNameCol.setMinWidth(100);
        firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
        firstNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
        firstNameCol.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Person, String>>() {
            @Override
            public void handle(TableColumn.CellEditEvent<Person, String> t) {
                t.getRowValue().setFirstName(t.getNewValue());
            }
        });

        return firstNameCol;
    }

    private TableColumn createLastNameColumn() {
        Callback<TableColumn, TableCell> editableFactory = new Callback<TableColumn, TableCell>() {
            @Override
            public TableCell call(TableColumn p) {
                return new EditingCell();
            }
        };

        TableColumn lastNameCol = new TableColumn("Last Name");
        lastNameCol.setMinWidth(100);
        lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
        lastNameCol.setCellFactory(editableFactory);
        lastNameCol.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Person, String>>() {
            @Override
            public void handle(TableColumn.CellEditEvent<Person, String> t) {
                System.out.println("Commiting last name change. Previous: " + t.getOldValue() + "   New: " + t.getNewValue());
                t.getRowValue().setLastName(t.getNewValue());
            }
        });

        return lastNameCol;
    }

    private TableColumn createFullNameColumn() {
        TableColumn fullNameCol = new TableColumn("Full Name");
        fullNameCol.setMinWidth(100);
        fullNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("fullName"));

        return fullNameCol;
    }
}

EditingCell.java

(unchanged from question)


Update to answer additional user questions on the sample code

I was able to run createSimpleFirstNameColumn() even though there are plenty of warnings from eclipse, for example: TableColumn is a raw type. References to generic type TableColumn should be parameterized.

Original source code was adapted from the standard JavaFX TableView sample code, which at the time wasn't the best example of working with Generics. Generic type specification in Java is optional, but if you mix use an API which has generics specified, but don't supply the generic type information in the API usage, the compiler will issue warnings such as above. In general the warnings can be ignored or if you wish, though you do get a bit nicer type inference and compile time type checking if you explicitly specify type information.

Rather than just saying:

TableColumn firstNameCol = new TableColumn("First Name");

Warnings can be eliminated by explicitly specifying type information such as below:

TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");

I have updated the sample code above to use this notation in the createSimpleFirstNameColumn() function.

Note the above code uses a Java 7 diamond notation, so the minimum language compilation level of the program must be set to at least Java 7.

However, in createLastNameColumn() the line with return new EditingCell();gives me an error that EditingCell cannot be resolved to a type.

I do not get this error. You probably have not included the EditingCell class defined in the original question in your project.

3
5/23/2017 11:58:20 AM

I am unable to leave a comment but in answer to this revised question :

I was able to run createSimpleFirstNameColumn() even though there are plenty of warnings from eclipse, for example: TableColumn is a raw type. References to generic type TableColumn should be parameterized.

Make sure that you have the correct import i.e. import javafx.scene.control.TableColumn; and not anything from Java Swing.


Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon