处理单元格中的空值



我在获取JavaFX 2 TableView以处理数据中的空值时遇到了严重问题。太麻烦了,事实上,我已经为这个问题做了一个简单的演示,如下所示。

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

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

目前的行为是:

  1. 单击单元格进入编辑模式
  2. 在单元格中输入值
  3. 按enter键提交编辑
  4. 什么都没发生

在步骤4中,我希望调用列的onEditCommit处理程序,但它没有。查看javax.scene.control.TableCell的源代码后,由于commitEdit的第一行是:,所以没有进行提交

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;
}   
}

编辑单元格

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;
}
}

自从发布后,我发现了另一个基本相同的问题。他们采取的方法总是避免空值,这对字符串来说很好(例如使用空字符串),但对没有明显"空"值的日期或其他数据类型来说是不可接受的。

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

最新更新