32
6-. Interfaces de Usuario. - 77 - Capítulo Capítulo Capítulo Capítulo 6: Interfac 6: Interfac 6: Interfac 6: Interfaces de es de es de es de Usuario. Usuario. Usuario. Usuario.

Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

  • Upload
    ngonhan

  • View
    221

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 77 -

Capítulo Capítulo Capítulo Capítulo 6: Interfac6: Interfac6: Interfac6: Interfaces de es de es de es de Usuario.Usuario.Usuario.Usuario.

Page 2: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de
Page 3: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 79 -

6. Interfaces de Usuario.

En el proceso de interacción persona-ordenador, la Interfaz Gráfica de Usuario (GUI), es el artefacto que permite la interacción amigable con el sistema informático. La interfaz gráfica de usuario (en inglés Graphical User Interface, GUI) es un tipo de interfaz de usuario que utiliza un conjunto de imágenes y objetos gráficos (iconos, ventanas, tipografía) para representar la información y acciones disponibles en la interfaz. En este capítulo comentaremos las diferentes herramientas que se pueden utilizar para realizar interfaces de usuario; comenzaremos por describir el paquete AWT (Abstract Window Toolkit) de java; a continuación describiremos con mucho detalle el paquete Java Swing, que es el que ha sido utilizado finalmente para implementar la interfaz. Por último hablaremos de SWT, otra tecnología alternativa para la realización de interfaces gráficas que está adquiriendo cada vez más importancia.

6.1-. Paquete Java.awt.

El AWT o Abstract Window Toolkit es un conjunto de herramientas que permite el desarrollo de aplicaciones gráficas basadas en ventanas. El AWT contiene numerosas clases y métodos que permiten crear y gestionar dichas ventanas. Aunque el propósito principal del AWT es suministrar a las applets el soporte necesario para que trabajen con ventanas, también se puede utilizar para crear ventanas independientes que se ejecuten en un entorno gráfico independiente, como puede ser Windows 95/98/NT. Una descripción completa del AWT podría fácilmente ocupar un libro entero, es por eso que aquí sólo se darán unas nociones básicas que permitan entender su funcionamiento, limitaciones, y el por qué de la elección de Swing para el desarrollo de este proyecto. Esta descripición pretende también realizar una pequeña introducción a las librerías de creación de interfaces gráficas.

Java.awt es el paquete que maneja ventanas e interfaces gráficas, independientes (abstractas) del entorno operativo. Es un paquete muy importante, y está disponible en todas las plataformas Java.

Antes de seguir, es necesario aclarar a qué nos referimos con GUI. Si estamos utilizando un programa, es muy probable que en la parte superior exista un menú. Típicamente tendremos los elementos “Archivo”, “Ver”, “Ayuda”, etc. Aparte de esto, es muy probable que el programa disponga también de una serie de iconos. Por ejemplo, una carpeta para abrir un documento, un disquette para guardar un documento, una impresora que pulsaremos si queremos imprimir, etc. Todo esto constituye la interfaz gráfica de usuario o GUI. Este es el fin del paquete java.awt: dar el soporte necesario para crear un GUI. Es un paquete que permite por ejemplo definir botones y cajas de texto. Es más, dado que es un paquete Java, mantiene como es lógico la característica principal de este lenguaje de programación: la portabilidad. Definiremos pues botones o etiquetas portables a Windows, OS/2, Xwindows, o cualquier otro entorno gráfico, siempre usando el mismo código fuente, evidentemente.

Java AWT (Abstract Windows Toolkit) es la librería visual más antigua de java, pero hoy en día es mucho más utilizada la librería Swing. El AWT define ventanas en

Page 4: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 80 -

función de una jerarquía de clases que añade funcionalidad y carácter específico con cada nivel. Las dos ventanas más comunes son las que derivan de panel (utilizadas por las applets) y las que derivan de Frame (marco), que permiten crear ventanas estándar. La mayor parte de la funcionalidad de estas ventanas la heredan de sus superclases. Para poder entender esto es fundamental describir una jerarquía de clases que relacione ambas clases. De forma gráfica, esta jerarquía podría representarse como sigue:

Ilustración 3: Jerarquía de clases para Panel y Frame.

En la parte superior de la jerarquía de clases del AWT se encuentra la clase Component. Es una clase abstracta que encapsula todos los atributos de un componente visual. Todos los elementos del GUI que se visualizan en pantalla e interactúan con el usuario son subclases de Component. A continuación viene la clase Container, que es una subclase abstracta de Component y que contiene una serie de métodos adicionales que permiten que otros objetos Component aniden dentro de él. También se pueden anidar objetos Container dentro de otros objetos Container, puesto que también son instancias de Component. Esto hace que sea un sistema de contenidos completamente jerárquico.

La clase Panel es una subclase concreta de Container que no añade ningún método. Es la superclase de Applet. Básicamente, un objeto Panel es una ventana que no contiene barra de título, ni barra de menú, ni bordes. Esta es la razón por la que no se pueden ver estos elementos cuando una applet se ejecuta en un navegador. Cuando se visualiza con el visualizador de applets, es el visualizador el que proporciona el título y el borde.

La clase Window crea una ventana de nivel superior que no está contenida en ningún otro objeto y se encuentra directamente sobre el escritorio. En general, no se pueden crear objetos de la clase Window directamente. En su lugar se crean objetos Frame. La clase Frame (marco) es una subclase de Window y tiene una barra de título, una barra de menú, bordes y esquinas para cambiar el tamaño.

Por último, y aunque no aparezca en la jerarquía de clases mostrada anteriormente, hay que hablar de la clase Canvas. Esta clase encapsula una ventana vacía sobre la que se puede dibujar. Es, digamos, una superficie de dibujo.

Component

Container

Panel Window

Frame

Menu Container Interface

Page 5: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 81 -

Aparte de estas clases de alto nivel, existen otras clases que permiten al usuario interactuar con la aplicación de muy diversas formas. Se denominan “controles” y el AWT permite los siguientes tipos de controles:

- Etiquetas. - Botones. - Cuadros de comprobación. - Menús de opciones. - Listas. - Barras de desplazamiento. - Edición de texto.

Estos controles son de también subclases de Component. Excepto las etiquetas, que son controles pasivos, el resto de controles generan eventos cuando el usuario actúa sobre ellos. Por ejemplo, si el usuario pulsa un botón, se genera un evento que identifica el botón pulsado. Estos eventos hay que gestionarlos. El modelo de gestión de eventos del AWT es el mismo que utiliza Swing y será explicado un poco más tarde.

No vamos a explicar mucho más sobre el AWT ya que el proyecto ha sido desarrollado utilizando el paquete Swing. Sin embargo, es bueno realizar esta pequeña introducción al AWT para ir entrando en materia e ir comprendiendo un poco qué nos vamos a encontrar en el paquete Swing.

6.2-. Paquete Javax.swing.

6.2.1-. Origen.

Para comenzar a hablar de Swing, vamos a ver su origen, y para ello nombraremos el JFC. JFC es la abreviatura de Java Foundation Classes, que comprende un grupo de características para ayudar a construir interfaces gráficas de usuario (GUIs). El JFC incluye una serie de características, como pueden ser:

- Los componentes Swing: hablaremos a continuación de ellos. Para comenzar, diremos que incluye prácticamente todo lo que había en el AWT y más. Por ejemplo, tenemos desde botones hasta SplitPanes (que dividen la pantalla en zonas) o tablas.

- Soporte de Aspecto y Comportamiento Conectable: Le ofrece a cualquier componente Swing una amplia selección de aspectos y comportamientos (look and feel). Por ejemplo, el mismo programa puede usar el Aspecto y Comportamiento Java o el Aspecto y Comportamiento Windows.

- API de Accesibilidad: Permite tecnologías asistivas como lectores de pantalla o displays Braille para obtener información desde el interfaz de usuario.

Estas tres primeras características del JFC fueron implementadas sin ningún

código nativo, es decir, utilizando únicamente la API definido en el JDK 1.1. Cómo resultado, se convirtieron en una extensión del JDK 1.1. Esta versión fue liberada como JFC 1.1, que algunas veces es llamada 'Versión Swing'. La API del JFC 1.1 es conocido como la API Swing. Una curiosidad sobre el nombre: "Swing" era el nombre clave del proyecto que desarrolló los nuevos componentes. Aunque no es un nombre oficial, frecuentemente se usa para referirse a los nuevos componentes y al API relacionado.

Page 6: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 82 -

Está inmortalizado en los nombres de paquete de la API Swing, que empiezan con "javax.swing". En el JDK 1.2 se incluyeron otras características, como el Java 2D API que permite a los desarrolladores incorporar fácilmente gráficos 2D de alta calidad, texto e imágenes, o por ejemplo el soporte de Drag and Drop para “arrastrar y soltar” entre aplicaciones Java y aplicaciones nativas.

