Java – Crear una interfaz gráfica con JavaFX

JavaFX es la biblioteca gráfica actual de Java, la sucesora de Swing. Una aplicación es una clase que extiende Application y rellena una ventana; lo que más despista hoy no es el código, sino que JavaFX ya no forma parte del JDK.

JavaFX ya no viene con el JDK. Hasta Java 10 estaba dentro; desde Java 11 se distribuye aparte, como OpenJFX. Si copias un ejemplo de hace años y te dice package javafx.application does not exist, no es que esté mal: es que faltan los módulos. Con Maven o Gradle se añaden como dependencia; a mano, se bajan y se pasan en el module path.

La dependencia

<dependency>
  <groupId>org.openjfx</groupId>
  <artifactId>javafx-controls</artifactId>
  <version>27</version>
</dependency>

El artefacto trae el clasificador de la plataforma automáticamente. Si compilas a mano, hacen falta javafx-base, javafx-graphics y javafx-controls.

Una ventana con un botón

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class HolaFx extends Application {

    @Override
    public void start(Stage ventana) {
        Label saludo = new Label("Escribe tu nombre y pulsa el botón");
        TextField nombre = new TextField();
        nombre.setPromptText("tu nombre");
        Button boton = new Button("Saludar");

        boton.setOnAction(e -> {
            String n = nombre.getText().isBlank() ? "mundo" : nombre.getText().strip();
            saludo.setText("¡Hola, " + n + "!");
        });

        VBox raiz = new VBox(12, saludo, nombre, boton);
        raiz.setPadding(new Insets(20));
        raiz.setAlignment(Pos.CENTER);

        ventana.setTitle("Hola JavaFX");
        ventana.setScene(new Scene(raiz, 360, 180));
        ventana.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
Dos capturas de la misma ventana JavaFX: antes de pulsar, con el texto «Escribe tu nombre y pulsa el botón», y después, con «¡Hola, Ana!»
La ventana antes y después de pulsar el botón, tal cual la dibuja JavaFX 27 sobre Java 25.

Compilar y ejecutar

javac --module-path /ruta/javafx --add-modules javafx.controls -d . HolaFx.java

java  --module-path /ruta/javafx --add-modules javafx.controls \
      --enable-native-access=javafx.graphics HolaFx

Con Maven, el plugin javafx-maven-plugin lo hace solo y basta mvn javafx:run. El --enable-native-access no es obligatorio: sin él, Java 25 imprime un aviso porque JavaFX carga bibliotecas nativas, y desaparecerá cuando OpenJFX lo declare por su cuenta.

Las cuatro piezas

Pieza Qué es
Application la clase de la que heredas; launch() la arranca
start(Stage) se llama una vez, con la ventana ya creada
Stage la ventana del sistema: título, tamaño, iconos
Scene lo que hay dentro de la ventana; se le pasa el nodo raíz
Contenedores VBox, HBox, GridPane, BorderPane
Controles Label, Button, TextField, TableView…

La jerarquía es siempre la misma: un Stage contiene una Scene, que contiene un árbol de nodos. Los contenedores colocan a sus hijos solos —el VBox los apila con 12 píxeles de separación— y por eso casi nunca hay que dar coordenadas.

Responder a los eventos

boton.setOnAction(e -> saludo.setText("¡Hola!"));
nombre.textProperty().addListener((obs, viejo, nuevo) -> boton.setDisable(nuevo.isBlank()));

setOnAction recibe una lambda porque EventHandler tiene un solo método. Y lo segundo es lo que distingue a JavaFX de Swing: las propiedades observables. En vez de preguntar, te suscribes; con bind puedes incluso atar el estado de un control al de otro sin escribir ningún manejador.

Todo lo que toque la interfaz tiene que ejecutarse en el hilo de JavaFX. Si haces una consulta o descargas algo, va en otro hilo, y el resultado se devuelve a la interfaz con Platform.runLater(() -> …) o, mejor, con un Task, que ya trae propiedades de progreso y de mensaje. Tocar un control desde otro hilo lanza IllegalStateException: Not on FX application thread.

Y si la ventana es grande, FXML

Para interfaces de más de una pantalla, la disposición se escribe en un archivo FXML —un XML— y se diseña con Scene Builder, que es visual. El código queda solo con la lógica, en una clase controladora. Es el equivalente de separar el HTML del JavaScript.

Más información en inglés: Create a graphic interface with JavaFX.

Todo el código se compiló y se ejecutó con Java 25 LTS en un contenedor limpio antes de publicar esta página; las salidas están copiadas de esa ejecución.