Swing, la solución actual de Java para crear GUIs

Marcela Calderón
Emilio Davis



Introducción:

El paquete Swing es parte de la JFC (Java Foundation Classes) en la plataforma Java. La JFC provee facilidades para ayudar a la gente a construir GUIs. Swing abarca componentes como botones, tablas, marcos, etc...

Las componentes Swing se identifican porque pertenecen al paquete javax.swing.

Swing existe desde la JDK 1.1 (como un agregado). Antes de la existencia de Swing, las interfaces gráficas con el usuario se realizaban a través de AWT (Abstract Window Toolkit), de quien Swing hereda todo el manejo de eventos. Usualmente, para toda componente AWT existe una componente Swing que la reemplaza, por ejemplo, la clase Button de AWT es reemplazada por la clase JButton de Swing (el nombre de todas las componentes Swing comienza con "J").

Las componentes de Swing utilizan la infraestructura de AWT, incluyendo el modelo de eventos AWT, el cual rige cómo una componente reacciona a eventos tales como, eventos de teclado, mouse, etc... Es por esto, que la mayoría de los programas Swing necesitan importar dos paquetes AWT: java.awt.* y java.awt.event.*.

Ojo: Como regla, los programas no deben usar componenetes pesados de AWT junto a componentes Swing, ya que los componentes de AWT son siempre pintados sobre los de Swing. (Por componentes pesadas de AWT se entiende Menu, ScrollPane y todas las componentes que heredan de las clases Canvas y Panel de AWT).

Compilación: Estándar.



Estructura básica de una aplicación Swing.


Una aplicación Swing se construye mezclando componentes con las siguientes reglas.

Ejemplo 1:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class HolaMundoSwing {
    public static void main(String[] args) {
        JFrame frame = new JFrame("HolaMundoSwing");
        final JLabel label = new JLabel("Hola Mundo");
        frame.getContentPane().add(label);

        //frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.addWindowListener(new java.awt.event.WindowAdapter(){
           public void windowClosing(WindowEvent e){
             System.exit(0);
           }
        }
        );

        frame.pack();
        frame.setVisible(true);
    }
}

En las primeras líneas se importan los paquetes necesarios.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
Luego se declara la clase HolaMundoSwing y en el método main se setea el top level container:
public class HolaMundoSwing {
    public static void main(String[] args) {
        JFrame frame = new JFrame("HolaMundoSwing");
        ...
        frame.pack();
        frame.setVisible(true);
    }
}

En el ejemplo, sólo hay un contenedor de alto nivel, un JFrame. Un frame implementado como una instancia de la clase JFrame es una ventana con decoraciones, tales como, borde, título y botones como íconos y para cerrar la ventana. Aplicaciones con un GUI típicamente usan, al menos, un frame.

Además, el ejemplo tiene un componente, una etiqueta que dice "Hola Mundo".

        final JLabel label = new JLabel("Hola Mundo"); //construye el JLabel.
        frame.getContentPane().add(label);             //agrega el label al frame.

Para que el botón de cerrar cierre la ventana, hay dos opciones:

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);       //JDK 1.3+

        frame.addWindowListener(new java.awt.event.WindowAdapter(){ //versiones anteriores
          public void windowClosing(WindowEvent e){
            System.exit(0);
          }
        }
        );



Principales aspectos de una aplicación Swing

Como ya se dijo antes, cada aplicación Swing debe tener al menos un top-level container que contendrá toda la aplicación, estos pueden ser

A un contenedor se le pueden agregar otros contenedores o componentes simples, para ver una muestra visual de cada componente y sus respectivas descripciones presione aquí

Configurando el "Look & Feel" de una aplicación

Swing permite setear dinámicamente el aspecto y la forma de interacción de una aplicación, por ejemplo el siguiente código setea el L&F al estilo Java

UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName())

Existen los siguientes L&F:

Configurando el "Layout" de un contenedor

Varias componentes pueden contener otras componentes dentro, para determinar de qué modo esas componentes se organizarán visualmente se utiliza el concepto de Layout. Por ejemplo, el siguiente código crea un frame y setea el layout del paño de contenidos (porción central de la ventana) con BorderLayout y finalmente agrega una etiqueta al sur de la ventana.

	JFrame frame= new Jframe();
	frame.getContentPane().setLayout(new BorderLayout());
	JLabel label=new JLabel("yo voy abajo");
	frame.add(label, BorderLayout.SOUTH);

A continuación se muestran los diferentes layout estándares de swing.

Manejando eventos

Cada vez que el usuario interactúa con la aplicación se gatilla un evento, para que una componente determinada reaccione frente a un evento, debe poseer un "escuchador" con, al menos, un método determinado que se ejecutará al escuchar un evento en particular, por ejemplo, en el siguiente código se crea un botón que al ser presionado imprime en la salida estándar un mensaje.

	JButton boton=new JButton("Un botón");
	boton.addActionListener(new ActionListener(){
	    public void actionPerformed(ActionEvent e){
		    System.out.println("Me apretaron");
		}
	});

Swing puede generar un variado set de eventos, en la siguiente tabla se resumen los más comunes con sus respectivos "escuchadores".

Ejemplos de eventos y sus escuchadores
Acción que gatilla un evento Tipo de escuchador
El usario hace un click, presiona Return
en un área de texto o selecciona un menú
ActionListener
El usuario escoje un frame (ventana principal) WindowListener
El usuario hace un click sobre una componente MouseListener
El usuario pasa el mouse sobre una componente MouseMotionListener
Una componente se hace visible ComponentListener
Una componente adquiere el foco del teclado FocusListener
Cambia la selección en una lista o tabla ListSelectionListener

Threads y Swing

Como regla general sólo un thread debe accesar una componente Swing, para lograr esto, la receta es que sólo el thread que atiende los eventos invoque los métodos de las componentes.

El siguiente código sigue el patrón "The Single-Thread Rule".

//Ejemplo Thread-safe
public class MyApplication {
    public static void main(String[] args) {
        JFrame f = new JFrame(...);
        ...//agregar componentes al frame
        f.pack();
        f.setVisible(true);
        //No hacer mas GUI
    }

    ...
    //Toda la manipulación de la GUI -- setText, getText, etc. 
    //es efectuada por manejadores de eventos como actionPerformed().
    ...
}

Nota: algunos métodos dentro de la API de Swing están marcados como "thread safe", éstos pueden ser invocados concurrentemente por cualquier thread, el texto que indica esto es: "This method is thread safe, although most Swing methods are not."




En la siguente sección veremos un ejemplo más complejo que servirá para mostrar parte de la potencialidad de Swing

Ejemplo 2: Simple editor de texto