Dada la importancia de Swing, hoy en día no se plantea el tener un JDK sin las capacidades que proporciona este paquete.

6.2.2-. API Swing.

Swing consta de 16 paquetes, cada uno de los cuales tiene un propósito concreto que detallamos brevemente a continuación:

- javax.swing: Es el paquete de más alto nivel, que contiene los componentes,

adaptadores, los modelos por defecto de los componentes, y las interfaces para todos los modelos.

- javax.swing.border: Clases e interfaces que se usan para definir estilos de bordes

específicos. Observe que los bordes pueden ser compartidos por cualquier número de componentes Swing, ya que no son componentes por sí mismos.

- javax.swing.colorchooser: Contiene clases de soporte para el componente

seleccionador de color.

- javax.swing.event: Contiene los tipos de eventos y listeners (oyentes) específicos de Swing. Además, los componentes Swing pueden generar sus propios eventos.

- javax.swing.filechooser: Contiene clase de soportes para el componente

seleccionador de ficheros.

- javax.swing.plaf: (PLAF = pluggable look-and-feel) contiene las clases de Interfaz de Usuario que implementan los diferentes look-and-feel para los componentes. Éstas están orientadas a desarrolladores que, por una razón u otra, no pueden usar uno de los look-and-feel existentes.

- javax.swing.plaf.basic: Consiste en la implementación del Basic look-and-feel,

encima del cual se construyen los look-and- feels que provee Swing. Normalmente deberemos usar las clases de este paquete si queremos crear nuestro look-and-feel personal.

- javax.swing.plaf.metal: Metal es el look-and-feel por defecto de los componentes

Swing. Es el único look-and-feel que viene con Swing y que no está diseñado para ser consistente con una plataforma específica.

- javax.swing.plaf.multi: Es el Multiplexing look-and-feel. No se trata de una

implementación normal de look-and-feel ya que no define ni el aspecto ni el

Page 7: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 83 -

comportamiento de ningún componente. Más bien ofrece la capacidad de combinar varios look-and-feels para usarlos simultáneamente.

- javax.swing.table: Contiene las clases e interfaces para soportar el componente

tabla.

- javax.swing.text: Contiene las clases de soporte para la edición de texto.

- javax.swing.text.html: Contiene las clases de soporte para crear editores de texto HTML.

- javax.swing.text.html.parser: Soporte para analizar gramaticalmente HTML.

- javax.swing.text.rtf: Contiene soporte para documentos RTF.

- javax.swing.tree: Clases e interfaces que dan soporte al componente tree. Este

componente se usa para mostrar y manejar datos que guardan alguna jerarquía.

- javax.swing.undo: El paquete undo contiene soporte para implementar y manejar la funcionalidad deshacer/rehacer.

6.2.3-. Diferencias entre Swing y AWT.

Para empezar con diferencias sencillas, nos fijaremos en una diferencia sintáctica que salta a la vista: se pueden identificar los componentes Swing porque sus nombres empiezan por “J”. Por ejemplo, la clase del AWT que crea un botón se denomina Button, y la clase Swing se llama JButton. Los componentes AWT están en el paquete java.awt, mientras que los componentes Swing están en el paquete javax.swing.

Si profundizamos un poco más, podemos ver otras diferencias importantes. La mayor diferencia entre los componentes AWT y los componentes Swing es que éstos últimos están implementados sin nada de código nativo. Esto significa que los componentes Swing pueden tener más funcionalidad que los componentes AWT, porque no están restringidos a las características presentes en cada plataforma. Esto también significa que un botón Swing y un área de texto se verán y funcionarán idénticamente en cualquier plataforma (Macintosh, Solaris, Linux, Windows, etc.).

Las capacidades que tienen los componentes Swing son bastante más importantes que las que ofrecen los componentes del AWT. Por citar algunos ejemplos:

- Los botones y las etiquetas Swing pueden mostrar imágenes en lugar de o además

del texto. - Se pueden añadir o modificar fácilmente los bordes dibujados alrededor de casi

cualquier componente Swing. - Se puede modificar fácilmente el comportamiento o la apariencia de un

componente Swing llamando a métodos o creando una subclase que herede del componente en cuestión.

- Los componentes Swing no tienen por qué ser rectangulares. Por ejemplo, los botones pueden ser redondos.

Page 8: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 84 -

Otra característica importante de Swing es que se puede especificar el Aspecto y Comportamiento (Look & Feel) que utilice el GUI de nuestro programa (Java o Metal / Motif / Windows) en función de nuestras preferencias. Por el contrario, los componentes AWT siempre tienen el aspecto y comportamiento de la plataforma nativa.

Otro detalle interesante es que los componentes Swing con estado usan modelos para mantener el estado. Por ejemplo, un JSlider (barra de desplazamiento) usa un objeto BoundedRangeModel para contener su valor actual y un rango de valores legales. La gran ventaja que esto ofrece es que los modelos se configuran automáticamente, por eso no tenemos que tratar con ellos, a menos que queramos tomar ventaja de la potencia que pueden ofrecernos.

Existen una serie de reglas a la hora de utilizar componentes AWT y/o componentes Swing. Estas reglas son especialmente importantes si se van a utilizar ambos tipos de componentes simultáneamente.

- Como regla general, los programas no deberían usar componentne de “peso

pesado” junto con componentes Swing. Los componentes de peso pesado incluyen todos los componentes AWT listos para usar (como Menu y ScrollPane) y todos los componentes que desciendan de las clases Canvas y Panel del AWT. Esta restricción existe porque cuando un componente Swing (u otro componente de “peso ligero”) se solapa con componentes de peso pesado, éste último siempre se dibuja encima.

- Los componentes Swing no son de “thread seguro”. Si se modifica un componente Swing visible desde cualquier lugar que no sea el manejador de eventos, hay que seguir unos pasos especiales para hacer que la modificación se ejecute en el thread de despacho de eventos. Esto no es ningún problema para la mayoría de los programas Swing, ya que el código que modifica los componentes normalmente se encuentra en los manejadores de eventos y estos se ejecutan en el thread de despacho de eventos. (Veremos estos conceptos de eventos y manejador de eventos con más detalle en apartados posteriores.).

- La herencia de contenidos de cualquier ventana o applet que contenga componentes Swing debe tener un contenedor de alto nivel Swing como raíz del árbol. Por ejemplo, una ventana principal debería ser implementada como un ejemplar de JFrame en vez de como un ejemplar de Frame.

- No se añaden directamente los componentes a un contenedor de alto nivel como un JFrame. En su lugar, se añaden los componentes a un contenedor (llamado “panel de contenido”) que a su vez está contenido por el JFrame.

6.2.4-. Vista rápida por el código de un programa Swing.

Antes de entrar en más detalle sobre los elementos del paquete Swing, vamos a echar un vistazo al código de una aplicación Swing. En los siguientes apartados se darán explicaciones completas sobre los tópicos que aquí se introduzcan, sin embargo es interesante empezar por tener una visón global.

Veamos la ventana de la aplicación con la que vamos a tratar:

Page 9: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 85 -

Ilustración 4: Ejemplo de Aplicación Swing.

El funcionamiento es bien sencillo: cada vez que el usuario pulsa el botón, la

etiqueta de la ventana se actualiza. Para llegar a este resultado hay que realizar una serie de acciones. Comentamos a continuación los detalles más notorios del código correspondiente a este ejemplo.

- IMPORTAR PAQUETES SWING:

Los programas Swing necesitan los elementos del paquete principal de Swing que se importa mediante la línea:

import javax.swing.*;

Sin embargo, no es este el único paquete que necesitaremos por norma general. Es

muy común que los programas Swing también necesiten clases de los paquetes principales del AWT, ya que Swing utiliza el modelo de eventos de este último. Para poder utilizarlos se añaden las líneas siguientes:

import java.awt.*; import java.awt.event.*;

- ELEGIR EL ASPECTO Y COMPORTAMIENTO:

Como ya dijimos, el aspecto y comportamiento de nuestro programa es configurable. Para poder elegir el Look and Feel que deseemos tendremos que tratar con el conocido como “UIManager” o “delegado UI” (UI significa User Interface). Swing incluye varios conjuntos de delegados UI. Cada conjunto contiene implementaciones de UI para casi todos los componentes Swing y podemos llamar a estos conjuntos una implementación de look-and-feel o pluggable look-andfeel (PLAF).

Hay tres implementaciones de pluggable look-and-feel que descienden de Basic look-and- feel y son:

- Windows: com.sun.java.swing.plaf.windows.WindowsLookAndFeel. - CDE\Motif: com.sun.java.swing.plaf.motif.MotifLookAndFeel. - Metal\Multiplataforma (look and feel por defecto): javax.swing.plaf.metal.

MetalLookAndFeel. - MacLookAndFeel: Existe también este look-and-feel que simula las interfaces de

