Programación Avanzada

Interfaces gráficas de usuario

Swing: Contenedores y componentes

Introducción

Java proporciona dos bibliotecas de clases para crear interfaces gráficas de usuario: AWT y Swing.

En esta presentación veremos cuales son las características de cada una de ellas, y el modelo de contenedores y componentes gráficos de usuario que ponen a nuestra disposición para crear interfaces gráficas de usuario.

Bibliografía

Contenidos

  1. Interfaces gráficas de usuario en el paquete estándar de Java: AWT y Swing.
  2. ¿Qué es Swing y qué me proporciona?
  3. Contenedores y componentes.
  4. Contenedores disponibles.
  5. No bloquees el hilo de ejecución principal.
  6. Organización de componentes en contenedores.

AWT y Swing

En el paquete estándar de Java, contamos con tres opciones para crear interfaces gráficas de usuario:

  1. AWT -Abstract Window Toolkit.
  2. Swing.
  3. JavaFX.

AWT es una biblioteca pesada -heavy weight-, mientras que Swing es una biblioteca ligera -light weight- de componentes. JavaFX permite crear intefaces gráficas de usuario tanto para aplicaciones de escritorio como para la web y dispositivos móviles.

AWT y Swing

La idea de pesada o ligera, en este caso, está relacionada con la dependencia de Java con el sistema operativo, para visualizar y gestionar los elementos de la interface gráfica de usuario.

En el caso de AWT, la creación, visualización y gestión de los elementos gráficos depende del SO. Es el propio SO quien dibuja y gestiona la interacción sobre los elementos.

En el caso de Swing, es Java quien visualiza y gestiona la interacción del usuario sobre los elementos de la interface gráfica.

AWT y Swing

Aquí tienes la aplicación de ejemplo MVC en Mac OSX programada con AWT:

Y programada con Swing:

Aparentemente la única diferencia está en el tamaño de la caja de texto.

AWT y Swing

En el caso de Windows, esta es la versión programada con AWT:

Y programada con Swing:

Ahora las diferencias son evidentes.

AWT y Swing

En el caso de Linux, esta es la versión programada con AWT:

Y programada con Swing:

Las diferencias también son evidentes.

AWT y Swing

Como has podido observar en los ejemplos, al utilizar AWT el aspecto de los componentes depende del SO sobre el que se ejecute la aplicación.

AWT y Swing

En el caso de Swing, el aspecto es más parecido, independientemente del SO sobre el que se ejecute la aplicación:

AWT y Swing

En el caso de Swing, podemos elegir incluso entre varios temas -Look And Feel- en la jerga Swing.

AWT y Swing

Esta posibilidad nos permite que, con bastante aproximación, el aspecto de la aplicación sea independiente de SO:

AWT y Swing

Para conocer los Look and Feel disponibles:

LookAndFeelInfo info[] = UIManager.getInstalledLookAndFeels();
for(LookAndFeelInfo look: info)
    System.out.println(look.getClassName());

Para seleccionar un Look and Feel en particular:

UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel");

La última línea de código lanza varias excepciones.

AWT y Swing

Importante

Existen clases con el mismo cometido tanto en AWT como en Swing. Por ejemplo, en ambas bibliotecas tenemos un clase para crear ventana Frame en el caso de AWT y JFrame en el caso de Swing. Fíjate que si es una clase del paquete Swing su nombre empieza por J.

Existen otras clases que son del paquete AWT pero se utilizan en Swing, por ejemplo, los eventos y escuchadores. Como no tienen representación gráfica, en Swing se reaprovechan los de AWT.

Qué es Swing

Swing es una biblioteca de clases que permite crear interfaces gráficas de usuario en Java.

Swing forma parte del paquete estándar, no hace falta importar ningún fichero adicional en nuestros proyectos.

Qué es Swing

Swing no es la única alternativa para crear interfaces gráficas de usuario en Java, SWT -Standard Widget Toolkit- es una biblioteca, desarrollada por IBM, con la que también podemos crear interfaces gráficas de usuario.

Eclipse utiliza SWT para construir su interfaz gráfica.

Qué es Swing

JavaFX es la apuesta actual de Oracle la comunidad de código abierto en Java para construir interfaces gráficas de usuario multi-dispositivo.

La tendencia actual en la construcción de interfaces gráficas de usuario es mantener la misma calidad, y riqueza de componentes, tanto si el cliente es de escritorio, como un navegador, un dispositivo móvil, la televisión, etcétera.

