Interfaces Tema 4
Layouts
Un layout decide dónde va cada componente dentro de un panel, y cómo se recolocan cuando la ventana cambia de tamaño. Se podría colocar todo a mano, diciendo la posición exacta de cada cosa en píxeles, pero entonces la ventana no se adapta:
Por eso en Swing cada panel tiene su layout. Swing trae varios; estos son los clásicos:
| Layout | Cómo coloca | Para qué se usa |
|---|---|---|
FlowLayout | En fila, y salta de línea cuando no cabe | Una fila de botones |
BorderLayout | En cinco zonas: arriba, abajo, izquierda, derecha y centro | El esqueleto de una ventana |
GridLayout | En una cuadrícula de celdas iguales | Teclados, tableros |
CardLayout | Uno encima de otro, y solo se ve uno | Cambiar de pantalla (en el último tema) |
El layout se elige al crear el panel: new JPanel(new FlowLayout()).
FlowLayout: en fila#
import java.awt.FlowLayout;
import javax.swing.*;
import com.formdev.flatlaf.FlatLightLaf;
public class EjemploFlow {
public static void main(String[] args) {
FlatLightLaf.setup();
SwingUtilities.invokeLater(() -> {
JFrame ventana = new JFrame("FlowLayout");
ventana.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new FlowLayout());
for (int i = 1; i <= 6; i++) {
panel.add(new JButton("Botón " + i));
}
ventana.add(panel);
ventana.setSize(420, 140);
ventana.setLocationRelativeTo(null);
ventana.setVisible(true);
});
}
}
Es el layout que tiene un JPanel si no le pones ninguno. Para alinear a la izquierda o a la derecha: new FlowLayout(FlowLayout.LEFT) o FlowLayout.RIGHT.
BorderLayout: cinco zonas#
import java.awt.BorderLayout;
import javax.swing.*;
import com.formdev.flatlaf.FlatLightLaf;
public class EjemploBorder {
public static void main(String[] args) {
FlatLightLaf.setup();
SwingUtilities.invokeLater(() -> {
JFrame ventana = new JFrame("BorderLayout");
ventana.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new BorderLayout());
panel.add(new JButton("NORTH"), BorderLayout.NORTH);
panel.add(new JButton("SOUTH"), BorderLayout.SOUTH);
panel.add(new JButton("WEST"), BorderLayout.WEST);
panel.add(new JButton("EAST"), BorderLayout.EAST);
panel.add(new JButton("CENTER"), BorderLayout.CENTER);
ventana.add(panel);
ventana.setSize(420, 260);
ventana.setLocationRelativeTo(null);
ventana.setVisible(true);
});
}
}
Consejo
Es el layout que tiene un JFrame: por eso, en la primera ventana, ventana.add(saludo) ponía el texto en el centro y ocupando todo. Si no dices zona, va a CENTER.
GridLayout: una cuadrícula#
import java.awt.GridLayout;
import javax.swing.*;
import com.formdev.flatlaf.FlatLightLaf;
public class EjemploGrid {
public static void main(String[] args) {
FlatLightLaf.setup();
SwingUtilities.invokeLater(() -> {
JFrame ventana = new JFrame("GridLayout: calculadora");
ventana.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new GridLayout(4, 3, 6, 6)); // 4 filas, 3 columnas, 6 px de hueco
String[] teclas = {"7", "8", "9", "4", "5", "6", "1", "2", "3", "0", ".", "="};
for (String t : teclas) {
panel.add(new JButton(t));
}
ventana.add(panel);
ventana.setSize(260, 280);
ventana.setLocationRelativeTo(null);
ventana.setVisible(true);
});
}
}
Paneles dentro de paneles#
Una ventana de verdad casi nunca se hace con un solo layout: se meten paneles dentro de otros, y cada uno usa el que mejor le va.
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import javax.swing.*;
import com.formdev.flatlaf.FlatLightLaf;
public class EjemploAnidado {
public static void main(String[] args) {
FlatLightLaf.setup();
SwingUtilities.invokeLater(() -> {
JFrame ventana = new JFrame("Paneles dentro de paneles");
ventana.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Arriba: un título
JLabel titulo = new JLabel("Notas de la clase");
titulo.setFont(titulo.getFont().deriveFont(Font.BOLD, 18f));
// En el centro: una lista
JList<String> lista = new JList<>(new String[] {"Antonio: 7", "María: 9", "Juan: 6", "Elena: 8"});
// Abajo: una fila de botones, alineados a la derecha
JPanel botones = new JPanel(new FlowLayout(FlowLayout.RIGHT));
botones.add(new JButton("Borrar"));
botones.add(new JButton("Añadir"));
JPanel panel = new JPanel(new BorderLayout(0, 10));
panel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
panel.add(titulo, BorderLayout.NORTH);
panel.add(new JScrollPane(lista), BorderLayout.CENTER);
panel.add(botones, BorderLayout.SOUTH);
ventana.add(panel);
ventana.setSize(360, 300);
ventana.setLocationRelativeTo(null);
ventana.setVisible(true);
});
}
}
Funciona, pero en cuanto la ventana tiene un formulario con varias filas, hacen falta muchos paneles anidados. Para eso usaremos MigLayout, en el tema siguiente: un solo layout que hace todo esto y más.
Errores frecuentes#
| Síntoma | Qué pasa | Cómo se arregla |
|---|---|---|
| Solo se ve el último componente que has añadido a la ventana | En un BorderLayout, todo lo que añades sin zona va a CENTER, y cada uno tapa al anterior | Mete los componentes en un JPanel y añade el panel a la ventana |
En GridLayout, los botones salen enormes | GridLayout estira cada componente hasta llenar su celda | Mete el GridLayout en una zona que no crezca, o usa MigLayout |
setBounds o setLocation no hacen nada | El layout del panel decide las posiciones y pisa las tuyas | No coloques a mano: elige el layout adecuado |
| Has añadido algo con la ventana ya visible y no aparece | La ventana no se ha recolocado | Añade todo antes de setVisible(true), o llama a panel.revalidate() después |