TableView doesn't commit values on focus lost event

后端 未结 4 1311
不思量自难忘°
不思量自难忘° 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:09

    I've run into the same issue and I solved it by combining these two code snippets:

    • https://gist.github.com/james-d/be5bbd6255a4640a5357
    • https://gist.github.com/abhinayagarwal/9383881

    Custom TableCell implementation

    public class EditCell extends TableCell {
        private final TextField textField = new TextField();
    
        // Converter for converting the text in the text field to the user type, and vice-versa:
        private final StringConverter converter;
    
        /**
         * Creates and initializes an edit cell object.
         * 
         * @param converter
         *            the converter to convert from and to strings
         */
        public EditCell(StringConverter converter) {
            this.converter = converter;
    
            itemProperty().addListener((obx, oldItem, newItem) -> {
                setText(newItem != null ? this.converter.toString(newItem) : null);
            });
    
            setGraphic(this.textField);
            setContentDisplay(ContentDisplay.TEXT_ONLY);
    
            this.textField.setOnAction(evt -> {
                commitEdit(this.converter.fromString(this.textField.getText()));
            });
            this.textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
                if (!isNowFocused) {
                    commitEdit(this.converter.fromString(this.textField.getText()));
                }
            });
            this.textField.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
                if (event.getCode() == KeyCode.ESCAPE) {
                    this.textField.setText(this.converter.toString(getItem()));
                    cancelEdit();
                    event.consume();
                } else if (event.getCode() == KeyCode.TAB) {
                    commitEdit(this.converter.fromString(this.textField.getText()));
                    TableColumn nextColumn = getNextColumn(!event.isShiftDown());
                    if (nextColumn != null) {
                        getTableView().getSelectionModel().clearAndSelect(getTableRow().getIndex(), nextColumn);
                        getTableView().edit(getTableRow().getIndex(), nextColumn);
                    }
                }
            });
        }
    
        /**
         * Convenience converter that does nothing (converts Strings to themselves and vice-versa...).
         */
        public static final StringConverter IDENTITY_CONVERTER = new StringConverter() {
    
            @Override
            public String toString(String object) {
                return object;
            }
    
            @Override
            public String fromString(String string) {
                return string;
            }
    
        };
    
        /**
         * Convenience method for creating an EditCell for a String value.
         * 
         * @return the edit cell
         */
        public static  EditCell createStringEditCell() {
            return new EditCell(IDENTITY_CONVERTER);
        }
    
        // set the text of the text field and display the graphic
        @Override
        public void startEdit() {
            super.startEdit();
            this.textField.setText(this.converter.toString(getItem()));
            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
            this.textField.requestFocus();
        }
    
        // revert to text display
        @Override
        public void cancelEdit() {
            super.cancelEdit();
            setContentDisplay(ContentDisplay.TEXT_ONLY);
        }
    
        // commits the edit. Update property if possible and revert to text display
        @Override
        public void commitEdit(T item) {
            // This block is necessary to support commit on losing focus, because the baked-in mechanism
            // sets our editing state to false before we can intercept the loss of focus.
            // The default commitEdit(...) method simply bails if we are not editing...
            if (!isEditing() && !item.equals(getItem())) {
                TableView table = getTableView();
                if (table != null) {
                    TableColumn column = getTableColumn();
                    CellEditEvent event = new CellEditEvent<>(table,
                            new TablePosition(table, getIndex(), column),
                            TableColumn.editCommitEvent(), item);
                    Event.fireEvent(column, event);
                }
            }
    
            super.commitEdit(item);
    
            setContentDisplay(ContentDisplay.TEXT_ONLY);
        }
    
        /**
         * Finds and returns the next editable column.
         * 
         * @param forward
         *            indicates whether to search forward or backward from the current column
         * @return the next editable column or {@code null} if there is no next column available
         */
        private TableColumn getNextColumn(boolean forward) {
            List> columns = new ArrayList<>();
            for (TableColumn column : getTableView().getColumns()) {
                columns.addAll(getEditableColumns(column));
            }
            // There is no other column that supports editing.
            if (columns.size() < 2) { return null; }
            int currentIndex = columns.indexOf(getTableColumn());
            int nextIndex = currentIndex;
            if (forward) {
                nextIndex++;
                if (nextIndex > columns.size() - 1) {
                    nextIndex = 0;
                }
            } else {
                nextIndex--;
                if (nextIndex < 0) {
                    nextIndex = columns.size() - 1;
                }
            }
            return columns.get(nextIndex);
        }
    
        /**
         * Returns all editable columns of a table column (supports nested columns).
         * 
         * @param root
         *            the table column to check for editable columns
         * @return a list of table columns which are editable
         */
        private List> getEditableColumns(TableColumn root) {
            List> columns = new ArrayList<>();
            if (root.getColumns().isEmpty()) {
                // We only want the leaves that are editable.
                if (root.isEditable()) {
                    columns.add(root);
                }
                return columns;
            } else {
                for (TableColumn column : root.getColumns()) {
                    columns.addAll(getEditableColumns(column));
                }
                return columns;
            }
        }
    }
    

    Controller

        @FXML
        private void initialize() {
            table.getSelectionModel().setCellSelectionEnabled(true);
            table.setEditable(true);
    
            table.getColumns().add(createColumn("First Name", Person::firstNameProperty));
            table.getColumns().add(createColumn("Last Name", Person::lastNameProperty));
            table.getColumns().add(createColumn("Email", Person::emailProperty));
    
            table.getItems().addAll(
                    new Person("Jacob", "Smith", "jacob.smith@example.com"),
                    new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
                    new Person("Ethan", "Williams", "ethan.williams@example.com"),
                    new Person("Emma", "Jones", "emma.jones@example.com"),
                    new Person("Michael", "Brown", "michael.brown@example.com")
            );
    
            table.setOnKeyPressed(event -> {
                TablePosition pos = table.getFocusModel().getFocusedCell() ;
                if (pos != null && event.getCode().isLetterKey()) {
                    table.edit(pos.getRow(), pos.getTableColumn());
                }
            });
        }
    
        private  TableColumn createColumn(String title, Function property) {
            TableColumn col = new TableColumn<>(title);
            col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
    
            col.setCellFactory(column -> EditCell.createStringEditCell());
            return col;
        }
    

提交回复
热议问题