Imagen: http://docs.oracle.com/javafx/2/layout/img/anchor_in_border_small.png

Qué es Swing

El número de clases dentro de la biblioteca Swing es muy grande enorme.

Nosotros vamos a estudiar una parte pequeña de ella, pero que nos permitirá construir interfaces gráficas de usuario muy completas.

Swing está orientado a objetos, y usa el patrón de diseño Observador/Observable para informar de las interacciones del usuario sobre la interface gráfica de usuario.

Contenedores y componentes

Existen dos elementos básicos para la creación de interfaces gráficas de usuario usando Swing:

  • Contenedores: Elementos capaces de albergar otros elementos.
  • Componentes: Elementos que se añaden a contenedores. Usualmente los componentes tienen aspecto gráfico, como un botón.

Contenedores y componentes

Swing proporciona tres tipos de contenedores de alto nivel. Esto significa que, cualquier otro contenedor que no sea de alto nivel, o componente, debe ir en su interior.

Estos tres contenedores de alto nivel son: JFrame, JDialog y JApplet

  • JFrame: Se visualiza como una ventana principal con marco y barra de título.

Contenedores y componentes

  • JDialog: Se visualiza como una ventana independiente de la ventana principal para mostrar información, como por ejemplo el contenido de un directorio.

Contenedores y componentes

  • JApplet: Permite crear aplicaciones con interface gráfica que se ejecutan en el contexto de un navegador web.

Contenedores y componentes

El primer ejemplo de contenedor: un JFrame:

JFrame ventana = new JFrame("Primera Ventana");
ventana.setSize(400, 400);
ventana.setVisible(true);

Contenedores y componentes

Detalles a tener en cuenta:

  • La ventana tiene barra de título y botones.
  • Le hemos dado unas dimensiones iniciales.
  • La hemos hecho visible.

Si pruebas a cerrar la ventana pulsando en el botón del marco comprobarás que esta no se cierra. Ya veremos cómo hacerlo.

Contenedores y componentes

Quizás, veas ejemplos donde se extiende a la clase JFrame para crear la ventana:

public class Ventana extends JFrame {
    private Ventana() {
        super("Primera Ventana");
    }
    private void ejecuta() {
        setSize(400, 400);
        setVisible(true);
    }
    public static void main(String[] args) {
        new Ventana().ejecuta();
    }
}

La desventaja de esta opción es que esta clase no puede extender a ninguna otra.

Contenedores y componentes

Una vez que hemos creado un contenedor, los componentes los añadiremos siguiendo las siguiente reglas:

  • Un componente se visualizará si lo hemos añadido a un contenedor.
  • Un componente sólo se puede añadir una vez a un contenedor.
  • Los componentes los debemos añadir al panel del contenedor.

Contenedores y componentes

Añadamos un primer componente, un botón.

JFrame ventana = new JFrame("Primera Ventana");
JButton boton = new JButton("Un botón");
ventana.getContentPane().add(boton);//Añadimos al panel de la ventana.
ventana.setSize(400, 400);
ventana.setVisible(true);

Contenedores y componentes

Detalles a tener en cuenta:

  • El botón lo añadimos al panel de la ventana principal.
  • El botón ocupa todo el tamaño de la ventana.
  • El botón se comporta como tal, pero no hace nada.

Recuerda: para añadir componentes, u otros contenedores, a la ventana principal, hay que hacerlo sobre su panel, y no directamente sobre la ventana.

No bloquees el hilo principal

Swing utiliza su propio hilo de atención a los eventos que se puedan producir en la interface gráfica de usuario. Es importante que no bloquees ese hilo. Para ello utiliza la siguiente técnica.

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            new Ventana().ejecuta();
        }
    });
}

Iremos aclarando el código anterior a medida que avancemos.

Organización de componentes

Como programadores, no hace falta que nos ocupemos de la organización de los componentes dentro de los contenedores.

Swing nos ofrece gestores de aspecto capaces de organizar, de manera automática, la posición y tamaño de los componentes dentro de los contenedores.

Los contenedores tienen asignado, por defecto, alguno de estos gestores de aspecto. En el caso de JFrame este gestor es BorderLayout.

Organización de componentes

Este es el comportamiento de este gestor de aspecto. Define 5 zonas, en cada una de las cuales sólo podemos añadir un componente.

