10.6 Mejoras prácticas
1. Propiedades estilizables¶
Se puede diseñar una propiedad JavaFX a través de css usando StyleableProperty. Esto es útil cuando los controles necesitan propiedades que se pueden configurar a través de css.
Para usar StyleableProperty en un Control, se necesita crear un nuevo CssMetaData usando StyleableProperty. Los CssMetaData creados para un control deben agregarse a List<CssMetaData> obtenidos del antecesor del control. Esta nueva lista luego se devuelve desde el archivo getControlCssMetaData().
Por convención, las clases de control que tienen CssMetaData implementarán un método estático getClassCssMetaData() y es habitual que getControlCssMetaData() simplemente devuelva getClassCssMetaData(). El propósito de getClassCssMetaData() es permitir que las subclases incluyan fácilmente los CssMetaData de algún antepasado.
La creación de StyleableProperty y CssMetaData necesita una gran cantidad de código repetitivo y esto se puede reducir mediante el uso de StyleablePropertyFactory . StyleablePropertyFactory contiene métodos para crear StyleableProperty con los CssMetaData correspondientes.
2. Tareas¶
Ahora veremos cómo usar una tarea JavaFX para mantener la IU responsible. Es imperativo que cualquier operación que tarde más de unos pocos cientos de milisegundos se ejecute en un subproceso separado para evitar bloquear la interfaz de usuario. Una tarea concluye la secuencia de pasos en una operación de larga duración y proporciona devoluciones de llamada para los posibles resultados.
La clase Task también mantiene al usuario al tanto de la operación a través de propiedades que se pueden vincular a controles de interfaz de usuario como ProgressBars y Labels. El enlace actualiza dinámicamente la interfaz de usuario. Estas propiedades incluyen
runningProperty: si la tarea se está ejecutando o noProgressProperty: el porcentaje completado de una operación.messageProperty: texto que describe un paso en la operación
2.1. Demostración¶
Las siguientes capturas de pantalla muestran el funcionamiento de una aplicación de recuperación de HTML.
Ingresar una URL y presionar "Ir" iniciará una tarea JavaFX. Al ejecutarse, la tarea hará visible un HBox que contiene una barra de progreso y una etiqueta. ProgressBar y Label se actualizan a lo largo de la operación.
Cuando finaliza la recuperación, se invoca al metodo succeeded() y se actualiza la interfaz de usuario. Tenga en cuenta que la llamada a succeeded() se lleva a cabo en el subproceso FX, por lo que es seguro manipular los controles.
Si hubo un error al recuperar el HTML, se invoca a failed() y se muestra una alerta de error. failed() también tiene lugar en el subproceso FX. Esta captura de pantalla muestra una entrada no válida. Se usa una "h" en la URL en lugar de "http".
2.2. Código¶
Se coloca un controlador de eventos en el botón Obtener HTML que crea la tarea. El punto de entrada de la Tarea es el método call() que comienza llamando a updateMessage() y updateProgress(). Estos métodos se ejecutan en el subproceso FX y generarán actualizaciones en cualquier propiedad enlazada.
El programa continúa emitiendo un HTTP GET usando clases estándar de java.net. Se crea una cadena "retval" a partir de los caracteres recuperados. Las propiedades de mensaje y progreso se actualizan con más llamadas a updateMessage() y updateProgress(). El método call() finaliza con la devolución de la cadena que contiene el texto HTML.
En una operación exitosa, se invoca la devolución de llamada de éxito (). getValue() es un método de tarea que devolverá el valor acumulado en la tarea (recuerde "retval"). El tipo del valor es lo que se proporciona en el argumento genérico, en este caso "String". Esto podría ser un tipo complejo como un objeto de dominio o una colección. La operación de éxito () se ejecuta en el subproceso FX, por lo que la cadena getValue () se establece directamente en el área de texto.
Si la operación falla, se lanza una excepción. La excepción es capturada por la tarea y convertida en una llamada fallida(). fail() también es seguro para subprocesos FX y muestra una alerta.
Tenga en cuenta que la tarea no actualiza la barra de progreso y la etiqueta de estado directamente. En su lugar, Task realiza llamadas seguras a updateMessage() y updateProgress(). Para actualizar la interfaz de usuario, se utiliza el enlace JavaFX en las siguientes declaraciones.
Task.runningProperty es un valor booleano que se puede vincular a bottomControls HBox visibleProperty. Task.progressProperty es un doble que se puede vincular a ProgressBarprogressProperty. Task.messageProperty es una cadena que se puede vincular a la etiqueta de estado textProperty.
Para ejecutar la tarea, cree un subproceso que proporcione la tarea como argumento del constructor e invoque start().
Para cualquier operación de ejecución prolongada (archivo IO, la red), use una tarea JavaFX para mantener la capacidad de respuesta de su aplicación. La tarea JavaFX le brinda a su aplicación una forma consistente de manejar operaciones asincrónicas y expone varias propiedades que se pueden usar para eliminar la lógica repetitiva y de programación.
2.3. Código completo¶
El código se puede probar en un solo archivo .java.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | |
3. Evitar Nulos en ComboBoxes¶
Para usar a ComboBoxen JavaFX, declare una Lista de elementos y establezca un valor inicial usando setValue(). El ComboBoxmétodo getValue() recupera el valor seleccionado actualmente. Si no se proporciona un valor inicial, el control tiene un valor nulo predeterminado.
El valor nulo es un problema cuando ComboBoximpulsa otra lógica como una transformación a mayúsculas o la búsqueda de un registro de base de datos. Si bien generalmente se usa una verificación nula para evitar este tipo de error, se prefiere un objeto vacío para simplificar el código. Los cuadros combinados a menudo aparecen en grupos y la técnica de objetos vacíos reduce las comprobaciones nulas en la interacción de los cuadros combinados relacionados y en las operaciones de guardar y cargar.
Este artículo presenta un par de ComboBoxes relacionados. Una selección de país en uno ComboBoxmodifica la lista de elementos de ciudad disponibles en un segundo ComboBox. No se requiere ninguna selección. El usuario puede presionar Guardar Buttonen cualquier momento y, si no se realiza ninguna selección ComboBox, se devolverá un objeto vacío, en este caso una Cadena vacía.
Esta es una captura de pantalla de la aplicación. Si selecciona "Suiza" de un valor inicial vacío, la ciudad se llenará ComboBoxde ciudades suizas. Seleccionando la ciudad "Zurich" y presionando Guardar recuperará esos valores.

