我的想法是添加一个丢失焦点事件的监听器,但我无法找到一种访问输入值的方法?
spinner.focusedProperty().addListener((observable, oldValue, newValue) ->
{
//if focus lost
if(!newValue)
{
//somehow get the text the user typed in?
}
});
这是奇怪的行为,它似乎违反了GUI旋转按钮控件的约定。
spinner.focusedProperty().addListener((observable, oldValue, newValue) ->
{
//if focus lost
if(!newValue)
{
//somehow get the text the user typed in?
}
});
这是奇怪的行为,它似乎违反了GUI旋转按钮控件的约定。
很遗憾,Spinner并不如预期那样运作:在大多数操作系统中,它应该在焦点丢失时提交编辑后的值。更不幸的是,它没有提供任何配置选项来轻松地使其按照预期运作。
因此,我们必须手动在监听器中将值提交到focusedProperty。好消息是,Spinner已经有了执行此操作的代码-虽然它是私有的,但我们可以复制粘贴它。
/**
* c&p from Spinner
*/
private <T> void commitEditorText(Spinner<T> spinner) {
if (!spinner.isEditable()) return;
String text = spinner.getEditor().getText();
SpinnerValueFactory<T> valueFactory = spinner.getValueFactory();
if (valueFactory != null) {
StringConverter<T> converter = valueFactory.getConverter();
if (converter != null) {
T value = converter.fromString(text);
valueFactory.setValue(value);
}
}
}
// useage in client code
spinner.focusedProperty().addListener((s, ov, nv) -> {
if (nv) return;
//intuitive method on textField, has no effect, though
//spinner.getEditor().commitValue();
commitEditorText(spinner);
});
textField.commitValue()
我本来希望你能提交没有效果的值,但事实上,它只更新了textFormatter的值(最终版本!),如果有的话。即使您使用文本格式验证:textFormatter for validation在Spinner中不起作用。可能是由于缺少某些内部侦听器或者Spinner尚未更新到相对较新的api - 虽然没有深入研究。
更新
当更多地尝试 TextFormatter 时,我注意到格式化程序确保在失去焦点时进行提交:
当控件失去焦点或被提交时更新值(仅适用于TextField)
这确实像所述那样正常工作,我们可以添加监听器到格式化程序的valueProperty,以便在提交值时得到通知:
TextField field = new TextField();
TextFormatter fieldFormatter = new TextFormatter(
TextFormatter.IDENTITY_STRING_CONVERTER, "initial");
field.setTextFormatter(fieldFormatter);
fieldFormatter.valueProperty().addListener((s, ov, nv) -> {
// do stuff that needs to be done on commit
} );
提交触发器:
回到Spinner:我们可以利用格式化程序值的失去焦点提交行为来强制提交spinnerFactory的值。类似于以下内容:
// normal setup of spinner
SpinnerValueFactory factory = new IntegerSpinnerValueFactory(0, 10000, 0);
spinner.setValueFactory(factory);
spinner.setEditable(true);
// hook in a formatter with the same properties as the factory
TextFormatter formatter = new TextFormatter(factory.getConverter(), factory.getValue());
spinner.getEditor().setTextFormatter(formatter);
// bidi-bind the values
factory.valueProperty().bindBidirectional(formatter.valueProperty());
@kleopatra的方向是正确的,但复制粘贴的解决方案感觉很尴尬,而基于TextFormatter的解决方案对我完全没有用。因此,这里有一个更短的解决方案,它强制Spinner调用其私有的commitEditorText()方法:
spinner.focusedProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue) {
spinner.increment(0); // won't change value, but will commit editor
}
});
这是控件的标准行为,根据文档:
可编辑属性用于指定是否可以在Spinner编辑器中输入用户输入。如果editable为true,则一旦用户键入并按下Enter键,将接收用户输入。此时,将输入传递给SpinnerValueFactory转换器StringConverter.fromString(String)方法。从此调用返回的值(类型为T)然后发送到SpinnerValueFactory.setValue(Object)方法。如果该值有效,它将保持作为值。如果无效,则值工厂需要相应地做出反应并撤销此更改。
您可以使用键盘事件来监听并随时调用控件的编辑提交方法。
public void initialize(URL location, ResourceBundle resources) {
for (Field field : getClass().getDeclaredFields()) {
try {
Object obj = field.get(this);
if (obj != null && obj instanceof Spinner)
((Spinner) obj).focusedProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue) {
((Spinner) obj).increment(0);
}
});
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
我使用一种替代方法 - 在打字时实时更新。这是我的当前实现:
getEditor().textProperty().addListener { _, _, nv ->
// let the user clear the field without complaining
if(nv.isNotEmpty()) {
Double newValue = getValue()
try {
newValue = getValueFactory().getConverter().fromString(nv)
} catch (Exception e) { /* user typed an illegal character */ }
getValueFactory().setValue(newValue)
}
使用监听器应该可以解决问题。您可以通过下拉列表的编辑器访问输入的值:
spinner.getEditor().getText();
我使用了这种方法
public class SpinnerFocusListener<T> implements ChangeListener<Boolean> {
private Spinner<T> spinner;
public SpinnerFocusListener(Spinner<T> spinner) {
super();
this.spinner = spinner;
this.spinner.getEditor().focusedProperty().addListener(this);
}
@Override
public void changed(ObservableValue<? extends Boolean> observable,
Boolean oldValue, Boolean newValue) {
StringConverter<T>converter=spinner.getValueFactory().getConverter();
TextField editor=spinner.getEditor();
String text=editor.getText();
try {
T value=converter.fromString(text);
spinner.getValueFactory().setValue(value);
}catch(Throwable ex) {
editor.setText(converter.toString(spinner.getValue()));
}
}
}