Organización de componentes

Si no existe alguna de las zonas, la central se extiende hasta ocupar toda la ventana.

Organización de componentes

El código para generar las imágenes anteriores:

JFrame ventana = new JFrame("BorderLayout Manager");
Container contenedor = ventana.getContentPane();
contenedor.add(new JButton("BorderLayout.CENTER"), 
                            BorderLayout.CENTER);
contenedor.add(new JButton("BorderLayout.NORTH"), BorderLayout.NORTH);
contenedor.add(new JButton("BorderLayout.SOUTH"), BorderLayout.SOUTH);
contenedor.add(new JButton("BorderLayout.EAST"), BorderLayout.EAST);
contenedor.add(new JButton("BorderLayout.WEST"), BorderLayout.WEST);
ventana.setSize(500, 500);
ventana.setVisible(true);

Organización de componentes

Otro contenedor muy utilizado es JPanel.

Este contenedor no tiene ningún aspecto visual, sirve como contenedor de otros componenetes y contenedores.

Por defecto, JPanel tiene un FlowLayout como gestor de aspecto.

FlowLayout distribuye los componentes de izquierda a derecha y de arriba hacia abajo, a medida que los añadimos.

Organización de componentes

El código del ejemplo anterior:

JFrame ventana = new JFrame("FlowLayout Manager");
Container contenedor = ventana.getContentPane();

JPanel panel = new JPanel();//Este panel contiene los componentes.
panel.add(new JButton("Uno"));
panel.add(new JButton("Dos"));
panel.add(new JButton("Tres"));
panel.add(new JButton("Cuatro"));
panel.add(new JButton("Cinco"));
contenedor.add(panel);

ventana.setSize(200, 200);
ventana.setVisible(true);

Organización de componentes

Como hemos comentado, cada contenedor tiene un gestor de aspecto por defecto. Pero lo podemos cambiar:

JFrame ventana = new JFrame("FlowLayout Manager");
Container contenedor = ventana.getContentPane();
//Cambiamos el gestor de aspecto.
contenedor.setLayout(new FlowLayout());
contenedor.add(new JButton("Uno"));
contenedor.add(new JButton("Dos"));
contenedor.add(new JButton("Tres"));
contenedor.add(new JButton("Cuatro"));
contenedor.add(new JButton("Cinco"));

ventana.setSize(200, 200);
ventana.setVisible(true);

Organización de componentes

El aspecto final de la interface gráfica es el mismo que usando un JPanel.

Organización de componentes

Existen otros gestores de aspecto, por ejemplo GridLayout, que nos permite colocar los componentes en una rejilla:

Organización de componentes

El código del ejemplo anterior:

JFrame ventana = new JFrame("GridLayout Manager");
ventana.setLayout(new GridLayout(2, 3));
Container contenedor = ventana.getContentPane();
contenedor.add(new JButton("Uno"));
contenedor.add(new JButton("Dos"));
contenedor.add(new JButton("Tres"));
contenedor.add(new JButton("Cuatro"));
contenedor.add(new JButton("Cinco"));
ventana.setSize(500, 500);
ventana.setVisible(true);

En este caso, la rejilla es 2 filas por 3 columnas.

Organización de componentes

BoxLayout nos permite colocar los componentes vertical u horizontalmente:

Como ves, por defecto, los componentes se alinean a la izquierda.

Organización de componentes

El código del ejemplo anterior;

JFrame ventana = new JFrame("BoxLayout Manager");
Container contenedor = ventana.getContentPane();
contenedor.setLayout(new BoxLayout(contenedor, BoxLayout.PAGE_AXIS));
contenedor.add(new JButton("Uno"));
contenedor.add(new JButton("Dos"));
contenedor.add(new JButton("Tres"));
contenedor.add(new JButton("Cutro"));
contenedor.add(new JButton("Cinco"));
ventana.setSize(400, 400);
ventana.setVisible(true);

Organización de componentes

La alineación es propia de cada componente. Si quieres que los componentes se alineen en el centro:

Organización de componentes

El código del ejemplo anterior:

JButton boton = new JButton("Uno");
boton.setAlignmentX(Component.CENTER_ALIGNMENT);
contenedor.add(boton);
boton = new JButton("Dos");
boton.setAlignmentX(Component.CENTER_ALIGNMENT);
contenedor.add(boton);
boton = new JButton("Tres");
boton.setAlignmentX(Component.CENTER_ALIGNMENT);
contenedor.add(boton);
boton = new JButton("Cuatro");
boton.setAlignmentX(Component.CENTER_ALIGNMENT);
contenedor.add(boton);
boton = new JButton("Cinco");
boton.setAlignmentX(Component.CENTER_ALIGNMENT);
contenedor.add(boton);

Organización de componentes

Y aún existen otros muchos gestores de aspecto que puedes encontrar en Gestores de aspecto.

Otro detalle que quizás hayas notado, es que estamos dando unas dimensiones explícitas para el tamaño de la ventana.

Pero, Swing es lo suficientemente inteligente como para calcular el tamaño óptimo de la ventana, a partir de los componentes que contiene.

Para ello, basta sustituir:

ventana.setSize(400, 400);

Organización de componentes

Por:

ventana.pack();

Swing calcula el tamaño óptimo para que se vean todos los componentes que hemos añadido.

La ventaja es que, si fijamos nosotros el tamaño, puede que al cambiar de SO el aspecto no sea el que nosotros queremos, ya que, por ejemplo, el ancho del tipo de letra no es el mismo en todos los SOs.

Organización de componentes

Fíjate como cambia el aspecto del ejemplo de GridLayout cuando le pedimos a Swing que empaquete el contenido de la ventana, en vez de dar nosotros unas dimensiones.

Basándose en el texto de cada uno de los botones, Swing calculo un tamaño óptimo para ellos. Y, a partir del tamaño de los botones, Swing calcula el tamañan óptimo para la ventana.

Organización de componentes

Este es el aspecto que tiene la misma aplicación en Windows:

Fíjate, que en este caso, no existe espacio entre los botones. Compárala con la versión en Mac OSX:

Organización de componentes

La idea básica en el uso de contenedores, para crear la interface gráfica de tu aplicación, es que los contenedores se pueden anidar dentro de otros contenedores.

Organización de componentes

En ejemplo anterior, los botones horizontales se han añadido a un panel -JPanel dispone de un FlowLayout como gestor de aspecto por defecto-, y el panel a la región norte de la ventana.

JPanel panelNorte = new JPanel();
panelNorte.add(new JButton("Norte 1"));
panelNorte.add(new JButton("Norte 2"));
panelNorte.add(new JButton("Norte 3"));
contenedor.add(panelNorte, BorderLayout.NORTH);

Organización de componentes

Los botones verticales se han añadido a un panel al que se le ha modificado su gestor de aspecto, para que sea BoxLayout. Además, la alineación de cada uno de los botones se ha fijado como centrada.

Finalmente, el panel se ha añadido a la región central de la ventana.

Organización de componentes

JPanel panelCentro = new JPanel();
panelCentro.setLayout(new BoxLayout(panelCentro, BoxLayout.PAGE_AXIS));
JButton boton = new JButton("Centro 1");
boton.setAlignmentX(Component.CENTER_ALIGNMENT);
panelCentro.add(boton);
boton = new JButton("Centro 2");
boton.setAlignmentX(Component.CENTER_ALIGNMENT);
panelCentro.add(boton);
boton = new JButton("Centro 3");
boton.setAlignmentX(Component.CENTER_ALIGNMENT);
panelCentro.add(boton);
contenedor.add(panelCentro);

La idea básica es que puedes agrupar contenedores dentro de otros contenedores, de este modo, con un poco de experiencia, siempre conseguirás que el aspecto de tus aplicaciones sea el que tú deseas.

Resumen

AWT y Swing son dos de las bibliotecas disponibles en el paquete estándar de Java para la creación de interfaces gráficas de usuario.

AWT descansa sobre el SO para la creación de los componentes gráficos.

Swing abstrae el SO y proporciona su propia colección de componentes gráficos.

Swing proporciona dos tipos básicos de elementos gráficos, los contenedores y los componentes.

Los contenedores pueden contener otros contenedores o componentes.

Resumen

Los componentes, en su inmensa mayoría, son elementos con una representación gráfica: botones, cajas de edición de texto, etcétera.

Los componentes se organizan dentro de los contenedores a través de los gestores de aspecto (Layout Managers).

Anidando unos contenedores dentro de otros, y eligiendo los gestores de aspecto adecuados, podemos organizar los componentes de nuestra interfaz como quereamos.

Enlaces web