Después de mucho luchar pudimos dar con el arreglo de esta incógnita que ciertos los usuarios de este sitio web han tenido. Si quieres aportar algún detalle puedes dejar tu conocimiento.
Solución:
Desafortunadamente, Spinner no se comporta como se esperaba: en la mayoría de los sistemas operativos, debería confirmar el valor editado en el foco perdido. Aún más desafortunado, no proporciona ninguna opción de configuración para que se comporte fácilmente como se esperaba.
Por lo tanto, tenemos que enviar manualmente el valor de un oyente a la propiedad enfocada. En el lado positivo, Spinner ya tiene un código para hacerlo; sin embargo, es privado, tenemos que copiarlo y copiarlo.
/**
* c&p from Spinner
*/
private void commitEditorText(Spinner spinner)
if (!spinner.isEditable()) return;
String text = spinner.getEditor().getText();
SpinnerValueFactory valueFactory = spinner.getValueFactory();
if (valueFactory != null)
StringConverter 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);
);
Tenga en cuenta que hay un método
textField.commitValue()
que hubiera esperado… bueno… cometer el valor, que no tiene ningún efecto. Es (¡final!) Implementado para actualizar el valor de textFormatter si está disponible. No funciona en el Spinner, incluso si usa un formateador de texto para la validación. Es posible que falte algún oyente interno o que el control giratorio aún no se haya actualizado a la API relativamente nueva; sin embargo, no probó.
Actualizar
Mientras jugaba un poco más con TextFormatter, noté que un formateador garantías para comprometerse en focusLost:
El valor se actualiza cuando el control pierde su foco o se confirma (solo TextField)
Lo que de hecho funciona como está documentado, de modo que podríamos agregar un oyente a valueProperty del formateador para recibir una notificación cada vez que se confirme el valor:
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
);
Disparadores para una confirmación:
- el usuario presiona ENTER
- el control pierde el foco
- field.setText se llama mediante programación (¡este es un comportamiento no documentado!)
Volviendo a la ruleta: podemos usar este comportamiento de commit-on-focusLost del valor de un formateador para forzar una confirmación en el valor de spinnerFactory. Algo como
// 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());
Tenga en cuenta que la edición (ya sea escribiendo o reemplazando/agregando/pegando texto mediante programación) no no activar una confirmación, por lo que no se puede usar si se necesita confirmación en el cambio de texto.
@kleopatra se dirigió a la dirección correcta, pero la solución de copiar y pegar se siente incómoda y la basada en TextFormatter no me funcionó en absoluto. Así que aquí hay uno más corto, que obliga a Spinner a llamar a su commitEditorText() privado como se desee:
spinner.focusedProperty().addListener((observable, oldValue, newValue) ->
if (!newValue)
spinner.increment(0); // won't change value, but will commit editor
);
Este es el comportamiento estándar para el control según la documentación:
La propiedad editable se usa para especificar si la entrada del usuario se puede escribir en el editor Spinner. Si es editable truela entrada del usuario se recibirá una vez que el usuario escriba y presione Enter key. En este punto, la entrada se pasa al método StringConverter.fromString(String) del convertidor SpinnerValueFactory. El valor devuelto de esta llamada (de tipo T) luego se envía al método SpinnerValueFactory.setValue(Object). Si el valor es válido, permanecerá como el valor. Si no es válido, Value Factory deberá reaccionar en consecuencia y revertir este cambio.
Tal vez podría usar un evento de teclado para escuchar y llamar a la confirmación de edición en el control sobre la marcha.
Acuérdate de que tienes la opción de glosar tu experiencia si encontraste tu aprieto en el momento cronométrico.