Unidad 9 Interfaces gráficas de usuario. JavaFX. Argon2.1. Empezando con javafx1.1 Introducción1.2 Versiones1.3 Instalación o configuración2 . Proyecto JavaFX básico usando FXML3. Ejemplo de Creación de Calculadora Sencilla4. Aplicación con acceso a BD cifrando las contraseñas con Argon2Login Access MySQLMainLogin.fxmlControlador: LoginControllerComponentes de la vistaInterfaz InitializableEventos de BotónMétodo de validación de contraseñas con Argon2Registro de nuevos usuarios¿Y si somos administrador y listamos ?Listar usuariosJavaFX Observable CollectionsInformación adicional¿Cómo podemos cerrar un formulario?ESTO NO LO VAMOS A TRABAJAR EN CLASE5. LibretaDirecciones: Creación del proyecto y configuración5.1 Creación y configuración5.2. Creación del archivo FXML de diseño5.3 Diseño mediante Scene Builder5.4 La vista principal5.5 La clase principal5.6 El modelo Persona5.7 La lista de personas5.8 El controlador para la vista de personas5.9 La conexión de LibretaDirecciones con VistaPersonaController5.10 Vincular la vista al controlador6. LibretaDirecciones: Interacción con el usuario6.1 Mostrar detalles de una persona6.2 Trabajar con fechas6.3 Detectar cambios en la selección de la tabla6.4 Borrar una persona6.5 Gestión de errores6.6 Editar y añadir una personaVista EditarPersonaControlador EditarPersonaEnlazar vista y controladorAbrir la vista EditarPersona7. LibretaDirecciones: Hojas de estilo CSS7.1 Crear el archivo CSS7.2 Vincular vistas y estilosVistaPrincipal.fxmlEditarPersona.fxmlVistaPersona.fxmlIcono de aplicación

FechaVersiónDescripción
21/03/20221.0.0Versión inicial
05/04/20221.0.1TableView y Observables
20/03/20253.0.0Reconfigurando contenidos.
21/03/20253.0.1Argon2

Unidad 9 Interfaces gráficas de usuario. JavaFX. Argon2.

 

1. Empezando con javafx

 

1.1 Introducción

JavaFX es una plataforma de software para crear y entregar aplicaciones de escritorio, así como aplicaciones de Internet enriquecidas (RIA) que pueden ejecutarse en una amplia variedad de dispositivos. JavaFX está destinado a reemplazar Swing como la biblioteca de GUI estándar para Java SE. TI permite a los desarrolladores diseñar, crear, probar, depurar e implementar aplicaciones de cliente enriquecidas. La apariencia de las aplicaciones JavaFX se puede personalizar utilizando Hojas de estilo en cascada (CSS) para el estilo (ver JavaFX: CSS ) y (F) Los archivos XML se pueden usar para estructurar objetos, lo que facilita la creación o el desarrollo de una aplicación (consulte FXML y controladores ) . Scene Builder es un editor visual que permite la creación de archivos fxml para una UI sin escribir código.

1.2 Versiones

1.3 Instalación o configuración

Para JDK 11 y versiones posteriores, Oracle tiene JavaFX de código abierto, por lo tanto, a partir de este no se incluyen las librerías de javaFX en estos JDK por lo tanto es necesario añadirlas. Para facilitar este trabajo a la hora de desarrollar y crear nuestras aplicaciones, vamos a descargar un OpenJDK en su versión 21 que lo incluye. Se trata de la compañia BellSoft que desarrolla un OpenJDK que lo incluye, en concreto el bellsoft-jdk21.0.6+10-windows-amd64-full.msi (Este enlace te permitirá descargar la versión completa que nos hace falta, la Full JDK)

Una vez que lo hayamos descargado, lo instalamos. Cuando creemos un proyecto de JavaFX utilizaremos este compilador.

En cuanto a la herramienta para el diseño gráfico de aplicaciones, Scene Builder la descargamos de aquí: https://gluonhq.com/products/scene-builder/#download

 

2 . Proyecto JavaFX básico usando FXML

Este proyecto básico nos va a servir de ejemplo para presentar la herramienta que nos permitirá desarrollar una pequeña aplicación de escritorio con un entorno gráfico.

Además nos va a permitir definir las diferentes partes de la misma.

Lo primero que realizaremos es crear un proyecto con javaFX con las siguientes características:

Proyecto de tipo JavaFX, Languaje: Java, gestor de dependencias Maven y JDK esl liberica21 BellSoft

javaFX_1

Pulsamos a Next y veremos la opción de incorporar una serie de controles. De momento no vamos a hacer uso de ninguno de estos. Más tarde se podrían incorporar en el proyecto.

Pulsamos Create:

javaFX_2