Figura 64. Cuadros combinados relacionados
3.1. Estructura de datos¶
Las estructuras de datos que soportan la aplicación son una Lista de países y un Mapa de ciudades. El Mapa de ciudades utiliza el país como clave.
NoNullComboApp.clase
Para recuperar el conjunto de ciudades de un país determinado, utilice el método get() del Mapa. El método containsKey() se puede utilizar para determinar si el mapa contiene o no un valor para el país especificado. En este ejemplo, containsKey() se usará para manejar el caso del objeto vacío.
3.2. interfaz de usuario¶
La interfaz de usuario es un par de cuadros combinados con etiquetas y un botón Guardar. Los controles se colocan en a VBoxy justificados a la izquierda. El VBoxestá envuelto en un TilePaney centrado. Se TilePaneutilizó ya que no se estira VBoxhorizontalmente.
NoNullComboApp.clase
3.3. Valores iniciales¶
Como se mencionó anteriormente, si no se especifica un valor para un ComboBox, se devolverá un valor nulo en una llamada a getValue(). Aunque existen varias técnicas defensivas (si se verifica, métodos Commons StringUtils) para defenderse de NullPointerExceptions, es mejor evitarlas por completo. Esto es especialmente cierto cuando las interacciones se vuelven complejas o hay varios ComboBoxes que permiten selecciones vacías.
NoNullComboApp.clase
En esta aplicación, el país ComboBoxno se cambiará, por lo que sus elementos se agregan en el método start(). El país comienza con una selección inicial vacía al igual que la ciudad. Ciudad, en este punto, contiene un único elemento vacío.
3.4. Interacción¶
Cuando se cambia el valor del país, se ComboBoxdebe reemplazar el contenido de la ciudad. Es común usar clear() en la lista de respaldo; sin embargo, esto producirá un valor nulo en ComboBox(sin elementos, sin valor). En su lugar, use removeIf() con una cláusula para mantener un único elemento vacío. Con la lista limpia de todos los datos (excepto el elemento vacío), los contenidos recién seleccionados se pueden agregar con addAll().
NoNullComboApp.clase
La acción del botón Guardar imprimirá los valores. En ningún caso se devolverá un valor nulo desde getValue().
Si es un desarrollador de Java, ha escrito "si no es nulo" miles de veces. Sin embargo, proyecto tras proyecto, veo NullPointerExceptions que resaltan los casos que se perdieron o las nuevas condiciones que surgieron. Este artículo presentó una técnica para mantener objetos vacíos en ComboBoxes estableciendo un valor inicial y usando removeIf() en lugar de clear() al cambiar listas. Aunque este ejemplo usó objetos String, esto se puede expandir para trabajar con objetos de dominio que tienen una implementación hashCode/equals, una representación de objeto vacía y cellFactory o toString() para producir una vista vacía.
3.5. Código completo¶
El código se puede probar en un solo archivo .java.
NoNullComboApp.clase