Ответ 1
К сожалению, Spinner не ведет себя так, как ожидалось: в большинстве ОС он должен зафиксировать отредактированное значение при утере фокуса. Еще более неудачно, он не предоставляет какой-либо опции конфигурации, чтобы заставить его вести себя так, как ожидалось.
Итак, мы должны вручную зафиксировать значение в слушателе для focusProperty. С яркой стороны у Spinner уже есть код, который делает это - он частный, однако мы должны c & p it
/**
* 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()
который я ожидал бы... ну... зафиксировать значение, которое не имеет никакого эффекта. Он (final!) Реализован для обновления значения textFormatter, если он доступен. Не работает в Spinner, даже если вы используете textFormatter для проверки. Может быть, какой-то внутренний прослушиватель отсутствует, или счетчик, еще не обновленный до относительно нового api, - не копал, хотя.
Обновление
Во время воспроизведения немного больше с TextFormatter я заметил, что форматтер гарантирует фиксацию на focusLost:
Значение обновляется, когда элемент управления теряет фокус или он совершается (только для TextField)
Что действительно работает так документировано, что мы могли бы добавить слушателя к formatter 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
} );
Триггеры для фиксации:
- пользователь нажимает ENTER.
- контроль теряет фокус
- field.setText называется программным (это недокументированное поведение!)
Возвращаясь к счетчику: мы можем использовать это поведение 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());
Обратите внимание, что редактирование (ввод или программная замена/добавление/вставка текста) не вызывает фиксацию - поэтому это не может быть использовано, если требуется фиксация текста в тексте.