Traducción del
tutorial de JavaFX: artículo de FXML y SceneBuilder de Vojtech Ruzicka.
Cómo crear una GUI con JavaFX usando el marcado FXML y SceneBuilder.
Todas las publicaciones en la serie JavaFX:
- Tutorial JavaFX: Primeros pasos
- Tutorial JavaFX: ¡Hola mundo!
- Tutorial JavaFX: FXML y SceneBuilder
- Tutorial JavaFX: diseños básicos
- Tutorial JavaFX: diseños avanzados
- Tutorial JavaFX: estilo CSS
- JavaFX Weaver: integración de aplicaciones JavaFX y Spring Boot
Forma tradicional
En el artículo anterior,
creamos una sencilla aplicación Hello World .
Solo un recordatorio: el código se veía así:
@Override public void start(Stage primaryStage) throws Exception { primaryStage.setTitle("Hello world Application"); primaryStage.setWidth(300); primaryStage.setHeight(200); InputStream iconStream = getClass().getResourceAsStream("/icon.png"); Image image = new Image(iconStream); primaryStage.getIcons().add(image); Label helloWorldLabel = new Label("Hello world!"); helloWorldLabel.setAlignment(Pos.CENTER); Scene primaryScene = new Scene(helloWorldLabel); primaryStage.setScene(primaryScene); primaryStage.show(); }
Como puede ver, toda la interfaz de usuario se crea en código Java.
Este es un ejemplo muy simple, pero a medida que su aplicación se vuelve más compleja, cuando tiene que ingresar varios niveles de diseños anidados y muchos componentes, el código resultante puede volverse muy difícil de entender. Sin embargo, esto no es todo: en la misma clase hay un código que es responsable de la estructura, los efectos visuales y el comportamiento al mismo tiempo.
La clase claramente no tiene una sola responsabilidad. Compare esto, por ejemplo, con la interfaz web, donde cada página tiene tareas claramente separadas:
- HTML es una estructura
- CSS es efectos visuales
- JavaScript es comportamiento
Introduciendo FXML
Obviamente, tener todo el código en un solo lugar no es una buena idea. Debe estructurarlo de alguna manera para que sea más fácil de entender y hacerlo más manejable.
En realidad, hay muchos patrones de diseño para esto. Por lo general, terminas con una opción de "Modelo-Vista-Lo que sea" - es algo así como "Controlador de vista de modelo", "Presentador de vista de modelo" o "Modelo de vista de vista de modelo".
Puede pasar horas discutiendo los pros y los contras de las diferentes opciones; no lo hagamos aquí. Más importante aún, con JavaFx puede usar cualquiera de ellos.
Esto es posible porque, además del diseño de procedimiento de su interfaz de usuario, puede utilizar el marcado XML declarativo.
Resulta que la estructura jerárquica de XML es una excelente manera de describir la jerarquía de componentes en la interfaz de usuario. HTML funciona bastante bien, ¿verdad?
El formato XML específico de JavaFX se llama FXML. En él, puede definir todos los componentes de la aplicación y sus propiedades, así como asociarlos con el controlador, que es responsable de administrar las interacciones.
Descargar archivos FXML
Entonces, ¿cómo podemos cambiar nuestro método de lanzamiento para trabajar con FXML?
FXMLLoader loader = new FXMLLoader(); URL xmlUrl = getClass().getResource("/mainScene.fxml"); loader.setLocation(xmlUrl); Parent root = loader.load(); primaryStage.setScene(new Scene(root)); primaryStage.show();
Aquí la
raíz representa el componente raíz de su interfaz de usuario, otros componentes están anidados en ella.
El método de
carga tiene un valor de retorno genérico, por lo que puede especificar un tipo específico, no
padre . A continuación, obtiene acceso a métodos orientados a componentes. Sin embargo, esto hace que su código sea más frágil. Si cambia el tipo de componente raíz en su FXML, la aplicación puede dejar de funcionar en tiempo de ejecución, pero no habrá errores durante la compilación. Esto se debe a que ahora hay una falta de coincidencia del tipo declarado en su FXML y en el cargador Java FXML.
Crear archivo FXML
Ahora sabemos cómo cargar el archivo FXML, pero aún necesitamos crearlo. El archivo debe tener la extensión .fxml. En un proyecto Maven, puede colocar este archivo en la carpeta de recursos o FXMLLoader puede descargarlo desde una URL externa.
Después de crear el archivo, ingrese la declaración XML en su primera línea:
<?xml version="1.0" encoding="UTF-8"?>
Importar
Antes de agregar componentes individuales a un archivo, debe asegurarse de que se reconocen correctamente. Para hacer esto, agregue declaraciones de importación. Esto es muy similar a importar en clases Java. Puede importar clases individuales o usar comodines como de costumbre. Veamos un ejemplo de una sección de importación:
<?import java.lang.*?> <?import java.util.*?> <?import javafx.scene.*?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?>
La buena noticia es que, en lugar de agregar todas las declaraciones de importación manualmente, su IDE debería ayudarlo a agregar la importación, como agregarlas a las clases de Java.
Agregar componentes
Ahora es el momento de agregar algunos componentes. En un
artículo anterior, aprendimos que cada escena puede tener solo un componente secundario. Para comenzar, agreguemos una etiqueta simple:
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.Label?> <Label>Hello World!</Label>
Por supuesto, el etiquetado como componente raíz no es un ejemplo muy realista. Por lo general, es preferible utilizar algún tipo de diseño (diseño), que es un contenedor para varios componentes y organiza su disposición. Cubriremos los diseños más adelante en esta serie, pero por ahora, usemos un simple VBox que coloque a sus hijos verticalmente uno encima del otro.
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.Label?> <?import javafx.scene.layout.VBox?> <?import javafx.scene.control.Button?> <VBox> <Label text="Hello world!"/> <Label text="This is a simple demo application."/> <Button text="Click me!"/> </VBox>
Espacio de nombres FX
Hay un par de elementos y atributos FXML que no están disponibles de forma predeterminada. Debe agregar un espacio de nombres FXML para que estén disponibles. Debe agregarse al componente raíz:
<?xml version="1.0" encoding="UTF-8"?> ... <VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"> ... </VBox>
Ahora puede usar los nuevos elementos del espacio de nombres fx. Intentemos agregar identificadores únicos a nuestros componentes:
<Label fx:id="mainTitle" text="Hello world!"/>
El atributo
fx: id es un identificador único para un componente que puede usarse para referirse a un componente de otras partes de nuestro FXML e incluso de nuestro controlador.
Guiones
Nuestra aplicación sigue siendo estática. Hay varias etiquetas y un botón, pero la aplicación no hace nada dinámico.
Respondamos al clic de nuestro botón y cambiemos el título de "¡Haz clic en mí!" A "¡Haz clic en mí otra vez!".
Lo primero que debe hacer es agregar un controlador de eventos onAction para nuestro botón.
<Button fx:id="mainButton" text="Click me!" onAction="buttonClicked()"/>
Preste atención a fx: id, este es el identificador que se utilizará más adelante para referirse al botón.
Ahora debe proporcionar una función que se llamará para manejar el evento. Se puede definir dentro de la etiqueta fx: script. Lo importante es que puede usar diferentes idiomas para escribir un script, JavaScript, Groovy o Clojure. Veamos un ejemplo en JavaScript:

