0

我在让 JavaFX 2 TableView 处理我的数据中的空值时遇到了严重的问题。这么多麻烦实际上我已经整理了一个简单的问题演示,如下所示。

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

表的第 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;
    }
}
4

1 回答 1

2

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

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

于 2012-09-28T09:20:32.580 回答