usuario de Macintosh, pero no viene con Java 2 y hay que descargarlo por separado.

Otro detalle a tener en cuenta es que las librerías de los Windows y Macintosh

pluggable look-and-feel sólo se soportan en la plataforma correspondiente. Realizar el cambio del look-and-feel de una aplicación es bien sencillo.

Únicamente hay que llamar al método setLookAndFeel() de UIManager, pasándole el

Page 10: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 86 -

nombre completo del LookAndFeel que vamos a usar. El código que se muestra a continuación se puede usar para llevar esto a cabo en tiempo de ejecución:

try { UIManager.setLookAndFeel( "com.sun.java.swing.plaf.motif.MotifLookAndFeel"); SwingUtilities.updateComponentTreeUI(myJFrame);

}catch (Exception e) { System.err.println("Could not load LookAndFeel");

} SwingUtilities.updateComponentTreeUI(……) informa a todos los hijos del

componente especificado que el look-and-feel ha cambiado y que necesitan reemplazar sus delegados UI por los del tipo especificado.

- CONFIGURAR EL CONTENEDOR DE ALTO NIVEL:

Es fundamental saber que todo programa que presente un GUI Swing contiene al menos un contenedor de alto nivel. Para la mayoría de los programas, los contenedores de alto nivel Swing son ejemplares de JFrame, JDialog o JApplet en el caso de las applets. Un contenedor de alto nivel existe principalmente para proporcionar el soporte que necesitan los componentes Swing para realizar su dibujado y manejo de eventos. Para ir conociendo un poco los elementos Swing podemos decir que un objeto JFrame (frame significa marco en Inglés), implementa una ventana principal, mientras que un objeto JApplet implementa un área de pantalla de un applet dentro de una ventana del navegador.

A continuación podemos ver un ejemplo de código en el que tenemos como contenedor de alto nivel un JFrame, y en el que se especifica que cuando el usuario cierre el frame, la aplicación finalice.

public class SwingApplication { ... public static void main(String[] args) { ... JFrame frame = new JFrame("SwingApplication"); //...crear los componentes que irán en el marco... //...incluirlos en un contenedor llamado contents... frame.getContentPane().add(contents,BorderLayout.CENTER); //Política de cierre de ventana frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } });

//Termina de configurar el frame y lo muestra. frame.pack(); frame.setVisible(true); }

Page 11: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 87 -

}

Como dijimos anteriormente, esto es una ligera introducción para que nos vayan sonando los conceptos. Muchos de ellos serán ampliamente desarrollados más adelante.

- CONFIGURAR LOS BOTONES Y LAS ETIQUETAS:

Dado que esto es un ejemplo sencillo, la aplicación sólo contiene un botón y una etiqueta. El resto de aplicaciones Swing tienen por norma general bastante más elementos, desde SplitPanes hasta CheckBoxes, pasando por TabbedPanes (paneles de pestañas) y otros componentes. Veremos diversos componentes más adelante, pero de momento nos centramos en los del ejemplo. El código que inicializa el botón es el siguiente:

JButton button = new JButton("I'm a Swing button!"); button.setMnemonic('i'); button.addActionListener(this);

En la primera línea se crea el botón con el texto “I´m a Swing button!”. A

continuación se añade un mnemónico al botón. Este mnemónico se utiliza para poder simular el click del ratón. En el caso del Look & Feel Metal, o en el Windows, simularíamos el click del ratón mediante el tecleo de Alt+i (i para este ejemplo ya que es la letra elegida como mnemónico en el código). La tercera línea añade el manejador de eventos, veremos más adelante qué son y para qué sirven los eventos.

El código para la etiqueta lo podemos ver a continuación:

// en el lugar de declaración de las variables de instancia:: private static String labelPrefix = "Number of button clicks: "; private int numClicks = 0; //en el código de inicialización del GUI: final JLabel label = new JLabel(labelPrefix + "0 "); //en el manejador de eventos de las pulsaciones de botón: label.setText(labelPrefix + numClicks);

Las primeras líneas son fáciles de interpretar, y la creación de la etiqueta es

bastante similar a la del botón. Sólo nos quedaría añadir al manejador de eventos la última línea en la que numClicks es una variable contador que irá acumulando el número de clicks que realicemos.

- AÑADIR COMPONENTES A LOS CONTENEDORES:

Si volvemos a mirar la imagen de la aplicación de ejemplo, vemos que la etiqueta y el botón están dentro de un marco o Frame. Sin embargo, los componentes no se pueden añadir directamente al frame, sino que hay que añadirlos antes a un contenedor. En este caso se ha utilizado un JPanel.

JPanel pane = new JPanel();

Page 12: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 88 -

pane.setBorder(BorderFactory.createEmptyBorder(30, 30, 10, 30)); pane.setLayout(new GridLayout(0, 1)); pane.add(button); pane.add(label);

Primero creamos el JPane, para a continuación decir qué tipo de borde y que

controlador de distribución queremos. El borde simplemente proporciona un espacio en blanco alrededor del panel. Los números son medidas en pixels.

En la tercera línea entra en juego el controlador de distribución. Éste fuerza al contenido del panel a dibujarse en una sola columna en este caso, por haber elegido GridLayout. Las dos últimas líneas añaden el botón button y la etiqueta label al panel. Esto significa que tanto el botón como la etiqueta serán controlados por el controlador de distribución que hemos asignado al panel, y será éste quien determine el tamaño y posición de cada componente.

- MANEJAR EVENTOS:

En el código que hemos visto hasta ahora hemos encontrado dos manejadores de eventos. Uno maneja las pulsaciones de botón y otro maneja los eventos de cierre de la ventana o frame. Desarrollaremos este punto con detalle en capítulos posteriores, pero veamos el código del ejemplo que corresponde al manejo de eventos (en este caso se definen en clases internas anónimas):

//Manejador de eventos para el botón . button.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent e) { numClicks++;

label.setText(labelPrefix + numClicks); }

}); //Manejador de eventos para el cierre de ventana. frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } });

- TRATAR CON LOS THREADS:

El programa de ejemplo es de thread seguro. Una vez q que su GUI es visible, en el manejador de eventos sólo ocurre manipulación del GUI (actualizar la etiqueta). Como el manejador de eventos se ejecuta en el mismo thread que realiza todo el manejo de eventos y pintado de la aplicación, no existe la posibilidad de que dos threads intenten manipular el GUI a la vez. Sin embargo, es fácil introducir problemas de threads en un programa Swing. Más adelante veremos algunos apartados sobre los hilos en Swing.

Page 13: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 89 -

6.2.5-. Estructura básica de una aplicación Swing.

En la aplicación de ejemplo hemos presentado algún componente Swing. En esta sección presentaremos con más detalles esos componentes así como otros componentes muy utilizados en Swing.

Los elementos que utilizamos en el ejemplo eran: un frame, o ventana principal (ejemplar de la clase JFrame), un panel, algunas veces llamado “pane” (clase JPanel), un botón (clase JButton) y una etiqueta (clase JLabel). Con este ejemplo vamos a poder estudiar bien la anatomía general de un programa Swing.

El frame es un contenedor de alto nivel. Como ya explicamos anteriormente, existe principalmente para proporcionar espacio para que se dibujen otros componentes Swing. En un programa Swing debe haber siempre un Contenedor de Alto Nivel para dar soporte al resto de componentes. Los contenedores de alto nivel más utilizados son:

- javax.swing.JFrame: una ventana independiente. - javax.swing.JApplet: un applet - Diálogos: ventanas de interacción sencilla con el usuario, como por ejemplo:

o javax.swing.JOptionPane: ventana de diálogo tipo SI/NO, SI/NO/ACEPTAR/CANCELAR, etc.

o javax.swing.JFileChooser: ventana para elegir un archivo. o javax.swing.JColorChooser: ventana para elegir colores.

El panel es un contenedor intermedio. Los Contenedores Intermedios tienen el único cometido de simplificar el posicionamiento de los otros componentes, en nuestro ejemplo del botón y la etiqueta. JPanel es un contenedor intermedio que no proporciona ninguna característica especial. Otros contenedores intermedios, como los paneles desplazables (JScrollPane) y los paneles con pestañas (JTabbedPane), juegan un papel más visible e interactivo en el GUI de un programa.

Por último llegamos a los Componentes Atómicos, que serían el botón y la etiqueta de nuestro ejemplo. Son componentes que existen no para contener otros componentes Swing como era el caso de los Contenedores Intermedios y de Alto Nivel, sino como entidades auto-suficientes que representan bits de información para el usuario. Frecuentemente, los componentes atómicos también obtienen entrada del usuario. El API Swing proporciona muchos componentes atómicos, incluyendo combo boxes (JComboBox), checkBoxes para elegir opciones (JCheckBox), campos de texto (JTextField), y tablas (JTable).

