JavaFX: 3 Formas de Pasar Información entre Escenas

Miguel Manjarres
7 min readJul 2, 2020

En el siguiente artículo se explicarán tres diferentes formas en las que se puede pasar información de una escena a otra en JavaFX sin necesidad de guardarla en archivos o en bases de datos.

Requisitos

  • Conocimientos en Java hasta su versión 8
  • Conocimientos básicos en JavaFX
  • Conocimientos básicos en Git
  • Conocimientos básicos en Maven (Opcional)

Para empezar el tutorial, en tu IDE favorito (en mi caso utilizaré IntelliJ) crea un nuevo proyecto JavaFX preferiblemente utilizando algún arquetipo Maven. Esto último no es particularmente importante, los conceptos que veremos se pueden aplicar de cualquier forma.

Independientemente del arquetipo que elijas, tu código boilerplate (el código que viene por defecto) será parecido a esto:

Boilerplate code

Con una estructura de carpetas parecida a ésta (los contenidos pueden variar en función del arquetipo elegido) :

├───src
│ └───main
│ ├───java
│ │ └───org
│ │ └───mediumtutorials
│ └───resources
│ ├───fxml
│ ├───images
│ └───styles

Puedes ignorar (y eliminar, si deseas) las clases controladores o archivos que hayan sido creados al momento de la inicialización del proyecto, pues empezaremos desde cero. Después de haber hecho eso, inicializa un repositorio vacío en tu proyecto.

Antes de empezar con la explicación de las diferentes formas en las que podemos compartir la información, vamos a pensar en qué casos podríamos necesitarlo. Imagina por un momento que debes construir una aplicación que le pida cierta información a un usuario, lo básico, podríamos entonces crear una clase que encapsule esta información, tal que así:

Clase usuario

Ahora seguramente tu puedes crear formularios con diseños extravagantes, pero para este ejemplo vamos a mantenerlo simple. Crea un nuevo archivo Home.fxml y guardalo en la carpeta resources/fxml , seguido de eso, crea la clase HomeController.java (como recomendación, mueve la clase a un paquete dedicado a los controladores). A continuación en el archivo FXML pega el siguiente código:

En tu archivo MainApp.java reemplaza el código que tengas ahí por el siguiente:

Si ejecutas tu aplicación ahora, deberías ver la siguiente ventana con nuestro formulario aparecer:

Venta principal

Excelente. Ahora vamos a crear una escena en la cual mostraremos la información que enviaremos desde la ventana principal y, de nuevo, será algo simple. Similar a lo que hicimos anteriormente, crea un nuevo archivo Destino.fxml con su controlador asociado DestinoController.java . En el archivo FXML, pega el siguiente código:

Eso renderizará una ventana con tres etiquetas simples, una para cada campo del formulario y un botón que nos será de utilidad más adelante.

Habrás notado que los campos en la ventana principal venían con un id fijo, esto con el fin de recolectar los datos y guardarlos en una instancia de la clase Usuario, para ello crea una nueva función en la clase HomeController.java llamada enviarDatos(Event) en donde guardes la información del formulario, tal que así:

Ahora tenemos todo lo que necesitamos para empezar a explorar los distintos métodos con los cuales podemos enviar información a nuestra ventana destino. Como paso final, guarda todos los cambios que hayas hecho en tu repositorio, pues este será nuestro punto de partida para cada una de las tres formas.

Método 1: UserData

En JavaFX todas las clases que hereden de la clase Node, poseen los métodos setUserData(Object) y getUserData() que, en palabras de la documentación oficial, permiten guardar cualquier objeto, efectivamente anclandolo al nodo, para poder recuperarlo en un momento posterior. Utilizaremos este método para guardar la instancia de la clase usuario. Como el stage es el mismo para ambas ventanas, es un excelente candidato para guardar la información ahí.

Comencemos creando una nueva rama en el repositorio de nuestro proyecto con el siguiente comando:

git checkout -b metodo_uno

A continuación, referencia los elementos de tu vista en tu archivo DestinoController.java y crea una función llamada recuperarDatos que esté anclada al botón que diseñamos anteriormente, ahí como podrás adivinar, vamos a recuperar la instancia de la clase Usuario a partir del nodo raíz, para eso sigue estos pasos:

  1. Recupera la instancia del stage a través de la instancia del AnchorPane
  2. Utiliza el método getUserData() y guarda el contenido en una instancia de la clase Usuario
  3. Llena los labels con la información correspondiente

Tu clase debería verse así:

De vuelta en la clase HomeController.java en la función enviarDatos , agrega el siguiente código:

Mira con atención la línea número 6 del código de arriba, ahí estamos pasando el usuario a la instancia del stage.

Ejecuta tu aplicación con datos de prueba y verás que podrás recuperarlos en la ventana destino, justo como esperábamos.

Método 2: Creando manualmente el controlador

El método setUserData es útil cuando queremos pasar una única instancia de la clase, pero no se recomienda si queremos pasar una cantidad mayor de datos, en su lugar podemos optar por crear las instancias de las clases que necesitamos directamente en el controlador.

Para empezar vuelve nuevamente a la rama master con el comando:

git switch master

Y crea una nueva rama con el comando:

git checkout -b metodo_dos

En la clase DestinoController.java crea un atributo privado de tipo Usuario con su respectivo método setter, a continuación y similar a como se hizo anteriormente, crea una función recuperarDatos que esté anclado al botón que diseñamos, la lógica es la misma que en el método anterior, con la diferencia de que llenaremos los labels directamente con esta instancia, tal que así:

Para que este método funcione, debemos crear manualmente el controlador y pasarle la instancia de usuario en el momento que creamos la vista, es decir, debemos sobreescribir el comportamiento por defecto de JavaFX. Para ello empieza por editar tu archivo destino.fxml y en la etiqueta raíz, el AnchorPane elimina la siguiente propiedad:

fx:controller="controllers.DestinoController"

A continuación, en la clase HomeController.java , en el método enviarDatos sigue los siguientes pasos:

  1. Crea una nueva instancia de FXMLLoader a partir del constructor de la clase, pasándole la ruta del archivo FXML
  2. Crea una nueva instancia de la clase DestinoController.java y pasale la instancia de la clase usuario
  3. A la instancia de FXMLLoader pasale el objeto controlador utilizando el método setController
  4. Crea el objeto root utilizando el método load en la instancia del objeto FXMLLoader

El resto de la lógica es igual al método anterior. El código completo de la función sería:

Ejecuta tu aplicación una vez más con datos de prueba y envíalos, verás que los datos se muestran tal cual en la otra vista, como es de esperarse.

Método 3: Clases Singleton

En los métodos anteriores nos hemos apoyado en los controladores para pasar información, pero si realmente queremos imitar el comportamiento que tendríamos con, por ejemplo, una base de datos, tenemos que poner esa lógica en una clase aparte. Ahí es dónde este método tiene lugar, la idea es crear una clase que contenga todos los atributos que sean comunes entre las vistas involucradas, ya que ambos controladores acceden a los mismos atributos, se necesita que compartan la misma instancia, para ello podemos utilizar el patrón de diseño Singleton.

Para empezar vuelve nuevamente a la rama master con:

git switch master

Y crea una nueva rama con el comando:

git checkout -b metodo_tres

Crea una nueva clase UsuarioHolder en el paquete que prefieras. Una vez lo hayas hecho crea un atributo público y estático de tipo Usuario. Como paso final, implementa los pasos del método Singleton. Hay muchas formas de implementar esto último, una de ellas siendo esta:

A continuación en la clase HomeController.java en el método enviarDatos sigue los siguientes pasos:

  1. Obtén la instancia de la clase UsuarioHolder
  2. Pásale el objeto usuario a través del método setter

El resto de la lógica es igual al método uno. La función completa queda entonces como:

Luego de que tengas eso listo, crea el método recuperarDatos en la clase DestinoController.java que estará anclado al botón que diseñamos, en ella recupera el usuario a partir de la instancia compartida de UsuarioHolder y llena los labels con la información respectiva, tal que así:

Ejecuta nuevamente tu aplicación con datos de prueba y verás que, efectivamente, los datos se han pasado de la ventana principal a la ventana destino. Este método es particularmente útil para cuando la aplicación tiene un apartado de configuraciones, de esa forma puedes acceder fácilmente a las preferencias del usuario desde cualquier parte del programa a través de la instancia compartida de la clase.

Con esto hemos llegado al final de este tutorial, si tuviste inconvenientes siguiendo los pasos o te gustaria ver el proyecto completo, puedes visitar el repositorio en GitHub:

También te invito a experimentar tus propias soluciones realizando un fork al proyecto o si tienes alguna sugerencia puedes dejarla en los comentarios o abrir un issue. Muchas gracias por leer hasta aquí y espero verte en una próxima ocasión 👋.

Si te gustó el tutorial y quieres apoyarme para seguir creando más contenido así puedes hacerme una donación aquí.

--

--

Miguel Manjarres

Ingeniero de Software con experiencia en desarrollo de aplicaciones web y de escritorio