Codemaker Interfaces

Interfaces Tema 5

MigLayout

MigLayout es el layout que usaremos para todo, también en el proyecto final. No viene con Java (es una de las librerías de Preparar el ordenador), pero hace en un solo panel lo que con los layouts clásicos necesita varios anidados.

La idea: una rejilla#

MigLayout coloca los componentes en una rejilla de filas y columnas, como una tabla. Cada add(…) ocupa la celda siguiente, y tú decides cómo es cada columna:

MigLayout coloca los componentes en una rejilla de filas y columnas, con márgenes (insets) y huecos (gap)

Se configura con tres textos al crearlo:

Java
new MigLayout("wrap 2, insets 20", "[right][grow, fill]", "")
//             └ 1. el panel ┘     └ 2. las columnas ┘   └ 3. las filas
TextoQué diceEn el ejemplo
1. Del panelReglas generaleswrap 2: salta de fila cada 2 componentes; insets 20: 20 px de margen
2. De las columnasUna pareja de corchetes […] por columna, con sus reglas[right]: la primera alineada a la derecha; [grow, fill]: la segunda crece y estira lo que tiene
3. De las filasIgual, para las filas"": nada especial

Y cada componente puede llevar sus propias reglas, como segundo dato del add: panel.add(boton, "span 2").

Un formulario#

Java
import javax.swing.*;
import com.formdev.flatlaf.FlatLightLaf;
import net.miginfocom.swing.MigLayout;

public class FormularioMig {
    public static void main(String[] args) {
        FlatLightLaf.setup();
        SwingUtilities.invokeLater(() -> {
            JFrame ventana = new JFrame("Nuevo alumno");
            ventana.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            // 2 columnas: la primera alineada a la derecha, la segunda crece
            JPanel panel = new JPanel(new MigLayout("wrap 2, insets 20", "[right][grow, fill]"));

            panel.add(new JLabel("Nombre"));
            panel.add(new JTextField());
            panel.add(new JLabel("Edad"));
            panel.add(new JSpinner(new SpinnerNumberModel(16, 6, 99, 1)), "growx 0");
            panel.add(new JLabel("Nivel"));
            panel.add(new JComboBox<>(new String[] {"Junior", "Senior", "Master", "DEV"}));
            panel.add(new JLabel("Observaciones"), "top");
            panel.add(new JScrollPane(new JTextArea(4, 20)));

            panel.add(new JButton("Cancelar"), "span 2, split 2, align right");
            panel.add(new JButton("Guardar"));

            ventana.add(panel);
            ventana.setSize(460, 330);
            ventana.setLocationRelativeTo(null);
            ventana.setVisible(true);
        });
    }
}

El formulario «Nuevo alumno»: etiquetas a la izquierda alineadas a la derecha, campos a la derecha que ocupan todo el ancho, y los botones Cancelar y Guardar abajo a la derecha

Fíjate en tres detalles:

CódigoQué consigue
"growx 0" en el JSpinnerQue el número no se estire a todo el ancho, aunque la columna sea fill
"top" en «Observaciones»Que la etiqueta quede arriba, a la altura de la primera línea del texto, y no centrada
"span 2, split 2, align right" en CancelarQue la última fila ocupe las 2 columnas (span 2), que en esa celda quepan 2 botones (split 2) y que vayan a la derecha

Consejo

Si no entiendes por qué algo sale donde sale, añade debug a las reglas del panel: new MigLayout("wrap 2, insets 20, debug", …). MigLayout dibuja las celdas con líneas de colores:

El mismo formulario con debug: cada celda rodeada por una línea discontinua roja y cada componente por una verde

Las reglas más usadas#

Del componente, en el add:

Las restricciones más usadas de MigLayout: wrap, span, skip, split, growx y push

