Capítulo 6: Interfaces es de Usuario.

Documentos relacionados
Swing. Andrés Marín López Jesús Arias Fisteus Laboratorio de Aplicaciones Telemáticas

Federico Peinado

Interfaces de Usuario en Java

PROGRAMACIÓN II INTERFACES GRÁFICAS DE USUARIO. Otoño de 2017 BUAP-FCC DR. MARIO ROSSAINZ LÓPEZ

Introducción a las Interfaces Gráficas de Usuario en Java

Introducción a la Programación Orientada a Objetos

Universidad Carlos III de Madrid Departamento de Ingeniería Telemática. Swing

Tópicos Avanzados de Programación (TAP3501)

Hasta ahora hemos desarrollado programas que usan la consola para interactuar con el usuario.

INF 473 Desarrollo de Aplicaciones en Java

Pasos requeridos para establecer el manejo de eventos para un componente de GUI.

Introducción a Swing. Taller de Programación 2016

Uso de Java Swing. Noviembre de 2013

public void mousepressed(mouseevent evento) Se llama cuando se oprime un botón del ratón, mientras el cursor del ratón está sobre un componente.

1 Funcionalidades Java que se presentan en esta práctica

Programación orientada a objetos. Capítulo 11 Construir interfaces gráficas de usuarios

Interfases gráficas. Programación Orientada a Objetos.

Java Avanzado Facultad de Ingeniería. Escuela de computación.

Construir una Interfaz Gráfica

EVENTOS en Java generador de un evento gestor de eventos

Programación de sistemas

IC Programación Orientada a Objetos I. Programación de Interfaces Gráficas de Usuario (GUI) 2

Desarrollo de Aplicaciones en Java INF 473

Java GUI La librería Swing

Metodología de la Programación

Programación Conducida por eventos Event-driven programming. Agustín J. González ELO329/ELO330

Programación de sistemas

Programación Orientada a Objetos

Ejemplo de GUI con Swing

Java es un lenguaje orientado a objetos, por lo que los objetos (las clases) son los elementos más importantes en el diseño y desarrollo de una

Programación de Interfaces Gráficas en Java. Agustín J. González ELO329

Programación de Interfaces Gráficas en Java

Lenguajes de Programación Curso Práctica 8. Interfaces gráficas de usuario y Swing. Trabajo con modelos. 1. Un ejemplo 2. 2.

Interfaces Gráficas de Usuario

Programación Java Curso C GUI

Componentes Swing. Las clases cuyo nombre comienza por J forman parte de Swing. Todas las demás están incluidas en AWT (Abstract Window Toolkit)

Capítulo 7. Introducción a las Interfaces Gráficas de usuario. Continuar

I. Introducción a la programación orientada a objetos y al lenguaje JAVA Colegio Reuven Feuerstein --Javier Navarro

Programación basada en eventos Event- Based Programming : Conceptos. ELO329: Diseño y Programación Orientados a Objetos

Programación de Eventos

Interfaces gráficas de usuario

Agenda. Contenedores y componentes

Desarrollo de aplicaciones gráficas

Programación Conducida por eventos Event driven programming. Agustín J. González ELO329/ELO330

Definición de una base de datos. Un manejador de base de datos es un programa que permite administrar y organizar una serie de datos.

PRESENTACIÓN DE LA ASIGNATURA E INTRODUCCIÓN A LA PROGRAMACIÓN VISUAL

Indice. Swing: conceptos. Generalidades. Swing & AWT: diferencias. Interfaz Gráfica de Usuario (GUI)

Examen de Programación II (Ingeniería Informática)

Tópicos Selectos de Programación unidad 4. Librería de Interfaz Gráfica

E1. Práctica UF2406: El ciclo de vida del desarrollo de aplicaciones

Intefaces gráficas. Eventos. Jose Jesus García Rueda

Problema 1 (2 puntos)

Lenguajes de Programación Curso Práctica 7.

GUIs en Java (4) Iván Alonso

Capítulo 5: Interfaces gráficas de usuario

Diseño y Programación Orientados a Objetos 29 de Abril de Primer Certamen

INTRODUCCIÓN THINK-CELL. Manual de Referencia para usuarios. Salomón Ccance CCANCE WEBSITE

Interfaces gráficas en Java GUIs Graphical User Interface

Aplicaciones de Escritorio

Java Avanzado Facultad de Ingeniería. Escuela de computación.

Universidad Autónoma de Baja California Facultad de Ingeniería Apuntes de Programación Orientada a Objetos I

INGRESAR DATOS CON UN CONTROL VISUAL EN JAVA. CLASE JTEXTFIELD Y MÉTODO GETTEXT. EJEMPLOS (CU00928C)

