在JavaFX Spinner中手动输入文本不会更新值(除非用户按下ENTER)

Jam*_*zba 24 java javafx spinner javafx-8

似乎Spinner控件在用户明确按Enter键之前不会更新手动键入的值.因此,他们可以键入一个值(不按回车键)退出控件,并提交表单,并且微调器中显示的值不是微调器的值,它是旧值.

我的想法是为丢失的焦点事件添加一个监听器,但我看不到一种获取输入值的方法?

spinner.focusedProperty().addListener((observable, oldValue, newValue) -> 
{
    //if focus lost
    if(!newValue)
    {
        //somehow get the text the user typed in?
    }
});
Run Code Online (Sandbox Code Playgroud)

这是奇怪的行为,它似乎违反了GUI微调控件的惯例.

kle*_*tra 29

不幸的是,Spinner的行为并不像预期的那样:在大多数操作系统中,它应该在焦点丢失时提交编辑后的值.更不幸的是,它没有提供任何配置选项来轻松使其按预期运行.

因此,我们必须手动将侦听器中的值提交给focusedProperty.从好的方面来看,Spinner已经有代码这样做 - 它是私有的,但是,我们必须对它进行c&p

/**
 * 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);
});
Run Code Online (Sandbox Code Playgroud)

请注意,有一种方法

textField.commitValue()
Run Code Online (Sandbox Code Playgroud)

我原本期望......嗯......提交价值,这没有效果.实现(final!)以更新textFormatter的值(如果可用).即使您使用textFormatter进行验证,也无法在Spinner中工作.可能是一些内部听众丢失或者旋转器还没有更新到相对较新的api - 但是没有挖掘.


更新

虽然有点绕多用的TextFormatter打我注意到一个格式化的保证承诺的focusLost:

当控件失去焦点或提交时,该值会更新(仅限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
} );
Run Code Online (Sandbox Code Playgroud)

提交的触发器:

  • 用户点击ENTER
  • 控制失去焦点
  • field.setText是以编程方式调用的(这是未记录的行为!)

回到微调器:我们可以使用formatter的值的commit-on-focusLost行为来强制提交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());
Run Code Online (Sandbox Code Playgroud)

需要注意的是编辑(无论是打字或以编程方式替换/添加/粘贴文本),并没有引发提交-所以这不能被使用,如果提交上文本变化是必要的.


Ser*_*gio 25

@kleopatra走向正确的方向,但复制粘贴解决方案感觉很尴尬,基于TextFormatter的解决方案根本不适合我.所以这里是一个较短的,它迫使Spinner根据需要调用它的私有commitEditorText():

spinner.focusedProperty().addListener((observable, oldValue, newValue) -> {
  if (!newValue) {
    spinner.increment(0); // won't change value, but will commit editor
  }
});
Run Code Online (Sandbox Code Playgroud)