如果解析失败,取消表格单元格编辑的规范方法

问题描述 投票:-1回答:1

编辑:在James_D找到this answer并将TextFormatter设置为TextField之后,我首先投票决定关闭副本。首先,我发现(在TableView上下文中)方法TextFieldTableCell.forTableColumn()在开始编辑时实际上并没有绘制TextField,而是一个LabeledText,它没有成为TextInputControl的子类,因此没有有setTextFormatter()。其次,我想要更多……熟悉的东西。我可能在回答中提出了“规范”解决方案:让其他人来判断。


这是TableColumn中的TableView(全部为Groovy):

TableColumn<Person, String> ageCol = new TableColumn("Age")
ageCol.cellValueFactory = { cdf -> cdf.value.ageProperty() }

int oldAgeValue
ageCol.onEditStart = new EventHandler(){
    @Override
    public void handle( Event event) {
        oldAgeValue = event.oldValue
    }
}
ageCol.cellFactory = TextFieldTableCell.forTableColumn(new IntegerStringConverter() {
    @Override
    public Integer fromString(String value) {
        try {
            return super.fromString(value)
        }
        catch ( NumberFormatException e) {
            // inform user by some means...
            println "string could not be parsed as integer..."
            // ... and cancel the edit
            return oldAgeValue
        }
    }
})

摘录自Person类:

public class Person {
    private IntegerProperty age;
    public void setAge(Integer value) { ageProperty().set(value) }
    public Integer getAge() { return ageProperty().get() }
    public IntegerProperty ageProperty() {
        if (age == null) age = new SimpleIntegerProperty(this, "age")
        return age
    }
    ...

[没有开始编辑Handler,当我输入无法解析为String IntegerNumberFormatException时,不会令人惊讶地抛出该错误。但是我还发现该单元格中的数字然后被设置为0,这可能不是期望的结果。

但是以上这些让我感到很笨拙。

我看了ageColageCol.cellFactory(因为它们可以从catch块内部访问),但看不到任何更好和明显的东西。我还可以看到人们可以轻松获得CallbackageCol.cellFactory),但是调用它需要参数cdf,即CellDataFeatures实例,您又必须将其存储在某个地方。

我确定Swing涉及某种验证器机制:即,在可以从编辑器组件(通过某些委托或其他方式)传输值之前,可以覆盖某些验证机制。但是,此IntegerStringConverter似乎可以用作验证器,尽管在验证失败时似乎没有提供任何方法来还原为现有(“旧”)值。

是否有比上面显示的笨拙的机制?

java javafx tablecelleditor
1个回答
0
投票

我以为我会举一个LocalDate的例子,比Integer稍微有趣些。给定以下类别:

public class Person {
    ...

    private ObjectProperty<LocalDate> dueDate
    public void setDueDate(LocalDate value) { dueDateProperty().set(value) }
    public LocalDate getDueDate() { return dueDateProperty().get() }
    public ObjectProperty dueDateProperty() {
        if (dueDate == null) dueDate = new SimpleObjectProperty(this, "dueDate");
        return dueDate;
    }

然后您定义编辑器单元类(基于TextField:]

class DueDateEditingCell extends TableCell<Person, LocalDate> {
    private TextField textField

    @Override
    public void startEdit() {
        if (!isEmpty()) {
            super.startEdit()
            createTextField()
            text = null
            setGraphic( textField )
            textField.selectAll()
        }
    }

    @Override
    public void cancelEdit() {
        super.cancelEdit()
        setText( item.toString() )
        setGraphic( null )
    }

    @Override
    public void commitEdit( LocalDate dueDate ){
        super.commitEdit( dueDate )
    }


    @Override
    public void updateItem( LocalDate dueDate, boolean empty) {
        super.updateItem( dueDate, empty)
        if (empty) {
            setText(null)
            setGraphic(null)
        }
        else {
            if (isEditing()) {
                if (textField != null) {
                    textField.text = getString()
                }
                setText(null)
                setGraphic(textField)
            } else {
                setText(getString())
                setGraphic(null)
            }
        }
    }

    private void createTextField() {
        textField = new TextField(getString())
        textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2)
        // configure so that only a string containing the word "bubble" will be accepted...
        textField.setOnKeyReleased(new EventHandler<KeyEvent>() {
            @Override
            void handle(KeyEvent event) {
                if (event.code == KeyCode.ENTER) {
                    boolean acceptChange = true
                    LocalDate newLocalDate
                    try {
                        newLocalDate = LocalDate.parse(textField.text)
                    }catch( DateTimeParseException e ){
                        // message out to user somehow...
                        acceptChange = false
                    }
                    if (acceptChange)
                        commitEdit( newLocalDate )
                } else if (event.code == KeyCode.ESCAPE) {
                    cancelEdit()
                }
            }
        })
    }

    private String getString() {
        return getItem() == null ? "" : getItem().toString()
    }
}

然后将其应用于“到期日期”列:

TableColumn<Person, Object> dueDateCol = new TableColumn("Due Date")
dueDateCol.cellValueFactory = { p -> p.value.dueDateProperty() }
Callback<TableColumn, TableCell> dueDateCellFactory =
        new Callback<TableColumn, TableCell>() {
            public TableCell call(TableColumn p) {
                return new DueDateEditingCell()
            }
        }
dueDateCol.setCellFactory( dueDateCellFactory )

[这意味着,当您按Enter键时,如果未解析单元格中输入的字符串,因为LocalDate焦点保留在单元格中,而“错误日期字符串”保持不变,并且仍处于编辑状态。您可以通过选择其他内容来取消编辑,以使单元格失去焦点并返回到先前的值。您还可以通过按Escape键将其重置为现有值。

TextFormatter中使用this solution过滤器的问题是验证对于我的目的而言有点太细了,并且在进行更改之前会触发lambda(Java)(即闭包(Groovy))到显示的String,因此您最终需要验证错误的内容。也可以用setFormatter来设置StringConverter ...但是我对此并不陌生。

© www.soinside.com 2019 - 2024. All rights reserved.