Saltar la navegación

FXML

Además de la forma que hemos visto de diseñar interfaces, JavaFX nos ofrece otra forma que hace que el diseño de interfaces sea aún más sencillo y visual. Utilizando FXML separamos el diseño de la interfaz de la lógica de la misma y así haremos que nuestro código sea mucho más fácil de mantener.

FXML no es más que un lenguaje basado en XML que contiene la definición del diseño de nuestra interfaz y que luego podemos cargar desde nuestra aplicación JavaFX y así evitarnos todo el código necesario para el diseño de la misma.

La siguiente imagen muestra una interfaz muy simple que hemos creado para ejemplificar el uso de FXML.

La siguiente imagen muestra la estructura de dicho proyecto usando FXML, dentro de nuestro proyecto con todos los ejemplos.

Ahora os dejo el código de cada uno de los ficheros del mismo.

Ejemplo.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>

<VBox alignment="CENTER" prefHeight="267.0" prefWidth="274.0" spacing="30.0" xmlns="http://javafx.com/javafx/15.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafx.aplicacionfxml.controladores.Ejemplo">
   <children>
      <Label text="Ejemplo de aplicacion utilizando FXML" textAlignment="CENTER" textFill="#0f62f2" wrapText="true">
         <font>
            <Font name="Ani" size="30.0" />
         </font>
      </Label>
      <HBox alignment="CENTER_RIGHT" prefHeight="48.0" prefWidth="274.0">
         <children>
            <Button fx:id="btCerrar" mnemonicParsing="false" onAction="#cerrar" text="Cerrar">
               <font>
                  <Font size="18.0" />
               </font>
            </Button>
         </children>
         <padding>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
         </padding>
      </HBox>
   </children>
   <padding>
      <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
   </padding>
</VBox>

Ejemplo.java

package javafx.aplicacionfxml.controladores;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.stage.Stage;

public class Ejemplo {
	@FXML
	private Button btCerrar;
	
	@FXML
	private void cerrar() {
		((Stage) btCerrar.getParent().getScene().getWindow()).close();
	}

}

MainApp.java

package javafx.aplicacionfxml;
	
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.fxml.FXMLLoader;
import javafx.recursos.LocalizadorRecursos;


public class MainApp extends Application {
	@Override
	public void start(Stage escenarioPrincipal) {
		try {
			Parent raiz = FXMLLoader.load(LocalizadorRecursos.class.getResource("vistas/Ejemplo.fxml"));
			Scene escena = new Scene(raiz);
			escena.getStylesheets().add(LocalizadorRecursos.class.getResource("estilos/aplicacion.css").toExternalForm());
			escenarioPrincipal.setTitle("Ejemplo FXML");
			escenarioPrincipal.setScene(escena);
			escenarioPrincipal.show();
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		launch(args);
	}
}

Podemos ver como nuestra aplicación queda muy simple, ya que lo único que hace es cargar el fichero fxml, lo interpreta y nos devuelve el nodo raíz de la estructura de nodos de nuestra interfaz, que en nuestro caso es un VBox, pero que podemos utilizar la clase contenedora Parent y que así nos sirva para cualquiera.  Esto se hace mediante la línea:

Parent raiz = FXMLLoader.load(LocalizadorRecursos.class.getResource("vistas/Ejemplo.fxml"));

Si nos fijamos en el fichero Ejemplo.fxml se indica:

  • La estructura jerárquica de nuestra interfaz con sus diferentes nodos y propiedades que hayamos modificado.
  • Quién será el controlador para dicha vista ( fx:controller="javafx.aplicacionfxml.controladores.Ejemplo")
  • El nombre de un botón al que luego podremos hacer referencia en el controlador (fx:id="btCerrar").
  • El nombre de un método que será el encargado de manejar el evento al pulsar dicho botón (onAction="#cerrar").

El fichero Ejemplo.java es el controlador para la vista anterior, que en nuestro caso es muy simple. Podemos destacar las anotaciones @FXML que son las que permiten hacer el mapeo entre los elementos de la vista y del controlador. Estas anotaciones no serían necesarias si el modificador de acceso fuese public, pero al ser private, que es lo recomendado, sí son necesarias.

Además en un controlador podemos añadir el método initialize (con la anotación @FXML) con lo que nos quedaría un controlador con el siguiente código, que ejecutará el código de inicialización para la interfaz y en el que podremos asignar modelos a controles o registrar nuevos métodos manejadores para los controles.

public class MiControlador {
    @FXML private Button button;

    @FXML
    private void initialize()
        //Aquí podemos asignar modelos a controles
        //También podemos registrar manejadores de eventos
    }
    ...
    ...
}

Como podemos observar, nuestra aplicación está muy bien estructurada, ya que el código de la clase principal es muy simple y luego tenemos bien separada la vista y su comportamiento. Pero tenemos un inconveniente añadido ya que ahora debemos aprender otro lenguaje como FXML para llevar a cabo el diseño de la vista. Sin embargo, tengo una buena noticia: no tendremos que aprender el lenguaje FXML ni modificar a mano los ficheros .fxml, pero eso lo veremos en el siguiente apartado.