编辑:在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
Integer
的NumberFormatException
时,不会令人惊讶地抛出该错误。但是我还发现该单元格中的数字然后被设置为0,这可能不是期望的结果。
但是以上这些让我感到很笨拙。
我看了ageCol
和ageCol.cellFactory
(因为它们可以从catch
块内部访问),但看不到任何更好和明显的东西。我还可以看到人们可以轻松获得Callback
(ageCol.cellFactory
),但是调用它需要参数cdf
,即CellDataFeatures
实例,您又必须将其存储在某个地方。
我确定Swing涉及某种验证器机制:即,在可以从编辑器组件(通过某些委托或其他方式)传输值之前,可以覆盖某些验证机制。但是,此IntegerStringConverter
似乎可以用作验证器,尽管在验证失败时似乎没有提供任何方法来还原为现有(“旧”)值。
是否有比上面显示的笨拙的机制?
我以为我会举一个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
...但是我对此并不陌生。