style jfx java javafx spinner javafx-8

java - style - jfx spinner



Escribir manualmente el texto en JavaFX Spinner no actualiza el valor(a menos que el usuario presione ENTER) (6)

Parece que el control Spinner no actualiza un valor ingresado manualmente hasta que el usuario presione explícitamente la tecla Intro. Por lo tanto, podrían escribir un valor (no presionar enter) para salir del control y enviar el formulario, y el valor que se muestra en la ruleta NO es el valor de la ruleta, es el valor antiguo.

Mi idea fue agregar un oyente al evento de enfoque perdido, pero ¿no puedo ver una manera de obtener acceso al valor ingresado?

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

Este es un comportamiento extraño, parece ir en contra de la convención de un control de giro de GUI.


@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, lo que obliga a Spinner a llamar a commitEditorText () privado como se desee:

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


Aquí hay una variante mejorada de la solución de Sergio.

El método de inicialización adjuntará el código de Sergio a todos los Spinners en el controlador.

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(); } } }


Desafortunadamente, Spinner no se comporta como se esperaba: en la mayoría de los sistemas operativos, debe confirmar el valor editado en el foco perdido. Aún más desafortunado, no proporciona ninguna opción de configuración para hacer que se comporte como se espera.

Por lo tanto, tenemos que confirmar manualmente el valor en un agente de escucha para focusProperty. En el lado positivo, Spinner ya tiene un código que lo hace, es privado, sin embargo, tenemos que 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); });

Tenga en cuenta que hay un método

textField.commitValue()

lo que habría esperado ... bueno ... cometer el valor, que no tiene efecto. Es (¡final!) Implementado para actualizar el valor de textFormatter si está disponible. No funciona en el Spinner, incluso si utiliza un textFormatter para la validación . Puede que falte un interlocutor interno o que el spinner aún no haya sido actualizado a la API relativamente nueva.

Actualizar

Mientras jugaba un poco más con TextFormatter noté que un formateador garantiza comprometerse en focusLost:

El valor se actualiza cuando el control pierde su enfoque o se compromete (solo TextField)

Lo que de hecho funciona como se documenta, de manera que podríamos agregar un servicio de escucha a valueProperty del formateador para recibir una notificación cada vez que se confirma 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 un commit:

  • usuario pulsa ENTER
  • el control pierde el enfoque
  • field.setText se llama programáticamente (¡esto es un comportamiento no documentado!)

Volviendo al spinner: podemos usar este comportamiento de commit-on-focus-focus del valor de un formateador para forzar un commit 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 sustituyendo / agregando / pegando texto mediante programación) no activa un compromiso, por lo que no se puede usar si se necesita un cambio en el texto.


El uso de un oyente debería funcionar. Puede obtener acceso al valor ingresado a través del editor de la ruleta:

spinner.getEditor().getText();


Este es el comportamiento estándar para el control de acuerdo a la documentación:

La propiedad editable se usa para especificar si la entrada del usuario se puede escribir en el editor Spinner. Si editable es verdadero, la entrada del usuario se recibirá una vez que el usuario escriba y presione la tecla Intro. 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) se envía al método SpinnerValueFactory.setValue (objeto). Si el valor es válido, permanecerá como el valor. Si no es válido, el valor de fábrica deberá reaccionar en consecuencia y anular este cambio.

Quizás podría usar un evento de teclado para escuchar y llamar a la confirmación de edición en el control a medida que avanza.


Utilizo un enfoque alternativo: actualícelo en vivo mientras escribo. Esta es mi implementación actual:

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) }