Veremos que nos descargará con Maven las dependencias de JavaFX (pom.xml)

javaFX_3

 

Vemos que nos ha descompuesto la aplicación en un desarrollo MVC (Modelo Vista Controlador)

Aplicación principal

Fichero FXML

Controlador

Si lo compilamos y ejecutamos nos mostrará lo siguiente:

HelloWorld_3

 

Cómo funciona

Brevemente, en la clase de aplicación principal, FXMLLoader cargará hello-view.fxml desde el jar/classpath, como se especifica en FXMLLoader.load(getClass().getResource("hello-view.fxml").

Al cargar hello-view.fxml, el cargador encontrará el nombre de la clase de controlador, como se especifica en fx:controller="com.manu.helloworld.HelloController" en el FXML.

Luego, el cargador creará una instancia de esa clase, en la que intentará inyectar todos los objetos que tengan fx:id en el FXML y estén marcados con la anotación @FXML en la clase del controlador.

En esta muestra, FXMLLoader creará la etiqueta basada en <Label ... fx:id="label"/>, e inyectará la instancia de la etiqueta en la etiqueta privada @FXML;.

Finalmente, cuando se haya cargado todo el FXML, FXMLLoader llamará al método de inicialización del controlador y se ejecutará el código que registra un controlador de acción con el botón.

Edición

Si bien el archivo FXML se puede editar dentro del IDE, no se recomienda, ya que el IDE proporciona solo verificación de sintaxis básica y autocompletado, pero no guía visual.

El mejor enfoque es abrir el archivo FXML con Scene Builder, donde todos los cambios se guardarán en el archivo.

Podemos abrir el FXML con la opción que nos aporta Intellij

SceneBuilder_Intellij_1

 

Configuraremos el SceneBuilder instalado desde aquí:

SceneBuilder_Intellij_Configuring

 

 

 

Se pueden realizar cambios arrastrando nuevos contenedores o nuevos controles desde los paneles de la izquierda, y las propiedades y los valores de diseño se pueden cambiar en los paneles de la derecha.

Ten en cuenta que una de las etiquetas de identificación que permite inyectar FXML en el código Java es fx:id. Se puede configurar en el panel Código:

SceneBuilder_Intellij_2

Después de aplicar los cambios, guarde el archivo (Scene Builder -> Archivo -> Guardar). Si se realizan cambios editando el archivo desde el IDE, al guardar el archivo, se actualizarán en Scene Builder.

 

3. Ejemplo de Creación de Calculadora Sencilla

Este va ser un ejemplo de desarrollo de una pequeña aplicación gráfica que nos realizará operaciones sencillas.

Crearemos un proyecto llamado CalcuFX hacieno uso de JavaFX, Maven y la versión 21 del jdk.

Lo primero que haremos es redefinir la estructura de nuestro proyecto. Para ello renombraremos primero el formulario y le llamaremos calculadora. Haremos uso de la propiedad rename y refactorizamos para que se cambien las ocurrencias allí donde aparecen:

calculadora_1

La estructura que tendrás será la siguiente:

 

calculadora_2

La clase suma, dispondrá de los siguientes elementos:

 

Vamos a abrir con SceneBuilder el formulario calculadora.fxml

Dejaremos como Panel principal un AnchorPane a quien le incluiremos los siguientes elementos:

 

calculadora_3

 

Como podemos observar este es el pequeño diseño. El código del mismo es el siguiente:

 

Una vez tenemos la vista, el formulario fxml, definimos el controlador que hará uso del mismo. En nuestra calculadora se llama CalcuController y su código es el siguiente:

Cómo se puede observar, disponemos de una serie de decoradores, @FXML, estos son utilizados para especificar los elementos gráficos de los que hacemos uso en el formulario fxml.

También un método con un decorador:

Si nos damos cuenta este método sumar hace uso de un parámetro de tipo ActionEvent. Fíjate en el código de este, además, fíjate en el diseño de SceneBuilder, selecciona Button y luego en la parte derecha Code y On Action ...

calculadora_4

 

¿Qué observas en este?

Añade los botones de Restar, Multiplicar y Dividir y añádeles la funcionalidad.

 

4. Aplicación con acceso a BD cifrando las contraseñas con Argon2

Con esta aplicación vamos a aprender como podemos crear un par de formularios, ofuscar contraseñas con Argon2 y además acceder a una BD y mostrar la información con observables.

No se va a desarrollar una aplicación completa. Sólo se van a mostrar ciertas características del desarrollo gráfico con JavaFX haciendo uso de SceneBuilder para construir interfaces gráficas y Argon2 para el cifrado.

Para poder ver y entender una aplicación de este calado, deberemos entender que estamos haciendo con esta aplicación.

Login Access MySQL

Esta aplicación simplemente se va a encargar de permitir el acceso a una BD. Para ello dispondrá de un formulario inicial de acceso y luego transitar par más de formularios. Uno de ellos para dar de alta a nuevos usuarios y otro para listarlos.

El mecanismo es muy sencillo pero pero nos va a permitir aprender algunos conceptos de JavaFX. Además, se utilizará como se indicó anteriormente la encriptación, ofuscación, de las contraseñas.

 

Vamos a partir de la siguiente estructura:

LoginAccessMySQL

 

En el directorio database dispondrás del fichero de base de datos que permitirá tener una serie de usuarios y contraseñas ya ofuscadas.

El usuario demo y contraseña 12345 es el usuario con rol ADMIN_USER que más hacia delante veremos.

En el directorio images se guardan las imágenes utilizadas en la aplicación.

Dentro del directorio src/main/java/com.manu.loginaccessmysql se encuentra los controladores de la aplicación, es decir, la lógica de la misma.

En el Package DAO disponemos de la clase de conexión, ConexionDB.

En el Package DTO disponemos de la clase de la que hará uso la aplicación, Usuarios.

Dentro del Package Utils encontramos 2 clases. La primera de ellas es la que vamos a utilizar y con la que están implementado el programa. De la segunda no debemos hacer uso, aunque la tenemos para ver como podemos cifrar haciendo uso de métodos inseguros.

Dentro del directorio src/main/resources/com.manu.loginaccessmysql tendremos los formularios fxml que implementan las vistas de nuestra aplicación.

 

Main

Lo primero que podemos ver es que estamos extendiendo la clase Application y sobreescribimos el método start.

Disponemos de una variable privada estática que nos va a permitir mantener la escena principal.

A esta variable, le pasamos el parametro de la escena del método redefinido.

Cargamos el formulario de login con un New FXMLoader.

Creamos la escena y le cargamos el formulario con unas dimensiones de 520 de ancho por 400 de alto.

En el escenario, stage, le cambiamos el estilo, le damos un título y le cargamos la escena, es decir, el formulario. Una vez hecho esto, lo mostramos.

En el método main, lanzamos el método launch(); que lanza la aplicación.

El método getStage() nos devolverá el valor de la escena principal.

 

Login.fxml

Vamos a ver el contenido del mismo abriendo este con SceneBuilder:

Login_fxml

La construcción de este se basa en un contenedor principal, BorderPane, que contiene un primer AnchorPane, que representa el lado izquierdo del formulario, que dispone de un ImageView.

Un segundo AnchorPane, que representa el lado derecho del formulario, que contiene los siguientes componentes: ImageView, label, TextField, Label, PasswordField, Button, Button y Label.

El fxml es el siguiente:

Asociado a un formulario, fxml, siempre tenemos asociado un controlador. Lo podemos ver siempre en la opción Controller del fxml, bien directamente en la etiqueta dentro de este: fx:controller="com.manu.loginaccessmysql.LoginController o bien en SceneBuilder:

Login_fxml_controller

 

Controlador: LoginController

 

Componentes de la vista

Cuando estamos desarrollando la vista con Scene Builder, es decir, nuestro formulario fxml a aquellos elementos que vamos a referenciar en nuestra clase controlador le asociamos un nombre identificador en el campo fx:id que se encuentra en la sección de código.

Cuando se hace referencia a estos en el controlador debemos definir variables del tipo de componente al que vamos a referenciar e indicar como nombre de variable el identificador que le hemos dado en el fx:id

 

Interfaz Initializable

 

Podemos observar que lo primero que implementamos en nuestro controlador es la clase que además implementa el interfaz Initializable

En muchas ocasiones cuando definamos un controlador necesitaremos implementar el interfaz Initializable.

Este nos obligará a redefinir (@override) el método initialize.

¿ Nos es necesario ?

En pocas palabra podemos decir que si, básicamente porque primero se llama al constructor, luego se cargan los atributos definidos con @FXML. El constructor por lo tanto no tiene acceso a estos pero en cambio initialize si.

Un ejemplo que nos sirve de ayuda es el momento de cargar las imágenes que tenemos definidas por defecto en nuestro formulario fxml (nuestra vista). Si no las añadimos en el método initialize no se cargarán.

También podemos realizar cualquier otra operativa en el mismo, pues ya estamos seguros de que tenemos acceso a todos las variables con las etiquetas @FXML.

Veamos un ejemplo de carga de una imágen:

 

Eventos de Botón

Para que un botón pueda responder a una serie de eventos, debemos definir los métodos. Estos los asociaremos luego en la vista, es decir, en el formulario:

Vemos en este formulario:

Login_fxml_controller_button_method

 

Que asociamos este método al botón loginBtn.

Las variables de las que se hace uso son aquellas que permiten acceder a los componentes gráficos, son las previamente definidas en la clase del controlador.

Método de validación de contraseñas con Argon2

 

El método validarLogin() se encarga de validar la información proporcionada, es decir, las credenciales de acceso a la base de datos:

 

Este método se encargará de validar si los datos introducidos son validos. La mayoría del código ya lo conocéis, puesto que es una consulta a la base de datos.

Veamos el método encargado de encriptar.

Aquí se invoca el método siguiente:

Este método lo encontramos en el package Utils y se va a encargar de verificar si los datos proporcionados, usuario y contraseña casan con los almacenados en la BD:

Este método recibe el usuario y la contraseña.

Con Argon2 vamos a seleccionar el algoritmo de cifrado a utilizar:

Los valores siguientes:

No son necesarios porque es la parte de la sal y la pimienta que vamos a utilizar para nuestro algoritmo. Lo puedes ver en el método de cifrado que se utiliza este.

Lanzamos la consulta y obtenemos la constraseña para este usuario. Al valor de comprueba le concatenamos la contraseña y luego le aplicamos una función de verificación a la contraseña del paquete de Argon2 que implementa la misma:

Este es capaz de realizar el proceso inverso y verificar si es la misma, en cuyo caso devolvemos true y sino false.

Podéis observar el método de cifrado utilizado:

 

Podéis cambiar los parámetros de cifrado, pero recordad luego llevarlos al método de verificación de la contraseña puesto que afectan. Variable comprueba.

Volviendo al método validarLogin():

Si la validación es ok del usuario y contraseña, nos quedamos con el rol e invocamos un nuevo método: crearCuentaForm();

 

Registro de nuevos usuarios

crearCuentaForm() nos va a permitir crear un nuevo formulario en el cual introducir los datos de nuevos usuarios y registrarlos. Además, si tenemos el perfil de administrador, podremos listar los existentes:

 

Este formulario de registro nos representará el siguiente formato:

RegisterForm

 

Cuyo diseño con SceneBuilder es el siguiente:

RegisterForm_FXML

Es importante tener en cuenta que bajo de Cerrar en SceneBuilder hay un botón, el de listar que aparece en la captura de tiempo de ejecución, pero este no se ve, pero en el árbol de componentes podemos ver que hay un Button llamado ListarBtn.

Código del controlador RegisterController:

Merece la pena que a este controlador le demos una serie de vueltas en detalle. No voy a profundizar en esta documentación pero si incidir en los aspectos a tener en cuenta y que podemos ver interesantes.

En la redefinición del método initialize podemos observar que además de cargar imágenes, en función del parámetro rol almacenado en el acceso en la variable privada rol, hacemos visible el botón de Listar, ese que no se ve en diseño por qué su propiedad de visibilidad está a false.

Y si pulsamos al botón de cerrar:

Recogemos la ventana de esta escena en un objeto de tipo stage, es decir, escenario.

Cerramos el escenario, y además cerramos la aplicación.

 

El resto de métodos son más accesibles y los podemos ver sobre la marcha.

 

¿Y si somos administrador y listamos ?

 

Listar usuarios

ListarForm_xml

Vemos que aparece un formulario que nos lista el contenido de los usuarios de la BD's. Dispone además de un buscador que nos filtrará por nombre de usuarios y un botón de salir que cerrará la aplicación.

La vista de este formulario la tenemos en Buscar.fxml cuyo diseño es el siguiente:

ListarForm_xml_scenebuilder

 

Veamos el código de este:

El controlador que tiene asociado este formulario es BuscarController:

El método initialize vemos que va a relizar más inicializaciones que en las pantallas anteriores.

Previo a este vemos un nuevo tipo de datos:

Se trata de una lista Observable. Los observables son objetos que se definen sobre clases y que permiten trabajar en tiempo real con las mismas y alimentar componentes de la BD.

 

JavaFX Observable Collections

El lenguaje de programación Java cuenta con colecciones como: List, Set, Map, la API JavaFX extiende estas colecciones con las interfaces: ObservableList, ObservableSet, ObservableMap, respectivamente, esto con el objetivo de proporcionarle a las colecciones el soporte para la notificación de cambios e invalidación.

Estas colecciones se encuentran en el paquete javafx.collections y para crearlas debemos usar la clase FXCollections que nos provee de distintos métodos estáticos para crear la colección que deseemos.

No es intención de profundizar con los Observables, pero si vamos a ver una pequeña utilidad para poder acceder y obtener información de la base de datos.

Haremos uso de los mismos en los Controladores.

Veamos un fragmento de código y lo describimos.

Se define el observable llamado AlumnosObservableList, si nos fijamos, la forma de definir este es especificando el modelo de datos dentro de este. Le asociamos con FXCollections el método que nos permite crear un arraylist.

 

¿Cómo añadimos objetos a la lista de este observable?

Como lo hacemos con los arrayList

Ahora tenemos un TableView con los respectivos TableColumn que nos mostrarán el id, nombre, apellidos y dni de todos los alumnos.

Para poder cargar este TableView vamos a necesitar los siguientes elementos:

setCellValueFactory : Se encuentra asociado al TableColumn y nos permite asignarle valor a una columna (celda)

PropertyValueFactory: Se puede usar como cellValueFactory en TableColumn. Utiliza la reflexión para acceder a métodos que coinciden con un determinado patrón para recuperar los datos de un elemento de TableView.

y lo añadimos al TableView:

Lo siguiente que vamos a realizar es envolver el ObservableList en un FilteredList

Ahora realizamos el filtrado haciendo uso de programación funcional:

Luego vamos a envolver, Wrap, el FilteredList en un SorteredList.

Vinculamos el comparador SortedList al comparador TableView.

Añadimos los datos ordenados (y filtrados) a la tabla.

Luego podemos gestionar las excepciones y generar logs:

 

La clase Logger nos va a permitir gestionar los diferentes niveles de log. Aquí os dejo un poco más de información:

https://keepcoding.io/blog/que-es-java-util-logging-logger-como-funciona/

 

Lo siguiente que tenemos es el fragmento de código asociado al evento que nos permitirá salir de la aplicación:

 

Información adicional

¿Cómo podemos cerrar un formulario?

Antes de nada vamos a entender que es Scene y que es Stage:

JavaFX Stage es una pantalla de escritorio que muestra la salida de JavaFX al usuario, también se dice que Stage es una ventana.

Dentro de un Stage de JavaFX estamos insertando un elemento Scene de JavaFX que muestra el contenido dentro de la ventana, o podemos decir dentro de un Scene. Cuando se llama al método principal desde una aplicación dentro del método launch(), se llama automáticamente a start(). Este método de inicio tiene un argumento que es el objeto Stage, que crea el objeto principal Stage.

Veamos un fragmento de código del controlador con su respectivo método start():

 

Hasta este punto, hemos visto como podemos inicializar un formulario, pero ... ¿ y si no queremos que el usuario de la aplicación lo cierre desde la venta y poder controlar nosotros la finalización del mismo?

Por ejemplo, se podría dar una situación en la que si se cierra este nosotros poder cerrar las conexiones de base de datos que tenemos. Veamos como podemos hacer esto con unos fragmentos de código.

Primera alternativa

Utilizamos un evento, como puede ser un click sobre un botón

Como podemos observar, definimos una variable de tipo Stage y a través de nuestro botón, obtenemos la escena y luego sobre esa la ventana.

Una vez tenemos esta ventana, la cerramos.

Segunda alternativa

En la aplicación principal (no en el controlador), nos definimos una variable privada y estática de tipo Stage, de tal manera que es accesible desde otras clases.

y cuando queremos cerrar la ventana, procedemos como anteriormente pero invocando esta variable, cuidado, definimos antes un getter estático:

 

ESTO NO LO VAMOS A TRABAJAR EN CLASE

 

5. LibretaDirecciones: Creación del proyecto y configuración


LibretaDirecciones

 

5.1 Creación y configuración

Crea un nuevo proyecto de tipo JavaFX Application llamado LibretaDirecciones. Desmarca la casilla que indica que se cree una clase principal, ya que la crearemos más adelante mediante otra estructura.

Para este proyecto y posteriores, nos acostumbraremos a utilizar una arquitectura de tipo Modelo-Vista-Controlador (MVC), de modo que separaremos el código de nuestra aplicación en 3 partes diferenciadas:

MVC

 

5.2. Creación del archivo FXML de diseño

Hay dos formas de crear una interfaz de usuario JavaFX: mediante código Java o mediante XML. En este caso, la realizaremos en XML (con un archivo de extensión .fxml) y utilizando para el diseño la herramienta de edición Scene Builder instalada anteriormente:

Nos vamos a la ruta LibretaDirecciones\src\main\resources\urldenuestroproyecto\ y creamos un fichero nuevo FXML al que llamaremos VistaPersona .

NuevoFXMLFile

Una vez hecho esto, bien, vamos a la opción del menu en el fichero fxml para ver con formato Scene Builder o lo abrimos con botón derecho sobre el fichero y opción de abrir desde Scene Builder.

VistaPersonaSceneBuilder

 

5.3 Diseño mediante Scene Builder

Una vez abierto Scene Builder, seleccionando el AnchorPane en la jerarquía de la izquierda, ajusta el tamaño en el apartado layout (a la derecha).

Añade un SplitPane (horizontal) arrastrándolo desde la librería (Library) al área principal de edición. Haz clic en el Split Pane y, desde el menú Modify, haz clic en Fit to Parent, para ajustarlo a la ventana (o pulsa Ctrl + K).

Añade una TableView (en Controls) y arrástralo al lado izquierdo del SplitPane. En el lado derecho, ajusta los cuatro AnchorPane Constraints de la TableView a 0 para que la TableView esté anclada a los bordes y "siga" el posible redimensionamiento de la ventana padre (Puedes hacer clic en el menú Preview -> Show Preview in window o pulsar Ctrl + P para comprobarlo).

VistaPersonaSceneBuilder_2

Haciendo clic en cada TableColumn de la TableView, cambia los títulos C1 y C2 por Nombre y Apellidos, respectivamente.

VistaPersonaSceneBuilder_3

Selecciona la TableView y en Properties (lado derecho) selecciona Column Resize Policy: constrained-resize para que las columnas utilicen todo el espacio derecho disponible. El resultado debería ser similar al siguiente:

VistaPersonaSceneBuilder_4

Crea ahora un Label (Controls) en el lado derecho del SplitPane con el texto "Detalles". Ajusta sus Properties a tu gusto.

 

Añade un GridPane (Containers) debajo del Label y ajusta su apariencia usando anclajes (similares a los que usamos en la TableView, aunque en este caso con valores arriba, derecha, abajo, izquierda: 30, 10, 60, 10).

Modifica el GridPane para que tenga 6 filas (haciendo clic derecho en un número de fila se pueden añadir nuevas con Add Row Above o Add Row Before) con las etiquetas Nombre, Apellidos, Dirección, Ciudad, Código Postal, Fecha de nacimiento. El resultado debe ser similar al siguiente:

VistaPersonaSceneBuilder_5

VistaPersonaSceneBuilder_6

 

Añade 3 Button (Controls) en la parte inferior derecha con los textos "Nuevo", "Editar" y "Borrar". Para ajustarlos más cómodamente de forma global, selecciona los 3 y con el botón derecho haz clic con el botón derecho, luego en Wrap in -> HBox. Ahora puedes ajustar el Layout del HBox para que el Spacing entre botones sea de 10 y los AnchorPane derecho e inferior sean 10. El resultado final del interfaz debería ser similar al siguiente:

VistaPersonaSceneBuilder_7

 

Cierra ahora Scene Builder y, antes de continuar, tómate un tiempo para analizar la estructura del archivo XML creado.

 

5.4 La vista principal

Con esto ya tendríamos creada nuestra VistaPersona, pero para nuestra aplicación necesitamos ademas una vista principal, que crearemos en un nuevo FXML llamado VistaPrincipal, también dentro del directorio anterior.

Una vez abierto en Scene Builder, borrar el AnchorPane, ya que en este caso utilizaremos un BorderPane, que puedes añadir arrastrando desde Containers en la parte superior izquierda.

El tamaño preferido del BorderPane (En Layout) debe ser 600x400 y de momento solo le añadiremos una barra de menú superior (MenuBar en Custom) en la parte superior. El resultado debería ser similar a este:

VistaPersonaSceneBuilder_8

Por último, vamos a cerrar Scene Builder y crear la clase principal de nuestra aplicación, pero primero tómate algo de tiempo para analizar la estructura del archivo FXML recién creado.

 

5.5 La clase principal

Vamos a crear nuestra clase principal con el nombre LibretaDirecciones haciendo clic derecho en LibretaDirecciones\src\main\java\com.loquesea.com\

VistaPersonaSceneBuilder_10

La clase generada (LibretaDirecciones.java) extiende a la clase Application y contiene dos métodos. Esta es la estructura básica que necesitamos para ejecutar una aplicación JavaFX. La parte más importante para nosotros es el método start(Stage primaryStage). Este método es invocado automáticamente cuando la aplicación es lanzada desde el método main.

Como puedes ver, el método start(...) toma un Stage como parámetro. El gráfico siguiente muestra la estructura de cualquier aplicación JavaFX:

1040

El funcionamiento es similar al de una obra de teatro: El Stage (escenario) es el contenedor principal, normalmente una ventana con borde y los típicos botones para maximizar, minimizar o cerrar la ventana. Dentro del Stage se puede añadir una Scene (escena), la cual puede cambiarse dinámicamente por otra Scene. Dentro de un Scene se añaden los nodos JavaFX, tales como AnchorPane, TextBox, MediaPlayer, ImageView, etc.

Para tener más información puedes consultar Working with the JavaFX Scene Graph.

Por defecto, la clase principal creada invoca una vista sencilla con un botón que permite decir "Hola" al hacer clic en él. Vamos a borrar todo el código de la clase y sustituirlo por el siguiente, que como puedes ver está comentado indicando a qué corresponde cada apartado para que puedas comprender en detalle su funcionamiento:

 

5.6 El modelo Persona

 

Necesitamos un modelo para almacenar toda la información relativa a los contactos de la libreta. Para ello, dentro del paquete model, crearemos una nueva clase Java llamada Persona.

El código para la clase Persona se detalla a continuación, y los aspectos más relevantes del mismo serían:

 

 

5.7 La lista de personas

El objetivo de nuestro proyecto era almacenar y gestionar una lista de personas, con lo que vamos a crear una lista de objetos de tipo Persona dentro de la clase LibretaDirecciones a la que luego podremos acceder desde cualquiera de los otros controladores.

Lista observable (ObservableList)

Para poder pasar y mantener sincronizados los datos de la lista de personas en las clases gráficas de JavaFX, utilizamos las denominadas clases de colección de JavaFX, de las cuales usaremos una ObservableList.

Se ha modificado el código de la clase principal LibretaDirecciones, de modo que ahora incluya nuestra variable ObservableList y un método de consulta (get) público. Además, hemos añadido un constructor para crear datos de ejemplo:

 

5.8 El controlador para la vista de personas

 

Por último, tenemos que añadir los datos a nuestra tabla de VistaPersona.fxml, y para ello crearemos un controlador mediante una clase Java llamada VistaPersonaController dentro del paquete view para evitar incompatibilidades entre versiones de Scene Builder.

El código de la clase VistaPersonaController se muestra a continuación, y en él hay que destacar ciertos detalles:

 

5.9 La conexión de LibretaDirecciones con VistaPersonaController

Debemos invocar el método setLibretaDirecciones desde la clase LibretaDirecciones, de modo que podamos acceder al objeto LibretaDirecciones y, entre otras cosas, obtener la lista de Persona. Para ello, debemos modificar el método muestraVistaPersona() en LibretaDirecciones para que incluya dicho acceso:

5.10 Vincular la vista al controlador

Para finalizar, debemos indicar a VistaPersona.fxml mediante Scene Builder cuál es su controlador y asociar los diferentes elementos de la TableView y del GridPane con las variables de VistaPersonaController:

 

 

6. LibretaDirecciones: Interacción con el usuario

En este apartado nos centraremos en las funciones relativas a la visualización de los detalles de cada persona, así como en las opciones de añadir, borrar y editar personas, incluyendo validación de datos de entrada.

 

6.1 Mostrar detalles de una persona

El objetivo será que cuando seleccionemos una persona en la tabla de la izquierda, nos muestre los detalles en la parte de la derecha.

Para ello, creamos el siguiente método llamado mostrarDetallesPersona(Persona persona) dentro de VistaPersonaController.java:

6.2 Trabajar con fechas

Dado que nuestra propiedad fechaDeNacimiento es de tipo LocalDate, no podemos trabajar con ella directamente, sino que tenemos que realizar una conversión de LocalDate a String.

No obstante, ya que en muchos sitios (y en futuros proyectos) vamos a necesitar esta conversión en ambos sentidos, vamos a crear una clase auxiliar que contenga los métodos estáticos necesarios para realizar estas conversiones.

Para ellos, vamos a crear una clase llamada UtilidadDeFechas dentro de un nuevo paquete (package) llamado util:

Como vemos, hemos utilizado el formato de fecha dd/MM/yyyy, si bien podríamos utilizar cualquier otro consultando als diferentes opciones que nos ofrece DateTimeFormatter.

Y por último, una vez creada la clase anterior, ya podemos sustituir el TODO del método mostrarDetallesPersona(Persona persona) para que quede como sigue:

 

6.3 Detectar cambios en la selección de la tabla

Para saber cuando el usario ha seleccionado a una persona de la tabla y mostrar sus detalles, necesitamos "escuchar" dichos cambios.

Para ello, implementaremos el interface de JavaFX ChangeListener con el método changed(...), que solo tiene 3 parámetros: observable, oldValue y newValue.

Esto lo vamos a hacer añadiendo al método initialize() de VistaPersonaController una lambda expression:

Con tablaPersonas.getSelectionModel()... obtenemos la selectedItemProperty de la tabla de personas, y le añadimos un listener. De este modo, cuando el usuario seleccione a una persona de la tabla, nuestra lambda expression será ejecutada, cogiendo a la persona recién seleccionada y pasándosela al método mostrarDetallesPersona(...).

Si ahora ejecutamos nuestra aplicación (ejecutando Clean and build previamente si es necesario), deberíamos conseguir la funcionalidad implementada y al ir seleccionando diferentes personas en la tabla de la izquierda veremos todos los detalles a la derecha.

 

6.4 Borrar una persona

Nuestro interfaz de usuario ya contiene un botón de borrar, pero sin funcionalidad.

Podemos seleccionar la acción a ejecutar al pulsar un botón desde el Scene Builder. Cualquier método de nuestro controlador anotado con @FXML (o declarado como public) es accesible desde Scene Builder. Así pues, empecemos añadiendo el método de borrado al final de nuestra clase VistaPersonaController:

Para terminar, abrimos el archivo VistaPersona.fxml con Scene Builder, seleccionamos el botón Borrar, y en la sección Code (derecha) seleccionamos borrarPersona como método para la opción On Action.

VistaPersonaSceneBuilder_11

 

6.5 Gestión de errores

Si bien en este punto deberíamos poder ejecutar nuestra aplicación y borrar elementos de la tabla ¿Qué ocurre si pulsamos el botón de Borrar sin ningún elemento seleccionado? Pues que obtendremos un error de tipo ArrayIndexOutOfBoundsException porque no puede borrar una persona en el índice -1, que es el valor devuelto por el método getSelectedIndex() cuando no hay ningún elemento seleccionado.

Para resolver el error simplemente modificaremos el método borrarPersona para asegurarnos de que el índice seleccionado es mayor o igual a 0, mostrando un mensaje de alerta en caso contrario:

Toda la información relativa al uso de diálogos en JavaFX la puedes encontrar haciendo clic AQUÍ o en la Documentación oficial de JavaFX.

 

6.6 Editar y añadir una persona

Para poder editar y/o añadir personas, vamos a necesitar una nueva ventana de diálogo a medida (es decir, un nuevo escenario o stage) que incluya un formulario con los campos de los detalles de la persona.

 

Vista EditarPersona

Dentro del paquete view, añadimos un nuevo archivo EditarPersona.fxml y, usando un panel de rejilla (GridPane), etiquetas(Label), campos de texto (TextField) y botones (Button) creamos una ventana de diálogo similar a la siguiente:

VistaPersonaSceneBuilder_12

 

Controlador EditarPersona

Algunas cuestiones relativas a este controlador:

 

 

Enlazar vista y controlador

 

Abrir la vista EditarPersona

La vista EditarPersona la abriremos desde un método nuevo llamado mostrarEditarPersona dentro de LibretaDirecciones.java:

Añade los siguientes métodos a la clase VistaPersonaController. Esos métodos llamarán al método anterior muestraEditarpersona(...) de LibretaDirecciones.java cuando el usuario pulse en los botones Crear o Editar:

Para finalizar, abre el archivo VistaPersona.fxml mediante Scene Builder y elige los métodos correspondientes (crearPersona() y editarPersona() para el campo On Action de la sección Code (derecha) de los botones Crear y Editar.

Llegados a este punto deberíamos tener nuestra aplicación LibretaDirecciones en funcionamiento, con un aspecto similar al de la captura mostrada al inicio de este capítulo. Esta aplicación es capaz de añadir, editar y borrar personas. Tiene incluso algunas capacidades de validación para evitar que el usuario introduzca información incorrecta.

 

7. LibretaDirecciones: Hojas de estilo CSS

En JavaFX puedes dar estilo al interfaz de usuario utilizando hojas de estilo en cascada (CSS).

En este ejemplo de aplicación vamos a crear un tema oscuro (DarkTheme).

Para información más específica de CSS y JavaFX puedes consultar:

 

7.1 Crear el archivo CSS

Crearemos en el directorio en el cual tenemos nuestros diseños los ficheros fxml un nuevo fichero denominado DarkTheme.css que contendrá nuestra hoja de estilos:

VistaPersonaSceneBuilder_13

Contenido DarkTheme.css:

 

Una vez tenemos nuestra hoja de estilos, vamos a aplicar esta a nuestras vistas.

 

7.2 Vincular vistas y estilos

Para vincular y el archivo CSS y asociar la clases correspondientes podríamos utilizar Java, si bien en este ejemplo de aplicación vamos a hacerlo mediante Scene Builder para que sea más visual:

 

VistaPrincipal.fxml

VistaPersonaSceneBuilder_14

 

EditarPersona.fxml

 

VistaPersona.fxml

 

Icono de aplicación

Ahora mismo nuestra aplicación utiliza el icono por defecto para la barra de título y la barra de tareas, pero quedaría mucho mejor con un icono personalizado.

Un posible sitio para obtener iconos gratuitos es Icon Finder. Yo descargué este icono de libreta de direcciones.

Una vez descargado el icono, crea un nuevo package img y añade el archivo descargado, en mi caso libretaDirecciones.png , redimensionado a 32px de altura.

Dejo el mismo en el siguiente directorio:

VistaPersonaSceneBuilder_21

Ahora modifica el método start() de LibretaDirecciones.java para que quede como sigue:

Por supuesto, podrías hacer lo mismo para el método muestraEditarPersona y asignarle su propio icono de edición.