Veamos un diagrama de árbol que nos muestra la anatomía del programa. Es importante comentar que si añadimos una ventana -- por ejemplo, un diálogo -- la nueva ventana tendría su propio árbol de contenidos, independiente del mostrado en esta figura.

JFrame (contenedor de alto nivel) |

(Panel de contenido) |

JPanel (contenedor intermedio) | +----------------+ | |

JButton JLabel

Page 14: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 90 -

Incluso el programa Swing más sencillo tiene múltiples niveles en su árbol de

contenidos. La raíz del árbol de contenidos es siempre un contenedor de alto nivel que proporciona espacio para que los componentes Swing descendentes se dibujen. Todo contenedor de alto nivel contiene un contenedor intermedio conocido como “panel de contenido”. Cómo regla general, el panel de contenido contiene todos los componentes visibles en el GUI de la ventana. Sin embargo, existe una gran excepción a esta regla y es la siguiente: si el contenedor de alto nivel tiene una barra de menú (JMenuBar), entonces ésta se sitúa en un lugar especial fuera del panel de contenido.

Como curiosidad comentar que se puede ver el árbol de contenidos de cualquier frame o diálogo, pulsando el borde para seleccionarlo y pulsando a continuación Control-Shift-F1. Se escribirá una lista con el árbol de contenidos en el flujo de salida estándar.

Hemos dicho que unos componentes contienen a otros pero, ¿cómo se añaden los componentes a un contenedor? Pues es algo tan sencillo como utilizar el método add (añadir en inglés) en sus distintas formas. Este método tendrá siempre como argumento al menos el componente añadir, aunque dependiendo del método add que utilicemos podemos encontrar otros argumentos adicionales que especifiquen por ejemplo, el lugar de colocación del elemento a añadir. Concluyamos esta explicación con código:

frame = new JFrame(...); pane = new JPanel(); button = new JButton(...); label = new JLabel(...); pane.add(button); pane.add(label); frame.getContentPane().add(pane, BorderLayout.CENTER);

Con el código anterior creamos el contenedor de alto nivel (frame), el intermedio

(pane) y los elementos (button y label). Añadimos al panel el botón y la etiqueta mediante pane.add(…), y a continuación lo añadimos al contendor de alto nivel el panel. Más concretamente, la última línea especifica que el panel debería estar en el centro (CENTER) de su contenedor (el panel de contenido).

6.2.6-. Control de Distribución.

Las ilustración 5 muestra los GUIs de cinco programas, cada uno de ellos con cinco botones. Los botones son idénticos, y el código de los programas es también prácticamente idéntico. Entonces, ¿por qué parecen tan diferentes? Simplemente porque usan diferentes controladores de distribución para controlar el tamaño y posición de los botones.

Page 15: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 91 -

Ilustración 5: Controladores de Distribución.

Control de Distribución es el proceso de determinar el tamaño y posición de los componentes. Por defecto, cada contenedor tiene un controlador de distribución, es decir, un objeto que realiza el control de la distribución de los componentes que se alojen dentro del contenedor. Los componentes pueden indicarle al controlador sus preferencias de tamaño y alineamiento, pero es finalmente el controlador el que decidirá la posición final del componente, es él quien tiene la última palabra.

Existen cinco tipos de layout estándares en Java Swing:

- BorderLayout - BoxLayout - FlowLayout - GridLayout - GridBagLayout

En la figura anterior se muestra cómo cada uno de ellos mostraría diferentes componentes. Existe una sexta clase, CardLayout, que es un controlador de distribución de carácter general que se utiliza en combinación con otros controladores de distribución.

Anteriormente hablamos del método add. Siempre que se use el método add para poner un componente en un contenedor, debemos tener en cuenta el controlador de distribución del contenedor con el que estamos tratando. En general los controladores de distribución colocarán los componentes en el orden que se han ido añadiendo. Sin embargo, algunos controladores como BorderLayout requieren que especifiquemos la posición relativa del componente en el contenedor, usando un argumento extra para el método add. También hay otros como GridBagLayout que requieren elaborados

Page 16: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 92 -

procesos de configuración. Generalmente, sólo se tendrá que seleccionar el controlador de distribución de dos tipos de contenedores: paneles de contenido (que usan BorderLayout por defecto) y JPanel (que usa FlowLayout por defecto).

Tras esta introducción a los Controladores de Distribución vamos a ver algunas de las tareas más comunes para ellos:

- SELECCIONAR EL CONTROLADOR DE DISTRIBUCIÓN: Se puede cambiar el controlador que utiliza un contenedor por defecto. Sólo hay que llamar al método setLayout del contenedor. Por ejemplo, para hacer que un JPanel, que por defecto usa FlowLayout, utilice BorderLayout habría que escribir lo siguiente:

JPanel pane = new JPanel(); pane.setLayout(new BorderLayout());

Aunque es recomendable utilizar controladores de distribución, se puede realizar la distribución sin ellos. Seleccionando una propiedad de distribución del contenedor inicializada a nulo, podemos hacer que el contenedor no use ningún controlador de distribución. Esta técnica se denomina “posicionamiento absoluto” y con ella se puede especificar el tamaño y posición de cada componente dentro del contenedor. Una desventaja del posicionamiento absoluto es que no se ajusta bien cuando se redimensiona el contenedor de alto nivel. Tampoco se ajusta bien a las diferencias entres usuarios y sistemas, ni a los diferentes tamaños de fuente.

- PROPORCIONAR CONSEJOS SOBRE UN COMPONENTE:

Como ya hemos comentado, un componente puede decirle al controlador de distribución sus preferencias de tamaño y alineamiento, pero es el controlador de distribución el que decide en última instancia qué hacer. Para personalizar estas preferencias del componente podemos configurar sus tamaños máximo, mínimo y preferido mediante los métodos de selección de tamaño del componente que son setMinimumSize, setPreferredSize y setMaximumSize. Sin embargo esto no tiene por qué dar el resultado que desearíamos: actualmente, el único controlador de distribución en la plataforma Java que presta atención a la petición de tamaño máximo del componente es BoxLayout.

También existen métodos para especificar preferencias de alineamiento (setAlignmentX y setAlignmentY). Al igual que para el tamaño, BoxLayout es el único controlador de distribución que presta atención a los consejos de alineamiento.

- PONER ESPACIO ENTRE COMPONENTES:

Hay tres factores que influyen en la cantidad de espacio entre componentes visibles en un GUI:

o El controlador de distribución: Algunos ponen espacio entre componentes automáticamente, otros no. Algunos permiten especificar la cantidad de espacio que queremos dejar entre los componentes.

o Los Componentes Invisibles: Son componentes de peso ligero que ocupan espacio en el GUI, pero no realizan ningún dibujo. Podemos por tanto crear un componente invisible si queremos dejar un espacio en nuestro GUI.

o Bordes Vacíos: Es otra de las opciones para añadir espacio entre componentes. Consiste en algo tan sencillo como añadir bordes vacíos. Los mejores candidatos para los bordes vacíos son los que típicamente no tienen bordes,

Page 17: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 93 -

como los paneles y las etiquetas. Sin embargo, hay algunos otros componentes, como los paneles desplazables, que no funcionan bien con bordes en algunas implementaciones del Aspecto y Comportamiento, debido a la forma en que implementan su código de dibujo.

- CÓMO OCURRE EL CONTROL DE DISTRIBUCIÓN:

Mostramos un ejemplo de secuencia de control de distribución para un frame (JFrame). Es interesante leerlo por tener una idea de cómo funciona por dentro nuestro GUI, pero realmente son detalles de bajo nivel de los que, por lo general, no tendremos que ocuparnos nosotros.

1. Después de que el GUI esté construido, se llama al método pack sobre el JFrame. Esto especifica que el frame debería ser de su tamaño preferido.

2. Para encontrar el tamaño preferido del frame, el controlador de distribución añade el tamaño de los lados del frame al tamaño preferido del componente directamente contenido por el frame (en nuestro ejemplo inicial este elemento sería el JPanel). Esto es: la suma del tamaño preferido del panel de contenido más el tamaño de la barra de menú del frame, si existe.

3. El controlador de disposición del panel de contenido es responsable de imaginarse el tamaño preferido del panel de contenido. Por defecto, este controlador de disposición es un objeto BorderLayout. Sin embargo, asumamos que lo hemos reemplazado con un objeto GridLayout que se ha configurado para crear dos columnas. Lo interesante de Gridlayout es que fuerza a que todos los componentes sean del mismo tamaño, e intenta hacerlos tan anchos como la anchura preferida del componente más ancho, y tan altos como la altura preferida del componente más alto. Primero, el controlador Gridlayout pregunta al panel de contenido por su insets (el tamaño del borde del panel de contenido), si existe. Luego, el controlador de Gridlayout le pregunta a cada componente del panel de contenido sus tamaños preferidos, anotando la mayor anchura preferida y la mayor altura preferida. Por último calcula el tamaño preferido del panel de contenido.