Interacción con el Usuario Gestión de Eventos

Programación basada en/dirigida por eventos Event-Based Programming : Conceptos (o Event-driven Programming)

Laboratorio de Redes de Comunicaciones Recursos de Sistema en Java

7.- ANEXOS. Anexo 1.-Diagramas uml LISTADO DE CLASES EN UML. Clase P1. Clase FrameAWT

ACCESIBILIDAD EN JAVA

Ya sabemos que en Excel podemos incluir dibujos e imágenes

USO DE LOS OBJETOS JLABEL, JTEXTFIELD Y JBUTTON

JAVA 6. Gestión de Eventos

Especificación de Requerimientos <Nombre del Proyecto> Nombre del Grupo de Desarrollo o Asignatura Nombre del Autor

Interfaz Gráfica de Usuario (GUI)

Ingeniería del Software Separación entre Presentación y Lógica del Negocio

Programación basada en/dirigida por eventos Event-Based Programming : Conceptos (o Event-driven Programming)

Programación Orientada a Objetos. Componentes Gráficos

CAPITULO 11. CONSTRUIR INTERFACES GRAFICAS DE USUARIO

Tema 6. Interfaces gráficas de usuario (Parte II Componentes y eventos)

Centro Asociado Palma de Mallorca. Antonio Rivero Cuesta

Interfaces gráficas. Jose Jesus García Rueda

4.1 Conceptos Básicos de Matlab. Matlab es creado por The MathWorks, el cual es un idioma de alto rendimiento

1. Cómo hacer un applet? Implementación de un Applet (awt) Ejecución de los Applets Hacer el archivo HTML con los siguientes

UNIDAD I.- Elementos de Interfaces Gráficas

Tema 6. Interfaces gráficas de usuario (Parte 1)

PROGRAMACIÓN VISUAL E.A.P. SISTEMAS E INFORMATICA UNIVERSIDAD NACIONAL DEL SANTA CURSO: MICROCOMPUTACION III FACULTA DE INGENIERIA.

CAPÍTULO 4 MANUAL DE USUARIO. 4.1 Introducción. 4.2 Interfaz de Usuario

JAVA 7 Los fundamentos del lenguaje Java

Crear gráficos 1. INTRODUCCIÓN

Objetivos y Temario CURSO JAVA 7

INTERFACES GRÁFICAS MTRA. CAROLINA GALAVIZ INZUNZA. Mtra. Carolina Galaviz Inzunza

Tipos de programas en Java. Programación basada en eventos. Programación basada en eventos. Creación de una interfaz gráfico de usuario

INTERFAZ GRÁFICO DE USUARIO EVENTOS

PROGRAMA JAVA SE (Standard Edition) MODALIDAD ONLINE

NETBEANS JAVA PROGRAMACIÓN ORIENTADA A OBJETOS

3URJUDPDFLyQ-DYD. 3UiFWLFDPDUWHV. -DYLHU*DUFtDGH-DOyQÂ -RVp,JQDFLR5RGUtJXH] $OIRQVR%UD]iOH]Â $OEHUWR/DU]DEDOÂ -HV~V&DOOHMDÂ -RQ*DUFtD

1

Curso: Java SE (Standard Edition)

Transcripción:

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

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 - 79 -

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: Component Container Menu Container Interface Window Panel Frame 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. - 80 -

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. - 81 -

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 - 82 -

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. - 83 -

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: - 84 -

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 - 85 -

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); - 86 -

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(); - 87 -

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. - 88 -

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 - 89 -

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. - 90 -

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 - 91 -

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, - 92 -

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: - 93 -

Acción que resulta en el evento El usuario pulsa un botón, presiona Return mientras teclea en un campo de texto, o elige un ítem de un menú. El usuario selecciona una ventana principal. El usuario pulsa un botón del ratón mientras el cursor está sobre un componente. El usuario mueve el cursor sobre un componente. El componente se hace visible. El componente obtiene el foco del teclado. Cambia la tabla o la selección de una lista. Tabla 6: Eventos y Oyentes Asociados en Swing. Tipo de oyente ActionListener WindowListener MouseListener MouseMotionListener ComponentListener FocusListener ListSelectionListener 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 { - 94 -

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 - 95 -

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); - 96 -

... 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. - 97 -

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. - 98 -

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) ============= =...()...= =...= =...= =...= ============= Ilustración 6: Orden en el dibujo de una aplicación Swing. 4. hijos (si existen) ============= =...()...= =.---------.= =. JButton.= =.---------.= ============= 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: - 99 -