2

I want to create a Cell factory that returns a TableCell that behaves exactly like TextFieldTableCell, with the following difference: When it loses focus, it commits the changes.

My code is very simple:

public final class TextFieldCellFactory<S, T> implements Callback<TableColumn<S, T>, TableCell<S, T>> {

    @Override
    public TableCell<S, T> call(TableColumn<S, T> p) {

        class EditingCell extends TextFieldTableCell {

            public EditingCell() {

                super();

                setConverter(new DefaultStringConverter());

                focusedProperty().addListener(new ChangeListener() {

                    @Override
                    public void changed(ObservableValue observable, Object oldValue, Object newValue) {

                        System.out.println("changed!");

                        System.out.println("getText() = " + getText());
                        System.out.println("textProperty() = " + textProperty().get());
                        System.out.println("getItem = " + getItem());
                    }
                });
            }

            @Override
            public void startEdit() {

                super.startEdit();
            }

            @Override
            public void cancelEdit() {

                super.cancelEdit();
            }

        }

        return new EditingCell();
    }
}

As you see I add a change listener in the focusedProperty. The problem is that the change method is not called (nothing is printed).

How can I get the desired behaviour? Thank you.

kleopatra
  • 51,061
  • 28
  • 99
  • 211
Minas Mina
  • 2,058
  • 3
  • 21
  • 35

1 Answers1

0

Basically, you have to register the listener with the textField's (not the cell's) focusedProperty. As the textfield is a private field of super, it's not directly accessible - you have to look it up once after it was added to the cell. That's when an edit was started for the first time:

private TextField myTextField;

@Override
public void startEdit() {
    super.startEdit();
    if (isEditing() && myTextField == null) {
        // most simple case, assuming that there is no graphic other than the field 
        // TBD: implement the general case: walk the tree and find the field
        myTextField = (TextField) getGraphic();
        myTextField.focusedProperty().addListener((e, old, nvalue) -> {
            if (!nvalue) {
                T edited = getConverter().fromString(myTextField.getText());
                commitEdit(edited);
            }

        });
    }
}

Some notes:

  • this is a workaround around an open issue (vote for it!)
  • since jdk8, it's not entirely functional: won't commit if you click somewhere else inside the table
  • a recent answer uses a binding approach which might or not be fully functional (didn't test)
Community
  • 1
  • 1
kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • I found the solution - look at the first answer in this post: http://stackoverflow.com/questions/23632884/how-to-commit-when-clicking-outside-an-editable-tableview-cell-in-javafx/25118925#25118925 – Minas Mina Aug 07 '14 at 06:23
  • @user92834 your answer is what I referenced in the last bullet of my notes :-) Anyway, it behaves a bit quirkily - see my comment to your answer. – kleopatra Aug 07 '14 at 09:08