ReglaQué haceEjemplo
wrapDespués de este, salta a la fila siguientepanel.add(campo, "wrap")
span NOcupa N celdas seguidas"span 2"
skipDeja una celda vacía antes"skip"
split NMete N componentes en una sola celda"split 2"
growx / growySe estira a lo ancho / a lo alto"growx" (y "growx 0" para que no se estire)
align rightLo alinea dentro de su celda"align right", "align center"
gapleft pushLo empuja al extremo derecho"gapleft push"
topLo pega arriba en su celda"top"
w 200!Ancho fijo, de 200 píxeles"w 200!" (y h 40! para el alto)

Del panel, en el primer texto:

ReglaQué hace
wrap NSalta de fila automáticamente cada N componentes
insets NMargen de N píxeles entre el borde y la rejilla
gap X YHueco entre celdas: X en horizontal, Y en vertical
fillQue la rejilla ocupe todo el panel
debugDibuja las celdas, para entender qué pasa

De las columnas (y filas), entre corchetes:

ReglaQué hace
[right], [center]Alinea lo que hay en la columna
[grow]La columna crece cuando la ventana se agranda
[fill]Lo que hay dentro se estira a todo el ancho de la columna
[200!]La columna mide exactamente 200 píxeles
[sg]Todas las columnas con sg miden lo mismo

Un panel de tarjetas#

Con lo mismo se hacen las pantallas de un dashboard, como el del proyecto final: cuatro columnas iguales, y cada tarjeta dice cuántas ocupa.

Java
import java.awt.Color;
import javax.swing.*;
import com.formdev.flatlaf.FlatLightLaf;
import net.miginfocom.swing.MigLayout;

public class PanelMig {
    public static void main(String[] args) {
        FlatLightLaf.setup();
        SwingUtilities.invokeLater(() -> {
            JFrame ventana = new JFrame("Panel de la academia");
            ventana.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            // 4 columnas iguales (sg = mismo tamaño) que crecen con la ventana
            JPanel panel = new JPanel(new MigLayout("insets 16, gap 12",
                    "[grow, fill, sg][grow, fill, sg][grow, fill, sg][grow, fill, sg]",
                    "[][grow, fill]"));

            panel.add(tarjeta("Alumnos", "128"));
            panel.add(tarjeta("Matrículas", "34"));
            panel.add(tarjeta("Cursos", "6"));
            panel.add(tarjeta("Media", "7,4"), "wrap");

            panel.add(tarjeta("Gráfica", "(ocupa 3 columnas)"), "span 3");
            panel.add(tarjeta("Avisos", "(1 columna)"));

            ventana.add(panel);
            ventana.setSize(720, 360);
            ventana.setLocationRelativeTo(null);
            ventana.setVisible(true);
        });
    }

    // Una "tarjeta": un panel con un título pequeño y un dato grande
    static JPanel tarjeta(String titulo, String dato) {
        JPanel t = new JPanel(new MigLayout("wrap, insets 14"));
        t.setBackground(Color.WHITE);
        t.setBorder(BorderFactory.createLineBorder(new Color(0xDCE4EE)));
        JLabel valor = new JLabel(dato);
        valor.setFont(valor.getFont().deriveFont(20f));
        t.add(new JLabel(titulo));
        t.add(valor);
        return t;
    }
}

Un panel con cuatro tarjetas pequeñas arriba (Alumnos, Matrículas, Cursos y Media) y debajo una tarjeta ancha de tres columnas y otra de una

Las tarjetas son a su vez JPanel con su propio MigLayout: un panel dentro de otro, pero ahora cada uno con muy poco código.

Errores frecuentes#

Mensaje o síntomaQué significaCómo se arregla
cannot find symbol en MigLayoutFalta el import o la libreríaimport net.miginfocom.swing.MigLayout;, y los dos .jar en lib
IllegalArgumentException: Error parsing Constraint: 'grwox'Una regla mal escrita (aquí, grwox en vez de growx). Salta al ejecutar, no al compilarCorrige la palabra que dice el mensaje
Todo sale en una sola filaFalta wrap, en el panel (wrap 2) o en el último componente de cada filaAñade el wrap
Los campos no se estiran al agrandar la ventanaLa columna no tiene grow y fill"[right][grow, fill]"
No entiendes por qué algo sale donde sale—Añade debug al panel y mira las celdas