Observe que nos referimos a nuestro componente Button utilizando el identificador mainButton, que se declaró así:
fx:id = "mainButton"
También debe indicar qué lenguaje de script usa en el archivo FXML:
<?language javascript?>
Veamos el texto completo del ejemplo:

¿Debo usar esto?
El ejemplo anterior muestra cómo hacer referencia a componentes usando
fx: id y cómo agregar un comportamiento simple usando un script JavaScript. ¿Es eso realmente lo que deberías estar haciendo?
La respuesta es en la mayoría de los casos no. Hay varios problemas con este enfoque. La razón por la que se introdujo FXML fue una separación de intereses, para separar la estructura y el comportamiento de la interfaz de usuario. En este script, el comportamiento combinado con la estructura de la interfaz de usuario volvió nuevamente. Además, dado que ya no trabajamos con código Java, sino con XML, se perdieron todas las comprobaciones de código en tiempo de compilación y seguridad de tipo. Ahora todos los problemas en la aplicación se detectarán en tiempo de ejecución, y no en tiempo de compilación. La aplicación se ha vuelto muy frágil y propensa a errores.
Agregar un controlador
Entonces, ¿qué se puede hacer para lograr una clara separación de intereses? Puede vincular el controlador a nuestro archivo FXML. Un controlador es una clase Java que se encarga de manejar el comportamiento y la interacción del usuario en una aplicación. De esta manera, puede devolver comprobaciones de seguridad de tipo y tiempo de compilación.
El controlador es un POJO, no debe extender ni implementar nada, ni debe tener anotaciones especiales.
¿Cómo puedo asociar una clase de controlador con nuestro FXML? Básicamente hay dos opciones.
En java
Puede crear una instancia del controlador usted mismo o usar cualquier otro método para crear una instancia, como la inyección de dependencia. Luego simplemente descargue su
FXMLLoader .
FXMLLoader loader = new FXMLLoader(); loader.setController(new MainSceneController());
En FXML
Puede especificar la clase de su controlador como un atributo
fx: controller , que debe ubicarse en el componente raíz.
<VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" fx:controller="com.vojtechruzicka.MainSceneController"> ... </VBox>
Si declara su clase de controlador en FXML, se crea automáticamente para usted. Este enfoque tiene una limitación: en el controlador debe crear un constructor sin argumentos para facilitar la creación de una nueva instancia de la clase Controller.
Para acceder a una instancia de un controlador creado automáticamente, puede usar el cargador FXML:
FXMLLoader loader = new FXMLLoader(); loader.setLocation(getClass().getResource("/mainScene.fxml")); MainSceneController controller = loader.getController();
Llamar a los métodos del controlador
Ahora que tiene un controlador, puede eliminar el script e implementar la lógica para presionar los botones directamente en el controlador:
public class MainSceneController { public void buttonClicked() { System.out.println("Button clicked!"); } }
El siguiente paso es registrar la llamada de este método como el
controlador de eventos
onAction de nuestro botón. Para referirnos a los métodos de nuestro controlador, necesitamos usar el signo
# delante del nombre del método:
<VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" fx:controller="com.vojtechruzicka.MainSceneController"> <Label fx:id="mainTitle" text="Hello world!"/> <Label fx:id="subTitle" text="This is a simple demo application."/> <Button fx:id="mainButton" text="Click me!" onAction="#buttonClicked"/> </VBox>
Cuando se hace clic en un botón, llama al método
MainSceneController.buttonClicked () . Tenga en cuenta que esto solo funciona si el método se declara público. Si el modificador de acceso es más estricto, debe anotar el método con la anotación
@FXML .
@FXML private void buttonClicked() { System.out.println("Button clicked!"); }
Incrustar componentes en un controlador
Hasta ahora, solo estamos imprimiendo en la consola. ¿Qué sucede si queremos cambiar el texto de nuestro botón a "
Haga clic en mí nuevamente "
nuevamente ? ¿Cómo podemos obtener enlaces a componentes en nuestro controlador?
Afortunadamente, esto es fácil. ¿Recuerdas estos atributos de
fx: id ?
<Button fx:id="mainButton" text="Click me!" onAction="#buttonClicked"/>
JavaFX intenta hacer coincidir automáticamente los componentes con
fx: id con los campos definidos en su controlador con el mismo nombre.
Supongamos que tenemos un botón descrito anteriormente con
fx:id="mainButton"
JavaFX intenta inyectar el objeto del botón en su controlador en un campo llamado
mainButton :
public class MainSceneController {
Como en los métodos anteriores, sus campos deben ser
públicos o anotados en
@FXML .
Ahora que tenemos un enlace a nuestro botón, podemos cambiar fácilmente su texto:
public class MainSceneController { @FXML private Button mainButton; @FXML private void buttonClicked() { mainButton.setText("Click me again!"); } }
Generador de escena
Escribir su estructura GUI en XML puede ser más natural que en Java (especialmente si está familiarizado con HTML). Sin embargo, todavía no es muy conveniente. La buena noticia es que existe una herramienta oficial llamada Scene Builder que lo ayudará a crear la interfaz de usuario. En pocas palabras, este es un editor gráfico para su GUI.

El editor tiene tres áreas principales:
- La parte izquierda muestra los componentes disponibles que se pueden arrastrar a la parte central. También contiene una jerarquía de todos los componentes en su interfaz de usuario, para que pueda navegar fácilmente.
- La parte central es su aplicación que se muestra en función de su archivo FXML.
- A la derecha está el inspector de componentes actual. Aquí puede editar varias propiedades del componente actual seleccionado. Cualquier componente seleccionado en el medio de la jerarquía se muestra en el inspector.
Independiente
Scene Builder se puede
descargar como una aplicación independiente que se puede usar para editar archivos FXML.
Integración con IntelliJ IDEA
Alternativamente, Scene Builder ofrece integración con el IDE.
En IntelliJ IDEA, puede hacer clic con el botón derecho en cualquier archivo FXML y luego seleccionar la opción Abrir menú en SceneBuilder.
Alternativamente, IntelliJ IDEA integra SceneBuilder directamente en el IDE. Si abre el archivo FXML en IDEA, aparecerán dos pestañas en la parte inferior de la pantalla
Para cada archivo FXML, puede cambiar fácilmente entre editar el archivo FXML directamente o mediante SceneBuilder.

En IntelliJ IDEA, puede configurar la ubicación del ejecutable SceneBuilder:
Settings → Languages & Frameworks → JavaFX → Path to SceneBuilder
Que sigue
En la próxima publicación de nuestra serie, discutiremos algunos diseños básicos que se pueden usar para organizar los componentes de una aplicación GUI en JavaFX.