Desarrollo de interfaces gráficas con JavaFX
Introducción
JavaFX es la tecnología moderna de Java para la creación de interfaces gráficas de usuario (GUI), sustituyendo a las antiguas bibliotecas como Swing y AWT. Esta plataforma permite a los desarrolladores crear aplicaciones de escritorio visualmente atractivas, con características avanzadas de diseño, animaciones y efectos visuales. JavaFX está integrado en el JDK desde Java 8, aunque a partir de Java 11 se convirtió en un módulo independiente, permitiendo mayor flexibilidad en su desarrollo y actualización.
En este artículo, aprenderás los fundamentos de JavaFX para desarrollar interfaces gráficas efectivas. Conocerás cómo estructurar una aplicación JavaFX, crear diversos controles de interfaz, aplicar estilos con CSS y manejar eventos para hacer que tu aplicación responda a las acciones del usuario. Este conocimiento te permitirá evolucionar desde las aplicaciones de consola hacia programas con interfaces visuales profesionales.
Estructura básica de una aplicación JavaFX
Toda aplicación JavaFX sigue una estructura fundamental que es importante comprender antes de comenzar a desarrollar. Los componentes clave son:
La clase Application
Para crear una aplicación JavaFX, debemos extender la clase javafx.application.Application
e implementar el método start()
:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class AplicacionBasica extends Application {
@Override
public void start(Stage primaryStage) {
// Creamos un componente Label
Label etiqueta = new Label("¡Hola JavaFX!");
// Creamos un contenedor para nuestros componentes
StackPane raiz = new StackPane();
raiz.getChildren().add(etiqueta);
// Creamos una escena con nuestro contenedor
Scene escena = new Scene(raiz, 300, 200);
// Configuramos y mostramos el escenario
primaryStage.setTitle("Mi Primera Aplicación JavaFX");
primaryStage.setScene(escena);
primaryStage.show();
}
public static void main(String[] args) {
// Lanzamos la aplicación
launch(args);
}
}
Jerarquía de componentes en JavaFX
JavaFX utiliza una estructura jerárquica para organizar los elementos de la interfaz:
- Stage (Escenario): Representa la ventana principal de la aplicación.
- Scene (Escena): El contenedor de primer nivel dentro del Stage.
- Nodos: Los componentes visuales (controles, contenedores, formas, etc.) que se organizan en la escena.
Contenedores de diseño
Los contenedores son nodos especiales que organizan la disposición de otros nodos dentro de ellos. JavaFX ofrece varios tipos de contenedores para diferentes necesidades de diseño:
BorderPane
Divide el espacio en cinco regiones: arriba, abajo, izquierda, derecha y centro.
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
BorderPane borderPane = new BorderPane();
borderPane.setTop(new Button("Arriba"));
borderPane.setBottom(new Button("Abajo"));
borderPane.setLeft(new Button("Izquierda"));
borderPane.setRight(new Button("Derecha"));
borderPane.setCenter(new Button("Centro"));
GridPane
Organiza los elementos en una cuadrícula flexible de filas y columnas:
import javafx.geometry.Insets;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
GridPane grid = new GridPane();
grid.setPadding(new Insets(10));
grid.setHgap(10); // Espacio horizontal entre columnas
grid.setVgap(8); // Espacio vertical entre filas
// Añadimos componentes específicos a posiciones de la cuadrícula
grid.add(new Label("Nombre:"), 0, 0); // columna 0, fila 0
grid.add(new TextField(), 1, 0); // columna 1, fila 0
grid.add(new Label("Apellido:"), 0, 1);
grid.add(new TextField(), 1, 1);
grid.add(new Label("Email:"), 0, 2);
grid.add(new TextField(), 1, 2);
HBox y VBox
HBox organiza componentes en una fila horizontal, mientras que VBox los organiza en una columna vertical:
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
// Crear un HBox con espaciado de 10px entre elementos
HBox hbox = new HBox(10);
hbox.setPadding(new Insets(15));
hbox.setAlignment(Pos.CENTER);
hbox.getChildren().addAll(
new Button("Botón 1"),
new Button("Botón 2"),
new Button("Botón 3"));
// Crear un VBox con espaciado de 8px entre elementos
VBox vbox = new VBox(8);
vbox.setPadding(new Insets(15));
vbox.setAlignment(Pos.CENTER_LEFT);
vbox.getChildren().addAll(
new Button("Opción A"),
new Button("Opción B"),
new Button("Opción C"));
Controles de interfaz de usuario
JavaFX proporciona una amplia gama de controles para la interacción con el usuario:
Botones y etiquetas
import javafx.scene.control.Button;
import javafx.scene.control.Label;
Label etiqueta = new Label("Introduce tu nombre:");
Button boton = new Button("Enviar");
Campos de texto y áreas de texto
import javafx.scene.control.TextField;
import javafx.scene.control.TextArea;
// Campo para texto de una sola línea
TextField campoNombre = new TextField();
campoNombre.setPromptText("Escribe tu nombre aquí");
// Área para texto multilínea
TextArea areaComentarios = new TextArea();
areaComentarios.setPromptText("Escribe tus comentarios");
areaComentarios.setPrefRowCount(5); // Número preferido de filas visibles
Casillas de verificación y botones de radio
import javafx.scene.control.CheckBox;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
// Casillas de verificación (permiten selección múltiple)
CheckBox chkJava = new CheckBox("Java");
CheckBox chkPython = new CheckBox("Python");
CheckBox chkCSharp = new CheckBox("C#");
// Botones de radio (permiten una sola selección dentro del grupo)
ToggleGroup grupoNivel = new ToggleGroup();
RadioButton rbPrincipiante = new RadioButton("Principiante");
RadioButton rbIntermedio = new RadioButton("Intermedio");
RadioButton rbAvanzado = new RadioButton("Avanzado");
rbPrincipiante.setToggleGroup(grupoNivel);
rbIntermedio.setToggleGroup(grupoNivel);
rbAvanzado.setToggleGroup(grupoNivel);
// Establecer selección predeterminada
rbPrincipiante.setSelected(true);
Menús
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
MenuBar barraMenu = new MenuBar();
// Menú Archivo
Menu menuArchivo = new Menu("Archivo");
MenuItem itemNuevo = new MenuItem("Nuevo");
MenuItem itemAbrir = new MenuItem("Abrir");
MenuItem itemGuardar = new MenuItem("Guardar");
MenuItem itemSalir = new MenuItem("Salir");
menuArchivo.getItems().addAll(itemNuevo, itemAbrir, itemGuardar, itemSalir);
// Menú Editar
Menu menuEditar = new Menu("Editar");
MenuItem itemCopiar = new MenuItem("Copiar");
MenuItem itemPegar = new MenuItem("Pegar");
menuEditar.getItems().addAll(itemCopiar, itemPegar);
barraMenu.getMenus().addAll(menuArchivo, menuEditar);
Manejo de eventos
El manejo de eventos en JavaFX permite que tu aplicación responda a las acciones del usuario, como clics de botón o entrada de texto:
Manejo básico de eventos
import javafx.event.ActionEvent;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
Button botonSaludar = new Button("Saludar");
// Usando expresiones lambda (Java 8+)
botonSaludar.setOnAction((ActionEvent evento) -> {
Alert alerta = new Alert(AlertType.INFORMATION);
alerta.setTitle("Saludo");
alerta.setHeaderText(null);
alerta.setContentText("¡Hola, bienvenido a JavaFX!");
alerta.showAndWait();
});
Controladores de eventos personalizados
Para aplicaciones más complejas, puedes crear clases controladoras dedicadas:
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
public class FormularioController {
@FXML
private TextField campoNombre;
@FXML
private Button botonEnviar;
@FXML
public void initialize() {
// Configuramos el evento al inicializarse el controlador
botonEnviar.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
System.out.println("Nombre enviado: " + campoNombre.getText());
}
});
}
// Método alternativo usando anotaciones FXML
@FXML
public void onEnviarClick(ActionEvent event) {
System.out.println("Nombre enviado: " + campoNombre.getText());
}
}
Estilo con CSS
JavaFX permite personalizar la apariencia de tu aplicación utilizando CSS, similar al usado en desarrollo web:
// Aplicar estilos directamente a un nodo
Button botonPersonalizado = new Button("Botón Estilizado");
botonPersonalizado.setStyle("-fx-background-color: #4286f4; -fx-text-fill: white;");
// Aplicar un archivo CSS a toda la escena
Scene escena = new Scene(raiz, 800, 600);
escena.getStylesheets().add(getClass().getResource("estilos.css").toExternalForm());
Ejemplo de archivo CSS (estilos.css):
/* Estilo para todos los botones */
.button {
-fx-background-color: #4286f4;
-fx-text-fill: white;
-fx-font-weight: bold;
-fx-padding: 5 10 5 10;
}
/* Estilo para botones al pasar el cursor */
.button:hover {
-fx-background-color: #2a5db0;
}
/* Estilo para etiquetas */
.label {
-fx-font-size: 14px;
-fx-text-fill: #333333;
}
Uso de FXML para separar diseño y código
FXML es un lenguaje basado en XML que permite separar la interfaz de usuario del código de la aplicación:
<!-- vista.fxml -->
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<VBox spacing="10" alignment="CENTER" prefWidth="300" prefHeight="200"
xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
fx:controller="com.ejemplo.FormularioController">
<padding>
<Insets top="20" right="20" bottom="20" left="20"/>
</padding>
<Label text="Nombre:"/>
<TextField fx:id="campoNombre"/>
<Button fx:id="botonEnviar" text="Enviar" onAction="#onEnviarClick"/>
</VBox>
Para cargar un archivo FXML:
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
@Override
public void start(Stage primaryStage) throws Exception {
Parent raiz = FXMLLoader.load(getClass().getResource("vista.fxml"));
Scene escena = new Scene(raiz);
primaryStage.setTitle("Aplicación FXML");
primaryStage.setScene(escena);
primaryStage.show();
}
Ejemplo práctico: Formulario de registro
Vamos a desarrollar un ejemplo completo de un formulario de registro utilizando JavaFX:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class FormularioRegistro extends Application {
@Override
public void start(Stage primaryStage) {
// Creamos el panel principal
BorderPane panelPrincipal = new BorderPane();
// Título
Label titulo = new Label("Formulario de Registro");
titulo.setStyle("-fx-font-size: 18px; -fx-font-weight: bold;");
HBox panelTitulo = new HBox(titulo);
panelTitulo.setAlignment(Pos.CENTER);
panelTitulo.setPadding(new Insets(15));
// Formulario
GridPane formulario = new GridPane();
formulario.setHgap(10);
formulario.setVgap(8);
formulario.setPadding(new Insets(20));
formulario.setAlignment(Pos.CENTER);
Label lblNombre = new Label("Nombre:");
TextField txtNombre = new TextField();
Label lblApellido = new Label("Apellido:");
TextField txtApellido = new TextField();
Label lblEmail = new Label("Email:");
TextField txtEmail = new TextField();
Label lblPassword = new Label("Contraseña:");
PasswordField txtPassword = new PasswordField();
Label lblGenero = new Label("Género:");
ToggleGroup grupoGenero = new ToggleGroup();
RadioButton rbMasculino = new RadioButton("Masculino");
RadioButton rbFemenino = new RadioButton("Femenino");
RadioButton rbOtro = new RadioButton("Otro");
rbMasculino.setToggleGroup(grupoGenero);
rbFemenino.setToggleGroup(grupoGenero);
rbOtro.setToggleGroup(grupoGenero);
HBox panelGenero = new HBox(10, rbMasculino, rbFemenino, rbOtro);
CheckBox chkTerminos = new CheckBox("Acepto los términos y condiciones");
Button btnRegistrar = new Button("Registrarse");
Button btnCancelar = new Button("Cancelar");
HBox panelBotones = new HBox(10, btnRegistrar, btnCancelar);
panelBotones.setAlignment(Pos.CENTER_RIGHT);
// Añadimos los componentes al formulario
formulario.add(lblNombre, 0, 0);
formulario.add(txtNombre, 1, 0);
formulario.add(lblApellido, 0, 1);
formulario.add(txtApellido, 1, 1);
formulario.add(lblEmail, 0, 2);
formulario.add(txtEmail, 1, 2);
formulario.add(lblPassword, 0, 3);
formulario.add(txtPassword, 1, 3);
formulario.add(lblGenero, 0, 4);
formulario.add(panelGenero, 1, 4);
formulario.add(chkTerminos, 0, 5, 2, 1);
formulario.add(panelBotones, 1, 6);
// Ajustamos el ancho de los campos
txtNombre.setPrefWidth(200);
txtApellido.setPrefWidth(200);
txtEmail.setPrefWidth(200);
txtPassword.setPrefWidth(200);
// Manejamos el evento del botón Registrar
btnRegistrar.setOnAction(e -> {
if (!chkTerminos.isSelected()) {
mostrarAlerta("Debe aceptar los términos y condiciones para registrarse.");
return;
}
if (txtNombre.getText().isEmpty() || txtApellido.getText().isEmpty() ||
txtEmail.getText().isEmpty() || txtPassword.getText().isEmpty()) {
mostrarAlerta("Por favor, complete todos los campos obligatorios.");
return;
}
mostrarConfirmacion("Usuario registrado correctamente: " + txtNombre.getText() +
" " + txtApellido.getText());
limpiarFormulario(txtNombre, txtApellido, txtEmail, txtPassword, chkTerminos, grupoGenero);
});
// Manejamos el evento del botón Cancelar
btnCancelar.setOnAction(e -> {
limpiarFormulario(txtNombre, txtApellido, txtEmail, txtPassword, chkTerminos, grupoGenero);
});
// Organizamos el panel principal
panelPrincipal.setTop(panelTitulo);
panelPrincipal.setCenter(formulario);
// Creamos la escena
Scene escena = new Scene(panelPrincipal, 450, 350);
// Configuramos y mostramos el escenario
primaryStage.setTitle("Registro de Usuario");
primaryStage.setScene(escena);
primaryStage.show();
}
private void mostrarAlerta(String mensaje) {
Alert alerta = new Alert(Alert.AlertType.WARNING);
alerta.setTitle("Advertencia");
alerta.setHeaderText(null);
alerta.setContentText(mensaje);
alerta.showAndWait();
}
private void mostrarConfirmacion(String mensaje) {
Alert confirmacion = new Alert(Alert.AlertType.INFORMATION);
confirmacion.setTitle("Registro Exitoso");
confirmacion.setHeaderText(null);
confirmacion.setContentText(mensaje);
confirmacion.showAndWait();
}
private void limpiarFormulario(TextField txtNombre, TextField txtApellido,
TextField txtEmail, PasswordField txtPassword,
CheckBox chkTerminos, ToggleGroup grupoGenero) {
txtNombre.clear();
txtApellido.clear();
txtEmail.clear();
txtPassword.clear();
chkTerminos.setSelected(false);
grupoGenero.selectToggle(null);
}
public static void main(String[] args) {
launch(args);
}
}
Al ejecutar este código, obtendrás un formulario de registro completo con validación básica de campos.
Resumen
En este artículo has aprendido los fundamentos del desarrollo de interfaces gráficas con JavaFX, desde la estructura básica de una aplicación hasta la creación de formularios interactivos. Hemos explorado los distintos contenedores para organizar elementos (BorderPane, GridPane, HBox, VBox), los controles de interfaz más comunes (botones, campos de texto, casillas, menús), el manejo de eventos para responder a acciones del usuario, y cómo aplicar estilos con CSS para mejorar la apariencia.
JavaFX representa una herramienta poderosa para crear aplicaciones Java con interfaces modernas y atractivas. A partir de aquí, puedes explorar características más avanzadas como animaciones, efectos visuales, gráficos y charts, o la integración con bases de datos que veremos en el siguiente artículo. La combinación de JavaFX con los conocimientos previos de Java te permitirá desarrollar aplicaciones completas y profesionales con una experiencia de usuario mejorada.