4. Cuando a cada botón se le pide su tamaño preferido, el botón primero comprueba si el usuario ha especificado un tamaño preferido. Si es así, reporta este tamaño. Si no es así, le pregunta a su Aspecto y Comportamiento el tamaño preferido por defecto.

El resultado final es que para determinar el mejor tamaño de un frame, el sitema determina los tamaños de los contenedores en la parte inferior del árbol de contenidos. De forma similar ocurren los cálculos cuando se redimensiona el frame.

6.2.7-. Manejo de Eventos.

Cada vez que el usuario teclea un carácter o pulsa un botón del ratón, ocurre un evento. Cualquier componente puede ser notificado del evento. Todo lo que tiene que hacer es implementar el interfaz apropiado y ser registrado como “oyente de evento” del evento fuente apropiado.

Existen muchos tipos de eventos diferentes en Swing. En la siguiente tabla se muestran algunos ejemplos:

Page 18: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 94 -

Acción que resulta en el evento Tipo de oyente

El usuario pulsa un botón, presiona Return mientras teclea en un campo de texto, o elige un ítem de un menú.

ActionListener

El usuario selecciona una ventana principal. WindowListener

El usuario pulsa un botón del ratón mientras el cursor está sobre un componente.

MouseListener

El usuario mueve el cursor sobre un componente. MouseMotionListener

El componente se hace visible. ComponentListener

El componente obtiene el foco del teclado. FocusListener

Cambia la tabla o la selección de una lista. ListSelectionListener

Tabla 6: Eventos y Oyentes Asociados en Swing.

Cada evento está representado por un objeto que ofrece información sobre el evento e identifica la fuente (es decir, quién produjo dicho evento). Las fuentes de los eventos normalmente son componentes, pero otros tipos de objetos también pueden ser fuente de eventos.

Cada fuente de eventos puede tener varios oyentes registrados, es decir, varios objetos a la vez pueden estar escuchando una fuente de eventos, y serán notificados cuando un se produzca un evento en esta fuente. Inversamente, un oyente puede registrarse con varias fuentes de eventos. Mostramos gráficamente lo explicado:

Objeto evento /----> oyente de evento Fuente de evento -----------------------------------> oyente de evento \----> oyente de evento Objeto evento /----> fuente de evento Oyente de evento -----------------------------------> fuente de evento \----> fuente de evento

- COMO IMPLEMENTAR UN MANEJADOR DE EVENTOS:

Todo manejador de eventos requiere tres partes de código. 1. La clase del manejador: debe o bien implementar una interfaz de oyente, o

bien descender de una clase que implementa una interfaz del oyente en cuestión. Por ejemplo:

