java - TableVew - 抛出 KeyEvent 时编辑单元格

标签 java javafx focus tableview

我在 TableView 上有一个事件监听器,用于监听键盘事件。

 // Add event listener to table
 table.setOnKeyTyped(event -> {
        TablePosition<SimpleStringProperty, String> focusedCell = table.getFocusModel().getFocusedCell();
        if (focusedCell != null)
        {
            table.getItems().get(focusedCell.getRow()).set(event.getCharacter());
            table.edit(focusedCell.getRow(), focusedCell.getTableColumn());
        }
    });

当用户单击 Enter 或将焦点更改到另一个单元格时,我在使用新数据更新单元格时遇到问题。当您单击 Enter 或更改焦点时,单元格将变为空。我不知道为什么。如何保存数据并使用新数据更新单元格。

// Here is the full code.
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
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 TableViewEdit extends Application
{

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

    table.getColumns().add(this.createColumn());

    ObservableList<SimpleStringProperty> rowData = FXCollections.observableArrayList();
    //table.getItems().addAll(rowData);
    for (int j = 0; j < 10; j++)
    {
        rowData.add(new SimpleStringProperty(String.format("Cell [%d", j)));
    }

    table.setItems(rowData);

    table.setOnKeyTyped(event -> {
        TablePosition<SimpleStringProperty, String> focusedCell = table.getFocusModel().getFocusedCell();
        if (focusedCell != null)
        {
            table.getItems().get(focusedCell.getRow()).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<SimpleStringProperty, String> createColumn()
{
    TableColumn<SimpleStringProperty, String> col = new TableColumn<>("Column ");
    col.setCellValueFactory(cellData -> cellData.getValue());
    col.setCellFactory(column -> new EditCell());
    return col;
}

private static class EditCell extends TableCell<SimpleStringProperty, String>
{

    private final TextField textField = new TextField();

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

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

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

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

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

}

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

最佳答案

这些真的很棘手;我认为任何“行为相关”(即对用户输入使用react的标准控件)都很难改变,并且通常在 JavaFX 中得不到很好的支持。希望这是 API 中需要改进的一个领域...

似乎存在几个不同的问题。我认为 Enter 键发生的情况是,尽管这会在文本字段上生成一个 ActionEvent 并提交编辑等,但 keyTyped 事件仍然会传播回来到表格,使其重新进入编辑模式。解决这个问题的方法似乎是在 table 上使用 keyPressed 处理程序(尽管说实话,这感觉不太健壮)。

该代码依赖于表列上的默认 onEditCommit 处理程序来实际更改属性值。 onEditCommit 处理程序由默认表格单元格的 commitEdit 方法调用。在失去焦点时调用 commitEdit(...) 的问题在于,默认的 commitEdit 方法首先检查单元格是否处于编辑状态,如果不是,则不执行任何操作。看来,当单元格失去焦点时,它会在调用 focusProperty 监听器之前退出编辑状态,因此永远不会调用 onEditCommit 处理程序。 (顺便说一句,这也会阻止示例 13-11“单元格编辑的替代解决方案”(原文如此)在 JDK 8 u25(当前版本)中正常工作。)

我能看到的第二个问题的唯一修复是直接从 commitEdit(...) 方法更新属性。这要求单元格具有对该属性的引用,这破坏了单元格和单元格值之间的良好分离。

我使用通常的 Person 示例重写了该示例,并合并了这两个修复。这个例子运行得很好,尽管正如我所说,有些部分感觉好像不太健壮:

import java.util.function.Function;

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<Person> table = new TableView<>();

        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<Person, ?> pos = table.getFocusModel().getFocusedCell() ;
            if (pos != null) {
                table.edit(pos.getRow(), pos.getTableColumn());
            }
        });

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

    private TableColumn<Person, String> createColumn(String title, Function<Person, StringProperty> property) {
        TableColumn<Person, String> col = new TableColumn<>(title);
        col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));

        col.setCellFactory(column -> new EditCell(property));

        return col ;
    }

    private static class EditCell extends TableCell<Person, String> {

        private final TextField textField = new TextField();

        private final Function<Person, StringProperty> property ;

        EditCell(Function<Person, StringProperty> property) {
            this.property = property ;

            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);
            Person person = getTableView().getItems().get(getIndex()) ;
            StringProperty cellProperty = property.apply(person);
            cellProperty.set(text);
            setContentDisplay(ContentDisplay.TEXT_ONLY);
        }

    }

    public static class Person {
        private final StringProperty firstName = new SimpleStringProperty();
        private final StringProperty lastName = new SimpleStringProperty();
        private final StringProperty email = new SimpleStringProperty();

        public Person(String firstName, String lastName, String email) {
            setFirstName(firstName);
            setLastName(lastName);
            setEmail(email);
        }

        public final StringProperty firstNameProperty() {
            return this.firstName;
        }

        public final java.lang.String getFirstName() {
            return this.firstNameProperty().get();
        }

        public final void setFirstName(final java.lang.String firstName) {
            this.firstNameProperty().set(firstName);
        }

        public final StringProperty lastNameProperty() {
            return this.lastName;
        }

        public final java.lang.String getLastName() {
            return this.lastNameProperty().get();
        }

        public final void setLastName(final java.lang.String lastName) {
            this.lastNameProperty().set(lastName);
        }

        public final StringProperty emailProperty() {
            return this.email;
        }

        public final java.lang.String getEmail() {
            return this.emailProperty().get();
        }

        public final void setEmail(final java.lang.String email) {
            this.emailProperty().set(email);
        }


    }

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

关于java - TableVew - 抛出 KeyEvent 时编辑单元格,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28478525/

相关文章:

Android 上的 Java - 威胁?

同一窗口中的 Javafx 弹出窗口(如上下文菜单)

javascript - 禁用 WordPress 的 wp_attempt_focus()

java - 如何设置我的请求焦点事件监听器线程安全?

macos - javafx FileChooser Mac 操作系统 NSInternalInconsistencyException

backbone.js - 如何自动聚焦第一个主干表单输入字段?

java - 调试谷歌数据存储 : IllegalArgumentException: projectId must match the following pattern

java - 如何删除 nullPointerException

java - 映射如何更改键值

javascript - 将 arraylist 中的数据发送到 JavaScript 中的数组