TableView - Изменить сфокусированную ячейку

У меня есть прослушиватель событий, который прослушивает события клавиатуры. Когда я пытаюсь войти в режим редактирования с помощью ключевого события, по какой-то странной причине в режим редактирования входит неправильная ячейка.

Например, я хочу изменить ячейку. Я использую стрелки клавиатуры, чтобы перейти к ячейке, которую я хочу отредактировать, то есть к ячейке, которая находится в фокусе. Щелкнув букву на клавиатуре, выделенная ячейка должна войти в режим редактирования. Когда я пытаюсь изменить выделенную ячейку, неправильная ячейка переходит в режим редактирования.

private final class EditCell extends TableCell<SimpleStringProperty, String> implements GenericTable
{

    public EditCell()
    {
        // Add event listsner. table is a TableView
        table.setOnKeyPressed(keyEvent -> this.handleKeyPressed(keyEvent));
    }

    public void handleKeyPressed(KeyEvent key)
    {
        // Keyboard events
        if (key.getCode().isLetterKey())
        {
            if (!this.isEditing())
            {

                this.edit = true;

                // focus index
                int focusIndex = this.table.getSelectionModel().getFocusedIndex();

                this.changeTableCellFocus(this.table, focusIndex);

                this.startEdit();
            }
        }
    }


    // startEdit() function
    @Override
    public void startEdit()
    {
        if (this.edit)
        {
            LOGGER.info("Start editing on cell index: " + this.getIndex());
            super.startEdit();
            this.createTextField();
            this.setText(null);
            this.setGraphic(this.textField);
            this.textField.selectAll();
            this.textField.requestFocus();

            this.textField.setOnKeyPressed(keyEvent -> this.handleKeyPressed(keyEvent));

            this.textField.focusedProperty()
                    .addListener((observable, oldValue, newValue) -> this.onTextFieldFocusChange(observable,
                            oldValue,
                            newValue));
        }
    }

    // Change focus
    public void changeTableCellFocus(final TableView<?> table, final int focusIndex)
    {
        table.requestFocus();
        table.getSelectionModel().clearAndSelect(focusIndex);
        table.getFocusModel().focus(focusIndex);
    }
}

Перед входом в режим редактирования я переключаю фокус на выбранную ячейку, а затем вызываю метод startEdit(). Я попытался отладить проблему, но безуспешно. Я заметил, что focusIndex отличается от текущего индекса ячейки. Я не уверен, почему индекс отличается.


person breaktop    schedule 11.02.2015    source источник
comment
Можете ли вы дать этому коду некоторый контекст? Это в подклассе TableCell?   -  person James_D    schedule 11.02.2015
comment
Вы пытались использовать выделение вместо фокуса? Я предполагаю, что редактирование таблицы основано на выборе, а не на фокусе.   -  person eckig    schedule 11.02.2015
comment
Функция changeTableCellFocus(...) выбирает ячейку + переводит фокус на ячейку. Вы имеете в виду, просто выберите ячейку, а затем вызовите startEdit(). Я думаю, проблема в том, что индекс текущей ячейки отличается от индекса фокусной ячейки, что означает, что даже если вы измените выбранную ячейку на сфокусированную ячейку индекса, у вас все равно будет проблема, так как в текущей ячейке вызывается startEdit().   -  person breaktop    schedule 11.02.2015
comment
Почему выбранный индекс ячейки отличается от текущего индекса ячейки.   -  person breaktop    schedule 11.02.2015
comment
Все созданные ячейки вызывают table.setOnKeyPressed(...). onKeyPressed — это свойство, поэтому каждое из них переопределяет предыдущий вызов. Следовательно, обработчик onPressed для таблицы вызовет handleKeyPressed(...) для последней созданной ячейки (которая, очевидно, не обязательно является сфокусированной).   -  person James_D    schedule 11.02.2015
comment
Как бы вы это сделали, чтобы при запуске события клавиатуры сфокусированная ячейка переходила в режим редактирования.   -  person breaktop    schedule 11.02.2015


Ответы (1)


Проблема с вашим кодом в том, что каждая ячейка вызывает table.setOnKeyPressed(...) по мере ее создания. Это работает так же, как и любой другой метод set, поэтому обработчик keyPressed в таблице просто устанавливается на обработчик из последнего созданного EditCell. У вас нет контроля над фактическим созданием ячеек, и это не обязательно (и маловероятно) быть той ячейкой, которая оказывается сфокусированной.

У TableView достаточно API, чтобы вы могли управлять этим прямо из таблицы. Особенно

table.getFocusModel().getFocusedCell()

даст вам TablePosition, представляющий текущую сфокусированную ячейку. Отсюда вы можете получить соответствующий индекс строки и TableColumn. Затем вам просто нужно вызвать table.edit(int row, TableColumn<...> column);, чтобы указать соответствующей ячейке перейти в режим редактирования.

Вот полный пример. Я не прилагал особых усилий, чтобы сделать редактирование «красивым» с точки зрения выбора текста и т. д. в текстовом поле, и вы, возможно, захотите каким-то образом реализовать отмену редактирования, но это должно помочь вам начать.

import java.util.ArrayList;
import java.util.List;

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class TableViewEditOnType extends Application {


    @Override
    public void start(Stage primaryStage) {
        TableView<List<StringProperty>> table = new TableView<>();
        table.getSelectionModel().setCellSelectionEnabled(true);
        table.setEditable(true);

        for (int i = 0; i < 10; i++) {
            table.getColumns().add(createColumn(i));

            List<StringProperty> rowData = new ArrayList<>();
            table.getItems().add(rowData);
            for (int j = 0; j < 10 ; j++) {
                rowData.add(new SimpleStringProperty(String.format("Cell [%d, %d]", i, j)));
            }
        }

        table.setOnKeyTyped(event -> {
            TablePosition<List<StringProperty>, String> focusedCell = table.getFocusModel().getFocusedCell();
            if (focusedCell != null) {
                table.getItems().get(focusedCell.getRow()).get(focusedCell.getColumn()).set(event.getCharacter());
                table.edit(focusedCell.getRow(), focusedCell.getTableColumn());
            }
        });

        Scene scene = new Scene(new BorderPane(table), 880, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private TableColumn<List<StringProperty>, String> createColumn(int colIndex) {
        TableColumn<List<StringProperty>, String> col = new TableColumn<>("Column "+colIndex);
        col.setCellValueFactory(cellData -> cellData.getValue().get(colIndex));
        col.setCellFactory(column -> new EditCell());
        return col ;
    }

    private static class EditCell extends TableCell<List<StringProperty>, String> {

        private final TextField textField = new TextField();

        EditCell() {   
            textProperty().bind(itemProperty());
            setGraphic(textField);
            setContentDisplay(ContentDisplay.TEXT_ONLY);

            textField.setOnAction(evt -> commitEdit(textField.getText()));
            textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
                if (! isNowFocused) {
                    commitEdit(textField.getText());
                }
            });
        }

        @Override
        public void startEdit() {
            super.startEdit();
            textField.setText(getItem());
            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
            textField.requestFocus();          
        }

        @Override
        public void cancelEdit() {
            super.cancelEdit();
            setContentDisplay(ContentDisplay.TEXT_ONLY);
        }

        @Override
        public void commitEdit(String text) {
            super.commitEdit(text);
            setContentDisplay(ContentDisplay.TEXT_ONLY);
        }

    }

    public static void main(String[] args) {
        launch(args);
    }
}
person James_D    schedule 11.02.2015