public class MyClass implements ActionListener {

Page 19: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 95 -

2. El código que registra sobre uno o más componentes un ejemplar de la clase de manejo de eventos de un oyente. Por ejemplo:

someComponent.addActionListener(instanceOfMyClass);

En este código se está registrando una instancia de la clase de manejo de eventos (MyClass, definida en el punto 1) sobre el componente de nombre someComponent.

3. En el caso en que en el primer punto hayamos decidido implementar el

interfaz, ahora es necesaria la implementación de los métodos del interfaz oyente. Por ejemplo:

public void actionPerformed(ActionEvent e) { ...//código que reaccione a la acción... }

Veamos un ejemplo en el que iremos indicando los pasos descritos anteriormente.

Imaginemos un programa que tiene botones (JButton). El usuario pulsará el botón de la pantalla (o bien las teclas equivalentes al botón). Para poder detectar esta pulsación, el programa debe tener algún objeto que implemente el interfaz ActionListener (que es el correspondiente al evento “pulsación de botón”). Este será nuestro “manejador” u “oyente de action” (PASO 1).

El programa debe registrar este manejador como un oyente de Action del botón (PASO 2). Cuando el usuario pulse el botón, se producirá el evento Action, y será notificado a todos los elementos que están registrados a este tipo de evento de esta fuente, en este caso nuestro manejador. Esto resulta en una llamada al método ActionPerformed del oyente de action, que en este caso particular es el único método del interfaz ActionListener. El único argumento del método es un objeto ActionEvent, que representa al evento ocurrido y nos proporciona información tanto del evento como de su fuente.

Es decir, cuando el usuario pulsa un botón, se produce un evento ActionEvent y los oyentes de action del botón son notificados. Gráficamente: ActionEvent button ---------------------------------> action listener

Los manejadores de eventos pueden ser ejemplares de cualquier clase. Frecuentemente, se implementan usando clases internas anónimas. Son clases sin nombre definidas dentro de otras clases. Aunque las clases internas puedan hacer el código algo más confuso y parezcan difíciles de leer, realmente hacen el código mucho más fácil de entender, una vez que se han utilizado. Manteniendo una implementación de un manejador de eventos cerca de donde se registra el manejador de eventos, las clases internas ayudan tanto al programador como a los que siguen su código a encontrar fácilmente la implementación completa del manejador de eventos.

- LOS THREADS Y EL MANEJO DE EVENTOS: El código de manejo de eventos se ejecuta en un sólo thread: el thread de despacho

de eventos. Esto asegura que todo manejador de eventos se terminará de ejecutar antes

Page 20: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 96 -

de ejecutar otro. Es más, el código de dibujo también se realiza el thread de despacho de eventos y sigue la misma norma. Esto significa que, mientras se está ejecutando por ejemplo el método actionPerformed de la interfaz ActionListener citada anteriormente, el GUI del programa permanecerá congelado: no se dibujará nada ni tampoco se responderá a otros eventos como pulsaciones de ratón.

Hay que tener cuidado con el manejo de eventos. El código de manejo de eventos debería poder ejecutar cada pulsación, cada click de ratón, cada evento en resumen. De otro modo el rendimiento del programa se verá empobrecido. Si fuese necesario realizar una larga operación como consecuencia de un evento, la opción más acertada es arrancar un nuevo thread que realice dicha operación.

- LOS ADAPTADORES:

La mayoría de los interfaces de oyentes, al contrario que ActionListener,

contienen más de un método. Por ejemplo, el interface MouseListener contiene cinco métodos: mousePressed, mouseReleased, mouseEntered, mouseExited, y mouseClicked. Incluso si sólo te importan las pulsaciones, si tu clase implementa directamente la interfaz MouseListener, entonces debes implementar los cinco métodos de MouseListener. Aquellos métodos de eventos que no te interesan pueden tener los cuerpos vacíos. Aquí hay un ejemplo:

public class MyClass implements MouseListener { ... someObject.addMouseListener(this); ... // Definiciones vacías de métodos: public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mouseClicked(MouseEvent e) { //Supongamos que solo nos interesa este evento. //La implementación iría aquí. } } Desafortunadamente, la colección de cuerpos de métodos vacíos resultante puede

resultar dura de leer y de mantener. Para ayudarnos a evitar este emborronamiento del código con cuerpos de métodos vacíos, el AWT y Swing proporcionan una clase adapter por cada interfaz de oyente con más de un método. Por ejemplo, la clase MouseAdapter implementa el interfaz MouseListener. Una clase adaptador implementa versiones vacías de todos los métodos del interfaz.

Para usar un adaptador se crea una subclase, en vez de implementar directamente una interfaz de oyente. Por ejemplo, extendiendo la clase MouseAdapter, nuestra clase hereda definiciones vacías para los métodos que contiene MouseListener. Por ejemplo:

public class MyClass extends MouseAdapter { ... someObject.addMouseListener(this);

Page 21: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 97 -

... public void mouseClicked(MouseEvent e) { //Implementación del manejador para este evento. } } ¿Qué pasa si no queremos que nuestras clases de manejo de eventos desciendan de

una clase adaptador? Por ejemplo, supongamos que escribimos un applet, y queremos que nuestra subclase Applet contenga algún método para manejar eventos de ratón. Como el lenguaje Java no permite la herencia múltiple, nuestra clase no puede descender de las clases Applet y MouseAdapter a la vez. La solución es definir una clase interna (una clase dentro de nuestra subclase Applet) que descienda de la clase MouseAdapter, como se muestra a continuación.

//Ejemplo de utilización de clase interna. public class MyClass extends Applet { ... someObject.addMouseListener(new MyAdapter()); ... class MyAdapter extends MouseAdapter { public void mouseClicked(MouseEvent e) { //Implementación del manejador para este evento. } }

Otro ejemplo de uso de clases internas, en este caso con una clase interna

anónima: public class MyClass extends Applet { ... someObject.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { //Implementación del manejador para este evento. } }); ... } } Las clases internas funcionan bien incluso si nuestro manejador de eventos

necesita acceder a ejemplares de variables privadas de la clase que la encierra, aunque algunos compiladores no permiten el acceso a dichas variables privadas. Un atajo es eliminar el especificador private de la declaración del ejemplar de la variable. Siempre que no declaremos una clase interna como static, se podrá referir a ejemplares de variables y métodos como lo hace el resto de código que contiene la clase.

Page 22: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 98 -

6.2.8-. Dibujo.

En principio no es necesario conocer todos los detalles de dibujo del GUI pues el programador no tiene que encargarse prácticamente de nada con respecto a este aspecto. Sin embargo, puede ser interesante tener ciertas nociones para, en caso de que nuestros componentes no se dibujen correctamente, ser capaces de entender qué hay de erróneo en nuestra implementación. También es útil lo que se explica a continuación si queremos crear código de dibujo personalizado para un componente.

- CÓMO FUNCIONA EL DIBUJO: Cuando un GUI necesita dibujarse a si mismo (al iniciarlo, en respuesta a la vuelta

de un ocultamiento, o porque necesite reflejar un cambio en su estado), empieza con el componente más alto del árbol de contenidos que necesite ser redibujado, y va descendiendo. Todo esto lo controla el sistema de dibujo del AWT, y se ha hecho más eficiente mediante el manejador de dibujo de Swing que utiliza un doble buffer.

Al igual que el código de manejo de eventos, el código de dibujo se ejecuta en el thread del despacho de eventos. Mientras se esté manejando un evento no ocurrirá ningún dibujo. De forma similar, si la operación de dibujado tarda mucho tiempo, no se manejará ningún evento durante ese tiempo

Los componentes Swing generalmente se redibujan a sí mismos siempre que es necesario. Por ejemplo, cuando llamamos al método setText de un componente, el componente debería redibujarse automáticamente a sí mismo, y si es necesario, redimensionarse. Si no lo hace así es un bug. El atajo es llamar al método repaint sobre el componente para pedir que el componente se ponga en la cola para redibujado.

Los programas sólo deberían dibujarse cuando el sistema de dibujo se lo diga. Esto es así porque cada ocurrencia de dibujo de un componente debe ejecutarse sin ningún tipo de interrupción. Si no se hace así, podríamos tener resultados impredecibles e indeseados, como por ejemplo un botón que se dibuje medio pulsado y medio liberado.

Para acelerar y mejorar el rendimiento, el dibujo Swing usa doble-buffer por defecto. ¿En qué consiste este doble buffer? Se realiza el dibujo en un buffer fuera de pantalla y luego se lanza a la pantalla una vez finalizado. Otra opción para ayudar al rendimiento es hacer un componente Swing opaco. Así, el sistema de dibujo de Swing conocerá lo que no tiene que pintar detrás del componente. Para hacer opaco un componente Swing, se llama al método setOpaque(true) sobre el componente.

Los componentes no-opacos de Swing puede parecer que tienen cualquier forma, aunque su área de dibujo disponible es siempre rectangular. Por ejemplo, un botón podría dibujarse a sí mismo dibujando un octógono relleno. El componente detrás del botón, (su contenedor, comúnmente) sería visible, a través de las esquinas de los lados del botón. El botón podría necesitar incluir código especial de detección para evitar que ocurra un evento Action cuando el usuario pulsa en las esquinas del botón.

- UN EJEMPLO DE DIBUJO:

Si volvemos a nuestra aplicación de ejemplo, que no contenía nada más que un botón y una etiqueta, podemos describir cómo ocurre su dibujo.

Page 23: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 99 -

1. El contenedor de alto nivel, JFrame, se dibuja as sí mismo. 2. El panel de contenido primero dibuja su fondo, que es un rectángulo sólido de

color gris. Luego le dice al JPanel que se dibuje el mismo. El rectángulo del panel de contenido realmente no aparece en el GUI finalizado porque está oscurecido por el JPanel. Es importante que el panel de contenido sea opaco. De otro modo, resultará en dibujados confusos. Como un JPanel es opaco, podemos hacer que sea el panel de contenido (utilizando getContentPane(). add). Esto simplifica considerablemente el árbol de contenidos y el dibujado, eliminado un contenedor innecesario.

3. El JPanel primero dibuja su fondo, un rectángulo sólido de color gris. Luego dibuja su borde. El borde es un EmptyBorder, que no tendrá efecto excepto para incrementar el tamaño del JPanel reservando algún espacio extra en los laterales del panel como ya explicamos cuando hablamos del espacio entre componentes. Finalmente, el panel le pide a sus hijos, es decir, a los componentes que descienden de él en el árbol que se dibujen a sí mismos.

4. Para dibujarse a sí mismo, el JButton dibuja su rectángulo de fondo si es necesario y luego dibuja el texto que contiene. Si el botón tiene el foco del teclado, significa que cualquier cosa que se teclee va directamente al botón para su procesamiento (el botón realiza algún dibujado característico para aclarar que tiene el foco, específico del Aspecto y Comportamiento que se esté utilizando).

5. Para dibujarse a sí misma, la etiqueta JLabel dibuja su texto.

Resumiendo, un componente se dibuja a si mismo antes de dibujar a los componentes que contenga. De este modo nos aseguramos que una zona del fondo de un JPanel sólo queda dibujada si no tiene ningún componente hijo que la cubra. La siguiente figura ilustra el orden en que cada componente que desciende de JComponent se dibuja a sí mismo.

1. fondo (si es opaco)

2. dibujo personalizado (si existe)

3. borde (si existe)

4. hijos (si existen)

.............

.............

.............

.............

.............

.............

.............

....().......

.............

.............

.............

.............

============= =...()......= =...........= =...........= =...........= =============

============= =...()......= =.---------.= =.|JButton|.= =.---------.= =============

Ilustración 6: Orden en el dibujo de una aplicación Swing.

6.2.9-. Los threads y Swing.

Hasta ahora hemos nombrado el thread de despacho de eventos. También hemos hablado de “programas de thread seguro”. Vamos a intentar aclarar un poco todo esto. Si nuestro programa es un applet, lo más seguro es construir el GUI en el método init. Por el contrario, si nuestro programa es una aplicación, podemos usar el siguiente patrón común para estar seguros con respecto a los hilos:

Page 24: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 100 -

//Ejemplo de hilo seguro public class MyApplication { public static void main(String[] args) { JFrame f = new JFrame(...); ...//Añadir componentes al frame aquí. f.pack(); f.setVisible(true); //No realizar más trabajo para la GUI aquí. } ... //Toda la manipulación de la GUI (setText, getText, etc.)se realiza //en manejadores de eventos como por ejemplo actionPerformed(). ... }

Existe una regla para tratar con los hilos en Swing: “Una vez que se haya realizado un componente Swing, todo el código que pudiera

afectar o depender del estado de ese componente debería ejecutarse en el thread de despacho de eventos.”

Esta regla puede parecer complicada a primera vista, pero para la mayoría de programas sencillos no tenemos que preocuparnos de los threads. Es importante definir un par de términos antes de continuar la exposición: “realizado” y “thread de despacho de eventos”.

Realizado significa que el método paint del componente haya sido o podría ser llamado. Un componente Swing que sea una ventana de alto nivel se realiza habiendo llamado a uno de estos métodos sobre ella: setVisible(true), show, o pack. Una vez que una ventana se ha realizado, todos los componentes que contiene están realizados. Otra forma de realizar un componente es añadirlo a un componente que ya esté realizado.

El thread de despacho de eventos es el thead que ejecuta el código de dibujo y de manejo de eventos. Por ejemplo, los métodos paint y actionPerformed se ejecutan automáticamente en el thread de despacho de eventos. Otra forma de ejecutar código en el thread de despacho de eventos es usar el método invokeLater de SwingUtilities

Como para casi toda regla, existen algunas excepciones. Existen algunos métodos que se denominan “de thread seguro”. Podemos ver qué métodos son éstos en la documentación de la API Swing, ya que los métodos de “thread seguro” están marcados con dicho texto.

Podemos añadir otra excepción que dice: “La GUI de una aplicación frecuentemente puede ser construida y mostrada en el thread principal.”. Mientras no se haya realizado ningún componente, no hay problema en construir y mostrar un GUI en el hilo main. De hecho, en general se puede construir (pero no mostrar) un GUI en cualquier thread, mientras no se hagan llamadas que se refieran o afecten a los componentes ya realizados. Veámoslo en el ejemplo de hilo seguro anterior.

1. El ejemplo construye el GUI en el thread principal. 2. Los componentes del GUI son realizados por la llamada a pack. 3. Según la regla, “una vez realizado un componente Swing, todo el código que

pudiera afectar o depender del estado de ese componente debería ejecutarse en el thread de despacho de eventos.” Es decir, la llamada a setVisible, con la que

Page 25: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 101 -

son mostrados los componentes del GUI, es técnicamente insegura ya que los componentes han sido realizados con la llamada a pack y se está ejecutando este setVisible fuera del thread de despacho de eventos. Sin embargo, como el programa no ha hecho visible el GUI todavía, es sumamente contrario a que una llamada a paint ocurra antes de que retorne setVisible.

4. El thread principal no ejecuta código GUI después de llamar a setVisible. Esto significa que el código del GUI se mueve del thread principal al thread de despacho de eventos, y el ejemplo es, en la práctica, de thread seguro.

Si queremos ejecutar código en el thread de despacho de eventos, pero no desde

algún método de un Oyente, sino desde otros lugares de nuestro programa, podemos utilizar unos métodos de la clase SwingUtilities:

- invokeLater: Pide que algún código se ejecute en el thread de despacho de eventos. Este método retorna inmediatamente, sin esperar a que el código sea ejecutado.

- invokeAndWait: Actúa igual que invokeLater, excepto en que este método espera a que el código se ejecute. Como regla, deberíamos usar invokeLater en vez de este método.

Si no hablamos de aplicaciones sino de Applets, la técnica para construir un GUI

de hilo seguro consiste en construir y mostrar el GUI en el método init. Los navegadores existentes no dibujan el applet hasta después de que hayan sido llamados los métodos init y start. Así, construir el GUI en el método init del applet es seguro, siempre que no se llame a show() o setVisible(true) sobre el objeto applet actual. Por supuesto, los applets que usan componentes Swing deben ser implementados como subclases de JApplet, y los componentes deben ser añadidos al panel de contenido del JApplet, en vez de directamente al JApplet, es decir, el procedimiento es similar al explicado para las aplicaciones. Al igual que para cualquier applet, nunca deberíamos realizar inicialización que consuma mucho tiempo en los métodos init o start; en su lugar deberíamos arrancar un thread que realice las tareas que consuman tiempo.

6.2.10-. Más características Swing.

- Características adquiridas de JComponent: Todos los componentes Swing cuyo nombre empieza por J, salvo los contenedores de alto nivel, descienden de la clase JComponent y por tanto obtienen muchas características de esta clase. Por ejemplo, la posibilidad de tener bordes, tooltips (texto de ayuda que aparece al dejar el ratón quieto sobre un componente durante algunos segundos) y Look & Feel configurable.

- Iconos: Muchos componentes Swing, principalmente los botones y las etiquetas,

pueden mostrar algún tipo de dibujo o imagen. Para ello se utilizan objetos de la clase Icon.

- Actions: Estos objetos permiten compartir datos y estados entre dos o más

componentes que puedan generar eventos de tipo Action. Un ejemplo: un botón e

Page 26: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 102 -

un item de menú que realicen la misma función pueden ser implementados con este tipo de objeto.

- Aspecto y Comportamiento Conectable: Un programa, aunque sea muy sencillo,

puede tener uno o varios Aspectos y Comportamientos diferentes. Es más, podemos permitir que el usuario los determine, o determinarlos nosotros directamente mediante la programación.

- Soporte para tecnologías asistivas: Ya hemos nombrado esta característica Swing.

No entraremos mucho más en detalle de lo visto hasta ahora, pero merece la pena recordar que las tecnologías asistivas, como por ejemplo un lector de pantalla, también pueden usar la API Swing. La utilización de tecnologías asistivas puede expandir sin duda el mercado de nuestro programa.

- Modelos de Datos y Estados separados: La mayoría de los objetos Swing que no

son contenedores tienen un “modelo”. Por ejemplo, un botón (JButton) tiene un modelo (ButtonModel) que almacena el estado del botón: su mnemónico de teclado, si está activado/seleccionado/pulsado, etc. Otros componentes como JList tienen múltiples modelos: un ListModel para almacenar los contenidos de la lista y un ListSelectionModel que sigue la pista de la selección actual de la lista. En general no es necesario conocer los modelos, de hecho hay muchas veces en las que ni se utilizan. Sin embargo, existen porque ofrecen la posibilidad de trabajar con componentes más eficientemente y de compartir fácilmente datos y estados entre componentes.

6.2.11-. Sobre los componentes de texto en Swing. Dado que la aplicación que se desarrolla en este proyecto fin de carrera es una

herramienta para la edición de un tipo concreto de documento, debemos prestar especial atención a los Componentes de Texto de Swing en esta memoria.

Los componentes de texto muestran algún texto y opcionalmente permiten que el usuario lo edite. Los programas necesitan componentes de texto para tareas dentro del rango del sencillo (introducir una palabra y pulsar Enter) al complejo (mostrar y editar texto con estilos y con imagenes embebidas en un lenguaje asiático). Los paquetes Swing proporcionan cinco componentes de texto y proporcionan clases e interfaces para conseguir los requerimientos más complejos. Sin importar sus diferentes usos o capacidades, todos los componentes de texto Swing descienden de la misma superclase, JTextComponent, que proporciona una base poderosa y ampliamente configurable para la manipulación de texto. La ilustración 7 muestra una clasificación de los distintos componentes de texto Swing. Así mismo, mostramos una figura (ilustración 8) de una sencilla aplicación en la que se pueden observar estos componentes.

Page 27: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 103 -

Ilustración 7: Clasificación de los Componentes de Texto Swing.

Ilustración 8: Aplicación con Componentes de Texto Swing.

El siguiente cuadro define los tres grupos existentes de componentes de texto.

Grupo Descripción Clases Swing Controles de

Texto

Conocidos simplemente como campos de texto, los controles de texto pueden mostrar y editar sólo una línea de texto y están basados en action como los botones. Se utilizan para obtener una pequeña cantidad de información textual del usuario y toman algunas acciones después de que la entrada se haya completado.

JTextField y su subclase

JPasswordField

Plano

JTextArea, el único componentes de texto plano de Swing, puede mostrar y editar múltiples líneas de texto. Aunque un área de texto puede mostrar texto en cualquier fuente, todo el texto está en la misma fuente. Toda la edición de los componentes de texto plano se consigue a través de la manipulación directa del texto con el teclado y el ratón, por esto los componentes de texto plano son más fáciles de

JTextArea

Page 28: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 104 -

configurar y utilizar que los componentes de texto formateado. También, si la longitud del texto es menor de unas pocas páginas, podemos fácilmente utilizar setText y getText para recuperar o modificar el contenido del componente con una simple llamada a un método.

Formateado

Un componente de texto formateado puede mostrar y editar texto usando más de una fuente. Algunos componentes de texto formateado permiten incluir imágenes e incluso componentes. Típicamente se tendrán que hacer más programación para usar y configurar componentes de texto formateado, porque muchas de sus funcionalidades no están disponibles a través de la manipulación directa con el ratón y el teclado. Una característica manejable y fácil de usar proporcionada por JEditorPane es que puede ser cargado con texto formateado desde una URL

JEditorPane y su subclase JTextPane

Tabla 7: Grupos de Componentes de Texto.

Veamos ahora algunas reglas de uso de los Componentes de Texto. JTextComponent es la base para los componentes de texto Swing y proporciona estas características, personalizables para todos sus descendientes:

- Un modelo separado, conocido como Document, para manejar el contenido del

componente. - Una vista separada, que se encarga de mostrar el componente en la pantalla. - Un controlador separado, conocido como un editor kit, que puede leer y escribir

texto e implementa capacidades de edición con comandos action. - Mapas de teclas personalizados. - Soporte para repetir/deshacer infinito. - Cursores conectables y oyentes de cambios de cursor.

Como hemos dicho, y al igual que muchos otros componentes Swing, un

componente de texto separa su contenido de su vista. El contenido de un componente es manejado por su documento, el cual contiene el texto, soporte para edición, y notifica a los oyentes los cambios en el texto. Un documento es un ejemplar de una clase que implementa el interfaz Document o su subinterfaz StyledDocument. Podemos personalizar un documento, dándole las características que necesitemos (por ejemplo, hacer que limite el número de caracteres que puede contener). También hemos dicho que un documento notifica sus cambios a los oyentes interesados. Se utiliza un oyente de Document (DocumentListener) para reaccionar cuando se inserta o se elimina texto de un documento, o cuando cambia el estilo de alguna parte del texto.

Todos los componentes de Texto Swing soportan comandos de edición estándar como cortar, copiar, pegar y la inserción de caracteres. Cada comando de edición está representada e implementado por un objeto Action. Esto hace sencillo el asociar un comando con un componente GUI, como un ítem de menú o un botón, y construir un GUI alrededor de un componente de texto. Un componente de texto usa un objeto EditorKit para crear y manejar estas acciones. Además de manejar un conjunto de

Page 29: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 105 -

acciones para un componente de texto, un kit de editor también sabe leer y escribir documentos en un formato particular. El paquete text de Swing proporciona estos tres kits de editor:

- DefaultEditorKit : Lee y escribe texto sin estilo. Proporciona un conjunto básico de comandos de edición. Los demás kits de editor descienden de este.

- StyledEditorKit: Lee y escribe texto con estilo y proporciona un conjunto de acciones mínimo para texto con estulo. Esta clase es una subclase de DefaultEditorKit y es el kit de editor usado por defecto por JTextPane.

- HTMLEditorKit: Lee, escribe y edita HTML. Esta es una subclase de StyledEditorKit.

6.3-. SWT.

6.3.1-. Introducción.

No sólo existen AWT y Swing para la creación de GUIs. Otro representante importante es SWT. Describiremos a continuación qué es, por qué surge y sobre todo por qué no se ha utilizado para el desarrollo de este proyecto.

El Standard Widget Toolkit (SWT) ha sido creado por IBM como reemplazo de AWT y Swing. De manera más precisa, el SWT es un conjunto de widgets (widgets son controles o componentes) para desarrolladores Java que ofrece una API portable y una integración muy ligada con la interfaz gráfica de usuario nativa al sistema operativo de cada plataforma. Así, cada plataforma debe adaptar el SWT para sus gráficos nativos. Esto parece que entra en contradicción con la filosofía de Java de ser independiente de la plataforma, pero ofrece ventajas en la unicidad del aspecto del GUI diseñado sea cual sea la plataforma sobre la que se ejecute. Hasta el momento los entornos a los que se ha portado SWT son Windows, Linux GTK, Linux Motif, Solaris Motif, AIX Motif, HPUX Motif, Photon QNX, y Mac OS X.

6.3.2-. Evolución de los gráficos en Java.

Los gráficos en Java han tenido un largo y exitoso desarrollo. Se empezó como ya sabemos con el paquete básico AWT, viéndose ampliado por el paquete Swing. Actualmente está empezando a hablarse de SWT, y parece que pueda llegar a imponerse. Ahondemos un poco en esta evolución:

- AWT: Primer acercamiento de Java al desarrollo de interfaces gráficas (GUI). Permite mostrar ventanas con controles variados como cajas de texto y botones. Las GUIs con AWT son fáciles de desarrollar, y usan los controles propios del sistema operativo en el que se programa, por ejemplo, en windows aparecerá una ventana de texto típica de windows, en Mac, una ventana con sus respectivas características Mac, etc. El problema que se presenta es que algunos sistema operativos difieren de otros en el conjunto de controles, por lo que Sun sólo implementó los controles comunes a los sistemas operativos a los que se dirige

Page 30: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 106 -

Java, que por lo general, es un conjunto reducido y simple respecto de los realmente disponibles.

- SWING: Introducido posteriormente, motivado por la creciente demanda de los desarrolladores de tener una implementación no nativa, esto es, independiente de la plataforma, de controles de más alto nivel como árboles, tablas y texto. Con esto se gana en funcionalidad, pero con el inconveniente de hacer las aplicaciones Swing demasiado específicas de Java. Sun añadió una emulación look and feel para aproximar el aspecto de las aplicaciones Swing al sistema operativo sobre las que se ejecuta, pero no abarca las nuevas versiones de los sistemas operativos (Windows Me, 2000 en adelante, por ejemplo). Además, al estar implementado sobre Java y no de forma nativa en el sistema operativo, los tiempos de respuesta de las interfaces Swing son sensiblemente más lentas que las nativas.

- SWT (Standard Widget Toolkit): Ofrece un conjunto de elementos que hacen uso directo de los controles nativos a través de la Interfaz Nativa de Java (JNI). Si los controles no están ofrecidos por el sistema operativo, SWT crea los suyos propios según las necesidades. Esto significa que se necesita código nativo para poder funcionar en cada sistema operativo, pero IBM ha sido capaz de adaptar SWT a un buen número de sistemas. Es muy destacable la importancia de SWT porque es el entorno gráfico de desarrollo que viene con Eclipse y debe ser utilizado en los plugins desarrollados para el mismo. Eclipse, como ya sabemos, es la herramienta que se ha utilizado para el desarrollo de este proyecto.

6.3.3-. Fundamentos de SWT.

SWT se compone esencialmente de tres elementos:

1. En el nivel inferior se encuentra una librería nativa que interacciona directamente con el sistema operativo. Es la denominada JNI, que hemos nombrado anteriormente. Al ser la parte más dependiente de la plataforma, debe ser portada en función de la misma.

2. Por encima de la anterior capa está la clase Display, la interfaz por la que el SWT se comunica con la interfaz gráfica.

3. El nivel superior lo forma la clase Shell, representa el tipo de ventana de más alto nivel y que es donde se alojan los widgets, controles o componentes. El shell es la parte de la interfaz que está directamente controlada por el sistema de ventanas del sistema operativo. La clase Shell es hija de la clase Display, en cuyo caso es la ventana o marco principal a partir de la cual se construye el resto de la interfaz, o bien hija de otro shell, siendo el ejemplo más común las ventanas de diálogo.

No vamos a entrar en detalles de código de SWT. Sin embargo, para tener una ligera idea, finalizaremos con un ejemplo: el típico “Hello World”.

import org.eclipse.swt.widgets.*; public class HelloWorld {

Page 31: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 107 -

public static void main(String[] args) { Display display = new Display(); Shell shell = new Shell(display); shell.setText("Hola Mundo"); shell.setSize(200, 100); shell.open(); while (!shell.isDisposed() { if (!display.readAndDispatch()) display.sleep(); } display.dispose(); } } Comentemos el código. Se identifican las partes de las que hablamos

anteriormente.

Display display = new Display();

Display representa la pantalla y su conexión con el sistema gestor de ventanas del sistema operativo en el que nos encontramos. El objeto display contendrá a su vez una lista de objetos shell. El shell es una ventana abierta sobre la pantalla. Es la clase raíz de los componentes y controles. Añadimos un shell a un display con el código:

Shell shell = new Shell(display); shell.open(); El siguiente fragmento de código es el denominado “bucle de eventos”. Ha de ser

programado explícitamente y su función es detectar y ejecutar eventos. Es la forma en que liberamos la CPU para otros menesteres ajenos a la interfaz gráfica cuando no se han producido eventos a los que dedicar ciclos de proceso para atender:

while (!shell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); }

Por último observamos que los recursos del sistema han de ser liberados por el

programador (display.dispose();). La liberación del objeto display conlleva la finalización de todos los shells hijos contenidos.

Esto no es más que una pequeña introducción. Para ahondar en el tema se puede ir a la web del proyecto Eclipse, www.eclipse.org/swt.

Page 32: Capítulo Capítulo 6: Interfac6: Interfac6: Interfaces de ...bibing.us.es/proyectos/abreproy/11387/fichero/VOLUMEN+1%2FCapitulo6... · - javax.swing.text: Contiene las clases de

6-. Interfaces de Usuario.

- 108 -

6.3.4-. Ventajas y desventajas de SWT. Por último, es importante comentar las ventajas y desventajas de este paquete, y

explicar por qué se ha elegido Swing para la realización del proyecto. Hoy en día hay una lucha importante entre defensores y detractores de Swing y SWT. Podríamos destacar algunos aspectos:

- No elija SWT por su fidelidad a la plataforma, ya que esto no es una gran virtud.

Normalmente los usuarios no tiene problema en utilizar interfaces alejados de su S.O. nativo, siempre que estos sean buenos y fáciles de usar. Véase el ejemplo de Firefox, Winamp, etc.

- Se utiliza un planteamiento de “mínimo común denominador” de las plataformas soportadas. Es decir, puede haber una característica muy típica de Windows pero que no esté disponible sólo porque ésta no existe en Motif.

- Si las necesidades del proyecto se alejan de las de las necesidades del proyecto Eclipse la dificultad aumenta ya que SWT fue creado como soporte para dicho proyecto y está orientado hacia él, dejando de lado características que Eclipse no utiliza.

- SWT tiene muchos fallos. Es una plataforma relativamente joven y se nota. - Para el que ya conoce Swing, elegir SWT por pensar que Swing es “feo” es un

gran error. Se pueden hacer aplicaciones realmente atractivas en Swing.

Aparte de los puntos anteriores, a la hora de decidir entre Swing y SWT para realizar el proyecto se tuvo en cuenta la escasa cantidad de documentación que se puede encontrar de este último, dado que es una tecnología joven y emergente. Por estas razones, la elección fue Swing.

6.4-. Conclusiones. Llegados a este punto, realicemos una breve recapitulación. Hemos descrito en este capítulo las herramientas más importantes para la realización de interfaces gráficas de usuario, prestando especial atención al paquete Java Swing. Hemos visto la gran cantidad de elementos que éste nos permite implementar y sus ventajas frente al AWT. También hemos hablado de la tecnología SWT. Las explicaciones presentadas en este capítulo servirán para comprender mejor la herramienta implementada. Pero antes de pasar a la descripción de dicha herramienta es necesario hablar de los Analizadores XML, tema que abordaremos en el siguiente capítulo.