Javafx 2 处理单元格中的空值

Javafx 2 处理单元格中的空值,javafx-2,Javafx 2,我在获取JavaFX2 TableView来处理数据中的空值时遇到了严重问题。这么多的麻烦,事实上,我已经把这个问题的一个简单演示放在一起,如下所示 本质上,问题是我的一些数据可能是空的,而将空值强制为值(如空字符串)是无效的,它必须是空的。在我正在处理的实际代码中,我有一个空的日期值,为了保持示例的简单性,我在下面显示了一个空字符串 表的第1行和第2行具有空值。这两列不同,第一列显示TextFieldTableCell的行为,第二列显示可编辑单元格的my实现。两者都表现出同样的错误行为 目前的

我在获取JavaFX2 TableView来处理数据中的空值时遇到了严重问题。这么多的麻烦,事实上,我已经把这个问题的一个简单演示放在一起,如下所示

本质上,问题是我的一些数据可能是空的,而将空值强制为值(如空字符串)是无效的,它必须是空的。在我正在处理的实际代码中,我有一个空的日期值,为了保持示例的简单性,我在下面显示了一个空字符串

表的第1行和第2行具有空值。这两列不同,第一列显示TextFieldTableCell的行为,第二列显示可编辑单元格的my实现。两者都表现出同样的错误行为

目前的情况是:

  • 单击单元格以进入编辑模式
  • 在单元格中输入一个值
  • 按enter键提交编辑
  • 什么也没发生
  • 在步骤4中,我希望该列的onEditCommit处理程序能够被调用,但事实并非如此。查看javax.scene.control.TableCell的源代码后,由于CommittedIt的第一行,提交没有发生:

    if (! isEditing()) return;
    
    似乎因为单元格为空,编辑属性从未设置为true,尽管我承认我还没有跟踪所有代码,以了解为什么它从未被切换为true

    一如既往地感谢你的帮助


    范例

    主要应用

    package example;
    
    import javafx.application.Application;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.event.EventHandler;
    import javafx.scene.Group;
    import javafx.scene.Scene;
    import javafx.scene.control.TableCell;
    import javafx.scene.control.TableColumn;
    import javafx.scene.control.TableView;
    import javafx.scene.control.cell.PropertyValueFactory;
    import javafx.scene.control.cell.TextFieldTableCell;
    import javafx.stage.Stage;
    import javafx.util.Callback;
    
    public class NullCellEditingExample extends Application {
    
        private TableView table = new TableView();
        private final ObservableList<Person> data =
                FXCollections.observableArrayList( new Person(null, "Smith"), new Person("Isabella", null), 
                new Person("Ethan", "Williams"), new Person("Emma", "Jones"), new Person("Michael", "Brown"));
    
        public static void main(String[] args) {
            launch(args);
        }
    
        @Override
        public void start(Stage stage) {
            Scene scene = new Scene(new Group());
    
            TableColumn firstNameCol = createSimpleFirstNameColumn();
            TableColumn lastNameCol = createLastNameColumn();
            table.setItems(data);
            table.getColumns().addAll(firstNameCol, lastNameCol);
            table.setEditable(true);
    
            ((Group) scene.getRoot()).getChildren().addAll(table);
            stage.setScene(scene);
            stage.show();
        }
    
        private TableColumn createSimpleFirstNameColumn() {
            TableColumn firstNameCol = new TableColumn("First Name");
            firstNameCol.setMinWidth(100);
            firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
            firstNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
            firstNameCol.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Person, String>>() {
                @Override
                public void handle(TableColumn.CellEditEvent<Person, String> t) {
                    ((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).setFirstName(t.getNewValue());
                }
            });
    
            return firstNameCol;
        }
    
        private TableColumn createLastNameColumn() {
            Callback<TableColumn, TableCell> editableFactory = new Callback<TableColumn, TableCell>() {
                @Override
                public TableCell call(TableColumn p) {
                    return new EditingCell();
                }
            };
    
            TableColumn lastNameCol = new TableColumn("Last Name");
            lastNameCol.setMinWidth(100);
            lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
            lastNameCol.setCellFactory(editableFactory);
            lastNameCol.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Person, String>>() {
                @Override
                public void handle(TableColumn.CellEditEvent<Person, String> t) {
                    t.getRowValue().setLastName(t.getNewValue());
                }
            });
    
            return lastNameCol;
        }   
    }
    
    自从发帖后,我发现基本上是一样的。他们采取的方法是始终避免空值,这对于字符串(例如使用空字符串)来说是好的,但对于日期或其他数据类型来说是不可接受的,因为这些数据类型没有明显的“空”值

    解决方案是将值false传递到EditingCell.updateItem方法中的super.updateItem调用中。如果有人对完整的分析感兴趣的话,我已经整理了一份报告

    package example;
    
    import javafx.beans.value.ChangeListener;
    import javafx.beans.value.ObservableValue;
    import javafx.event.EventHandler;
    import javafx.scene.control.TableCell;
    import javafx.scene.control.TextField;
    import javafx.scene.input.KeyCode;
    import javafx.scene.input.KeyEvent;
    
    public class EditingCell extends TableCell<Person, String> {
    
        private TextField textField;
    
        public EditingCell() {
        }
    
        @Override
        public void startEdit() {
            super.startEdit();
            if( textField == null ) {
                createTextField();
            }
            setText(null);
            setGraphic(textField);
            textField.selectAll();
        }
    
        @Override
        public void cancelEdit() {
            super.cancelEdit();
            setText((String) getItem());
            setGraphic(null);
        }
    
        @Override
        public void updateItem(String item, boolean empty) {
            super.updateItem(item, empty);
            if (empty) {
                setText(null);
                setGraphic(null);
            } else {
                if (isEditing()) {
                    if (textField != null) {
                        textField.setText(getString());
                    }
                    setText(null);
                    setGraphic(textField);
                } else {
                    setText(getString());
                    setGraphic(null);
                }
            }
        }
    
        private void createTextField() {
            textField = new TextField(getString());
            textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
            textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2) {
                    if (!arg2) { commitEdit(textField.getText()); }
                }
            });
    
            textField.setOnKeyReleased(new EventHandler<KeyEvent>() {
                @Override
                public void handle(KeyEvent t) {
                    if (t.getCode() == KeyCode.ENTER) {
                        String value = textField.getText();
                        if (value != null) { commitEdit(value); } else { commitEdit(null); }
                    } else if (t.getCode() == KeyCode.ESCAPE) {
                        cancelEdit();
                    }
                }
            });
        }
    
        private String getString() {
            return getItem() == null ? "" : getItem().toString();
        }
    }
    
    package example;
    
    import javafx.beans.property.SimpleStringProperty;
    
    public class Person {
    
        private final SimpleStringProperty firstName;
        private final SimpleStringProperty lastName;
    
        public Person(String firstName, String lastName) {
            this.firstName = new SimpleStringProperty(firstName);
            this.lastName = new SimpleStringProperty(lastName);
        }
    
        public String getFirstName() {
            return firstName.get();
        }
    
        public void setFirstName(String firstName) {
            this.firstName.set(firstName);
        }
    
        public SimpleStringProperty firstNameProperty() {
            return firstName;
        }
    
        public String getLastName() {
            return lastName.get();
        }
    
        public void setLastName(String lastName) {
            this.lastName.set(lastName);
        }
    
        public SimpleStringProperty lastNameProperty() {
            return lastName;
        }
    }