TableView doesn't commit values on focus lost event

后端 未结 4 1313
不思量自难忘°
不思量自难忘° 2020-11-29 09:41

I\'d like to create a table with the following features:

  • Edit on key press
  • Enter key = next row
  • Tab key = next column
  • Escape key = c
4条回答
  •  春和景丽
    2020-11-29 10:15

    My proposal to solve this atrocity is the following (sorry for missing JavaDoc).

    This is a cancel-to-commit redirection solution. I tested it under LINUX with Java 1.8.0-121. Here, the only way how to discard a cell editor is to press ESCAPE.

    import javafx.beans.binding.Bindings;
    import javafx.scene.Node;
    import javafx.scene.control.ContentDisplay;
    import javafx.scene.control.TableCell;
    import javafx.scene.input.KeyCode;
    import javafx.scene.input.KeyEvent;
    
    public abstract class AutoCommitTableCell extends TableCell
    {
        private Node field;
        private boolean startEditing;
        private T defaultValue;
    
    
        /** @return a newly created input field. */
        protected abstract Node newInputField();
    
        /** @return the current value of the input field. */
        protected abstract T getInputValue();
    
        /** Sets given value to the input field. */
        protected abstract void setInputValue(T value);
    
        /** @return the default in case item is null, must be never null, else cell will not be editable. */
        protected abstract T getDefaultValue();
    
        /** @return converts the given value to a string, being the cell-renderer representation. */
        protected abstract String inputValueToText(T value);
    
    
        @Override
        public void startEdit() {
            try {
                startEditing = true;
    
                super.startEdit();  // updateItem() will be called
    
                setInputValue(getItem());
            }
            finally {
                startEditing = false;
            }
        }
    
        /** Redirects to commitEdit(). Leaving the cell should commit, just ESCAPE should cancel. */
        @Override
        public void cancelEdit() {
            // avoid JavaFX NullPointerException when calling commitEdit()
            getTableView().edit(getIndex(), getTableColumn());
    
            commitEdit(getInputValue());
        }
    
        private void cancelOnEscape() {
            if (defaultValue != null)    {   // canceling default means writing null
                setItem(defaultValue = null);
                setText(null);
                setInputValue(null);
            }
            super.cancelEdit();
        }
    
        @Override
        protected void updateItem(T newValue, boolean empty) {
            if (startEditing && newValue == null)
                newValue = (defaultValue = getDefaultValue());
    
            super.updateItem(newValue, empty);
    
            if (empty || newValue == null) {
                setText(null);
                setGraphic(null);
            }
            else {
                setText(inputValueToText(newValue));
                setGraphic(startEditing || isEditing() ? getInputField() : null);
            }
        }
    
        protected final Node getInputField()    {
            if (field == null)    {
                field = newInputField();
    
                // a cell-editor won't be committed or canceled automatically by JFX
                field.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
                    if (event.getCode() == KeyCode.ENTER || event.getCode() == KeyCode.TAB)
                        commitEdit(getInputValue());
                    else if (event.getCode() == KeyCode.ESCAPE)
                        cancelOnEscape();
                });
    
                contentDisplayProperty().bind(
                        Bindings.when(editingProperty())
                            .then(ContentDisplay.GRAPHIC_ONLY)
                            .otherwise(ContentDisplay.TEXT_ONLY)
                    );
            }
            return field;
        }
    }
    

    You can extend this class to support any data type.

    Example for a String field is (Person is an example bean):

    import javafx.scene.Node;
    import javafx.scene.control.TextField;
    import jfx.examples.tablebinding.PersonsModel.Person;
    
    public class StringTableCell extends AutoCommitTableCell
    {
        @Override
        protected String getInputValue() {
            return ((TextField) getInputField()).getText();
        }
    
        @Override
        protected void setInputValue(String value) {
            ((TextField) getInputField()).setText(value);
        }
    
        @Override
        protected String getDefaultValue() {
            return "";
        }
    
        @Override
        protected Node newInputField() {
            return new TextField();
        }
    
       @Override
        protected String inputValueToText(String newValue) {
            return newValue;
        }
    }
    

    To be applied in this way:

    final TableColumn nameColumn = new TableColumn("Name");
    nameColumn.setCellValueFactory(
            cellDataFeatures -> cellDataFeatures.getValue().nameProperty());
    nameColumn.setCellFactory(
            cellDataFeatures -> new StringTableCell());
    

提交回复
热议问题