76
TEMARIO. AWT 1. Introducción al AWT 2. Interface de Usuario 3. Estructura del AWT 4. Componentes y Contenedores o Tipos de Componentes 5. Componentes o Botones 1. Eventos Button 2. De pulsación (Push) 3. De lista (Choice) 4. De marcación (CheckBox) 5. De selección (Radio) 6. Autocontenidos o Etiquetas o Listas o Campos de Texto o Areas de Texto o Canvas o Barras de Desplazamiento o Diseño de Componentes propios 6. Contenedores o Window o Frame o Dialog o Panel o Crear un Contenedor o Añadir Componentes a un Contendor 7. Creación de Aplicaciones con AWT o Crear el Marco de la Aplicación o Inicializar Fuentes, Colores y Recursos o Crear Menús y Barras de Menús o Diálogos y Ventanas 8. Paneles 9. Layouts o FlowLayout o BorderLayout o GridLayout o GridBagLayout o CardLayout o Crear un Layout propio 10. Control de Eventos o La clase Event

tarea de componentes

Embed Size (px)

Citation preview

Page 1: tarea de componentes

TEMARIO.

AWT

1. Introducción al AWT 2. Interface de Usuario 3. Estructura del AWT 4. Componentes y Contenedores

o Tipos de Componentes 5. Componentes

o Botones 1. Eventos Button 2. De pulsación (Push) 3. De lista (Choice) 4. De marcación (CheckBox) 5. De selección (Radio) 6. Autocontenidos

o Etiquetas o Listas o Campos de Texto o Areas de Texto o Canvas o Barras de Desplazamiento o Diseño de Componentes propios

6. Contenedores o Window o Frame o Dialog o Panel o Crear un Contenedor o Añadir Componentes a un Contendor

7. Creación de Aplicaciones con AWT o Crear el Marco de la Aplicación o Inicializar Fuentes, Colores y Recursos o Crear Menús y Barras de Menús o Diálogos y Ventanas

8. Paneles 9. Layouts

o FlowLayout o BorderLayout o GridLayout o GridBagLayout o CardLayout o Crear un Layout propio

10. Control de Eventos o La clase Event o Tipos de Eventos

1. Eventos de Ventana 2. Eventos de Teclado 3. Eventos de Ratón 4. Eventos de Barras

Page 3: tarea de componentes

1. INTRODUCCIÓN AL AWT

AWT es el acrónimo del X Window Toolkit para Java, donde X puede ser cualquier cosa: Abstract, Alternative, Awkward, Another o Asqueroso; aunque parece que Sun se decanta por Abstracto, seriedad por encima de todo. Se trata de una biblioteca de clases Java para el desarrollo de Interfaces de Usuario Gráficas. La versión del AWT que Sun proporciona con el JDK se desarrolló en sólo dos meses y es la parte más débil de todo lo que representa Java como lenguaje. El entorno que ofrece es demasiado simple, no se han tenido en cuenta las ideas de entornos gráficos novedosos, sino que se ha ahondado en estructuras orientadas a eventos, llenas de callbacks y sin soporte alguno del entorno para la construcción gráfica; veremos que la simple acción de colocar un dibujo sobre un botón se vuelve una tarea harto complicada.

JavaSoft, asegura que esto sólo era el principio y que AWT será multi-idioma, tendrá herramientas visuales, etc.

La estructura básica del AWT se basa en Componentes y Contenedores . Estos últimos contienen Componentes posicionados a su respecto y son Componentes a su vez, de forma que los eventos pueden tratarse tanto en Contenedores como en Componentes, corriendo por cuenta del programador (todavía no hay herramientas de composición visual) el encaje de todas las piezas, así como la seguridad de tratamiento de los eventos adecuados.

No obstante y pese a ello, vamos a abordar en este momento la programación con el AWT para tener la base suficiente y poder seguir profundizando en las demás características del lenguaje Java, porque como vamos a ir presentando ejemplos gráficos es imprescindible el conocimiento del AWT.

1.1 Interface de usuario

La interface de usuario es la parte del programa que permite a éste interactuar con el usuario. Las interfaces de usuario pueden adoptar muchas formas, que van desde la simple línea de comandos hasta las interfaces gráficas que proporcionan las aplicaciones más modernas.

Page 4: tarea de componentes

La interface de usuario es el aspecto más importante de cualquier aplicación. Una aplicación sin un interfaz fácil, impide que los usuarios saquen el máximo rendimiento del programa. Java proporciona los elementos básicos para construir decentes interfaces de usuario a través del AWT.

Al nivel más bajo, el sistema operativo transmite información desde el ratón y el teclado como dispositivos de entrada al programa. El AWT fue diseñado pensando en que el programador no tuviese que preocuparse de detalles como controlar el movimiento del ratón o leer el teclado, ni tampoco atender a detalles como la escritura en pantalla. El AWT constituye una librería de clases orientada a objeto para cubrir estos recursos y servicios de bajo nivel.

Debido a que el lenguaje de programación Java es independiente de la plataforma en que se ejecuten sus aplicaciones, el AWT también es independiente de la plataforma en que se ejecute. Los elementos de interface proporcionados por el AWT están implementados utilizando toolkits nativos de las plataformas, preservando una apariencia semejante a todas las aplicaciones que se creen para esa plataforma. Este es un punto fuerte del AWT, pero también tiene la desventaja de que una interface gráfica diseñada para una plataforma, puede no visualizarse correctamente en otra diferente.

1.2 Estructura del awt

La estructura de la versión actual del AWT podemos resumirla en los puntos que exponemos a continuación:

Los Contenedores contienen Componentes, que son los controles básicos No se usan posiciones fijas de los Componentes, sino que están situados a

través de una disposición controlada (layouts) El común denominador de más bajo nivel se acerca al teclado, ratón y manejo

de eventos Alto nivel de abstracción respecto al entorno de ventanas en que se ejecute la

aplicación (no hay áreas cliente, ni llamadas a X, ni hWnds, etc.) La arquitectura de la aplicación es dependiente del entorno de ventanas, en

vez de tener un tamaño fijo Es bastante dependiente de la máquina en que se ejecuta la aplicación (no

puede asumir que un diálogo tendrá el mismo tamaño en cada máquina) Carece de un formato de recursos. No se puede separar el código de lo que es

propiamente interface. No hay ningún diseñador de interfaces (todavía)

1.3 Componentes y contenedores

Una interface gráfica está construida en base a elementos gráficos básicos, los Componentes. Típicos ejemplos de estos Componentes son los botones, barras de desplazamiento, etiquetas, listas, cajas de selección o campos de texto. Los Componentes permiten al usuario interactuar con la aplicación y proporcionar información desde el programa al usuario sobre el estado del programa. En el AWT, todos los Componentes de la interface de usuario son instancias de la clase Component o uno de sus subtipos.

Page 5: tarea de componentes

Los Componentes no se encuentran aislados, sino agrupados dentro de Contenedores. Los Contenedores contienen y organizan la situación de los Componentes; además, los Contenedores son en sí mismos Componentes y como tales pueden ser situados dentro de otros Contenedores. También contienen el código necesario para el control de eventos, cambiar la forma del cursor o modificar el icono de la aplicación. En el AWT, todos los Contenedores son instancias de la clase Container o uno de sus subtipos.

En la imagen siguiente presentamos una interface de usuario muy simple, con la apariencia que presenta cuando se visualiza bajo Windows '95.

Los Componentes deben circunscribirse dentro del Contenedor que los contiene. Esto hace que el anidamiento de Componentes (incluyendo Contenedores) en Contenedores crean árboles de elementos, comenzando con un Contenedor en la raiz del árbol y expandiéndolo en sus ramas. A continuación presentamos el árbol que representa la interface que corresponde con la aplicación gráfica generada anteriormente.

1.4 TIPOS DE COMPONENTES

En el árbol siguiente mostramos la relación que existe entre todas las clases que proporciona AWT para la creación de interfaces de usuario, presentando la jerarquía de Clases e Interfaces:

Clases:

BorderLayout CardLayout CheckboxGroup Color Component

o Button o Canvas o Checkbox o Choice o Container

Panel Window

Page 6: tarea de componentes

Dialog Frame

o Label o List o Scrollbar o TextComponent

TextArea TextField

o Dimension o Event o FileDialog o FlowLayout o Font o FontMetrics o Graphics o GridLayout o GridBagConstraints o GridBagLayout o Image o Insets o MediaTracker o MenuComponent

MenuBar MenuItem

CheckboxMenuItem Menu

o Point o Polygon o Rectangle o Toolkit

Interfaces:

LayoutManager MenuContainer

En la figura siguiente reproducimos la ventana generada por el código de la aplicación ComponentesAWT.java que muestra todos los Componentes que proporciona el AWT. Vamos a ver en detalle estos Componentes, pero aquí podemos observar ya la estética que presentan en su conjunto. La ventana es necesaria porque el programa incluye un menú, y los menús solamente pueden utilizarse en ventanas. El código contiene un método main() para poder ejecutarlo como una aplicación independiente.

Page 7: tarea de componentes

2. COMPONENTES

Component es una clase abstracta que representa todo lo que tiene una posición, un tamaño, puede ser pintado en pantalla y puede recibir eventos.

Los Objetos derivados de la clase Component que se incluyen en el Abstract Window Toolkit son los que aparecen a continuación:

Button Canvas Checkbox Choice Container

o Panel o Window

Dialog Frame

Label List Scrollbar TextComponent

o TextArea o TextField

Vamos a ver un poco más en detalle los Componentes que nos proporciona el AWT para incorporar a la creación de la interface con el usuario.

2.1 Botones

Veremos ejemplos de cómo se añaden botones a un panel para la interacción del usuario con la aplicación, pero antes vamos a ver la creación de botones como objetos.

Se pueden crear objetos Button con el operador new:

Button boton;

boton = new Button( "Botón");

Page 8: tarea de componentes

La cadena utilizada en la creación del botón aparecerá en el botón cuando se visualice en pantalla. Esta cadena también se devolverá para utilizarla como identificación del botón cuando ocurra un evento.

Eventos Button

Cada vez que el usuario pulsa un botón, se produce un evento, de la misma forma que se produce un evento cuando se aprieta el botón del ratón. Los eventos de pulsación de un botón se pueden capturar sobrecargando el método action():

public boolean action( Event evt,Object obj ) { if( evt.target instanceof Button ) System.out.println( (String)obj ); else System.out.println( "Evento No-Button" ); }

La distinción entre todos los botones existentes se puede hacer utilizando el objeto destino pasado por el objeto Event y comparándolo con los objetos botón que hemos dispuesto en nuestro interface:

import java.awt.*;import java.applet.Applet;

public class Botones extends Applet { Button b1,b2,b3;

public void init() { b1 = new Button( "Botón B1" ); b2 = new Button( "Botón B2" ); b3 = new Button( "Botón B3" );

this.add( b1 ); this.add( b2 ); this.add( b3 ); }

public boolean action( Event evt,Object obj ) { if( evt.target.equals( b1 ) ) System.out.println( "Se ha pulsado el boton B1" ); if( evt.target.equals( b2 ) ) System.out.println( "Se ha pulsado el boton B2" ); if( evt.target.equals( b3 ) ) System.out.println( "Se ha pulsado el boton B3" );

return true; } }

En el applet anterior, Botones.java, observamos que se imprime el texto asociado al botón que hayamos pulsado.

Page 9: tarea de componentes

Botones de Pulsación

Los botones presentados en el applet son los botones de pulsación estándar; no obstante, para variar la representación en pantalla y para conseguir una interfaz más limpia, AWT ofrece a los programadores otros tipos de botones.

Botones de Lista

Los botones de selección en una lista (Choice) permiten el rápido acceso a una lista de elementos. Por ejemplo, podríamos implementar una selección de colores y mantenerla en un botón Choice:

import java.awt.*;import java.applet.Applet;

public class BotonSeleccion extends Applet { Choice Selector;

public void init() { Selector = new Choice();

Selector.addItem( "Rojo" ); Selector.addItem( "Verde" ); Selector.addItem( "Azul" );

add( Selector ); }

public boolean action( Event evt,Object obj ) { if( evt.target instanceof Choice ) { String color = (String)obj;

System.out.println( "El color elegido es el " + color ); }

return true; } }

En este ejemplo, BotonSeleccion.java, la cadena proporcionada al método addItem() será devuelta en el argumento Object de un evento Choice, por ello en el manejador del botón de selección, comprobamos en primer lugar que se trate efectivamente de un evento generado en un botón de tipo Choice.

Botones de Marcación

Los botones de comprobación (Checkbox) se utilizan frecuentemente como botones de estado. Proporcionan información del tipo Sí o No (true o false). El estado del botón

Page 10: tarea de componentes

se devuelve en el argumento Object de los eventos Checkbox; el argumento es de tipo booleano: verdadero (true) si la caja se ha seleccionado y falso (false) en otro caso.

Tanto el nombre como el estado se devuelven en el argumento del evento, aunque se pueden obtener a través de los métodos getLabel() y getState() del objeto Checkbox.

import java.awt.*;import java.applet.Applet;

public class BotonComprobacion extends Applet { Checkbox Relleno;

public void init() { Relleno = new Checkbox( "Relleno");

add( Relleno ); }

public boolean action( Event evt,Object obj ) { if( evt.target instanceof Checkbox ) System.out.println( "CheckBox: " + evt.arg.toString() );

return true; } }

El sencillo ejemplo anterior, BotonComprobacion.java, muestra los cambios que se producen en el estado del botón cuando la caja está o no seleccionada.

Botones de Selección

Los botones de comprobación se pueden agrupar para formar una interfaz de botón de radio (CheckboxGroup), que son agrupaciones de botones Checkbox en las que siempre hay un único botón activo.

import java.awt.*;import java.applet.Applet;

public class BotonRadio extends Applet { CheckboxGroup Radio;

public void init() { Radio = new CheckboxGroup();

add( new Checkbox( "Primero",Radio,true) );

Page 11: tarea de componentes

add( new Checkbox( "Segundo",Radio,false) ); add( new Checkbox( "Tercero",Radio,false) ); } }

En el ejemplo anterior, BotonRadio.java, observamos que siempre hay un botón activo entre los que conforman el interfaz de comprobación que se ha implementado.

Botones Autocontenidos

La naturaleza orientada a objetos de Java nos da la posibilidad de crear botones completamente autocontenidos. En este tipo de botón, se construye el manejador de eventos dentro de la propia clase extendida de botón. Se pueden añadir estos botones a la aplicación, sin tener que preocuparse de los eventos que pudieran generar.

En el ejemplo siguiente, BotonAuto.java, creamos el botón que muestra la figura, un botón que genera el texto "Boton Aceptar" por la salida estándar:

import java.awt.*;import java.applet.Applet;

class BotonAceptar extends Button {

public BotonAceptar() { setLabel( "Aceptar" ); }

public boolean action( Event evt,Object obj ) { System.out.println( "Boton Aceptar" ); return true; } }

public class BotonAuto extends Applet { BotonAceptar boton;

public void init() { boton = new BotonAceptar(); add( boton ); } }

Page 12: tarea de componentes

Es de hacer notar que no hay un método action() en la clase applet BotonAuto, la clase BotonAceptar manejará sus eventos. Si se hubiesen colocado otros objetos en el applet, se podría haber usado un método action() para tratar los eventos de esos objetos.

2.2 Etiquetas

Las etiquetas (Label) proporcionan una forma de colocar texto estático en un panel, para mostrar información que no varía, normalmente, al usuario.

El applet Etiqueta.java presenta dos textos en pantalla, tal como aparece en la figura siguiente:

import java.awt.*;import java.applet.Applet;

public class Etiqueta extends Applet {

public void init() { Label etiq1 = new Label( "Hola Java!" ); Label etiq2 = new Label( "Otra Etiqueta" );

setLayout( new FlowLayout( FlowLayout.CENTER,10,10) ); add( etiq1 ); add( etiq2 ); } }

2.3 LISTAS

Las listas (List) aparecen en los interfaces de usuario para facilitar a los operadores la manipulación de muchos elementos. Se crean utilizando métodos similares a los de los botones Choice. La lista es visible todo el tiempo, utilizándose una barra de desplazamiento para visualizar los elementos que no caben en el área que aparece en la pantalla.

El ejemplo siguiente, Lista.java, crea una lista que muestra cuatro líneas a la vez y no permite selección múltiple.

Page 13: tarea de componentes

import java.awt.*;import java.applet.Applet;

public class Lista extends Applet {

public void init() { List l = new List( 4,false );

l.addItem( "Mercurio" ); l.addItem( "Venus" ); l.addItem( "Tierra" ); l.addItem( "Marte" ); l.addItem( "Jupiter" ); l.addItem( "Saturno" ); l.addItem( "Neptuno" ); l.addItem( "Urano" ); l.addItem( "Pluton" ); add( l ); }

public boolean action( Event evt,Object obj ) { if( evt.target instanceof List ) System.out.println( "Entrada de la Lista: " + obj );

return true; } }

Para acceder a los elementos seleccionados se utilizan los métodos getSelectedItem() o getSelectedItems(). Para listas de selección simple, cualquier selección con doble-click en la lista disparará el método action() de la misma forma que con los eventos de selección en menús.

En el applet siguiente, ListaMult.java, se permite al usuario seleccionar varios elementos de los que constituyen la lista. En la figura se muestra la apariencia de una selección múltiple en este applet.

Page 14: tarea de componentes

import java.awt.*;import java.applet.Applet;

public class ListaMult extends Applet { List lm = new List( 6,true );

public void init() { Button boton = new Button( "Aceptar" );

lm.addItem( "Mercurio" ); lm.addItem( "Venus" ); lm.addItem( "Tierra" ); lm.addItem( "Marte" ); lm.addItem( "Jupiter" ); lm.addItem( "Saturno" ); lm.addItem( "Neptuno" ); lm.addItem( "Urano" ); lm.addItem( "Pluton" ); add( lm ); add( boton ); }

public boolean action( Event evt,Object obj ) { if( evt.target instanceof Button ) { if( "Aceptar".equals( obj ) ) { String seleccion[];

seleccion = lm.getSelectedItems(); for( int i=0; i < seleccion.length; i++ ) System.out.println( seleccion[i] ); } }

return true; } }

En este caso de la selección múltiple en una lista, utilizamos un evento externo para disparar las acciones asociadas a la lista. En el ejemplo, hemos incluido un botón para generar el evento que hace que el applet recoja los elementos que hay seleccionados en la lista.

Page 15: tarea de componentes

2.4 Campos de texto

Para la entrada directa de datos se suelen utilizar los campos de texto, que aparecen en pantalla como pequeñas cajas que permiten al usuario la entrada por teclado.

Los campos de texto (TextField) se pueden crear vacíos, vacíos con una longitud determinada, rellenos con texto predefinido y rellenos con texto predefinido y una longitud determinada. El applet siguiente, CampoTexto.java, genera cuatro campos de texto con cada una de las características anteriores. La imagen muestra los distintos tipos de campos.

import java.awt.*;import java.applet.Applet;

public class CampoTexto extends Applet { TextField tf1,tf2,tf3,tf4;

public void init() { // Campo de texto vacío tf1 = new TextField(); // Campo de texto vacío con 20 columnas tf2 = new TextField( 20 ); // Texto predefinido tf3 = new TextField( "Hola" ); // Texto predefinido en 30 columnas tf4 = new TextField( "Hola",30 ); add( tf1 ); add( tf2 ); add( tf3 ); add( tf4 ); }

public boolean action( Event evt,Object obj ) { if( evt.target instanceof TextField ) { if( evt.target.equals( tf1 ) ) System.out.println( "Campo de Texto 1: " + evt.arg.toString() ); if( evt.target.equals( tf1 ) ) System.out.println( "Campo de Texto 2: " + evt.arg.toString() ); if( evt.target.equals( tf1 ) ) System.out.println( "Campo de Texto 3: " +

Page 16: tarea de componentes

evt.arg.toString() ); if( evt.target.equals( tf1 ) ) System.out.println( "Campo de Texto 4: " + evt.arg.toString() ); } return true; } }

Cuando el usuario teclea un retorno de carro en un campo de texto, se genera un evento TextField, que al igual que con los otros Componentes del AWT podemos capturar con el método action(), tal como se demuestra en el ejemplo.

2.5 Áreas de texto

Java, a través del AWT, permite incorporar texto multilínea dentro de zonas de texto (TextArea). Los objetos TextArea se utilizan para elementos de texto que ocupan más de una línea, como puede ser la presentación tanto de texto editable como de sólo lectura.

Para crear una área de texto se pueden utilizar cuatro formas análogas a las que se han descrito en la creación de Campos de Texto. Pero además, para las áreas de texto hay que especificar el número de columnas.

Se puede permitir que el usuario edite el texto con el método setEditable() de la misma forma que se hacía en el TextField. En la figura aparece la representación del applet AreaTexto.java, que presenta dos áreas de texto, una vacía y editable y otra con un texto predefinido y no editable.

import java.awt.*;import java.applet.Applet;

public class AreaTexto extends Applet { TextArea t1,t2;

public void init() { Button boton = new Button( "Aceptar" );

t1 = new TextArea(); t2 = new TextArea( "Tutorial de Java",5,40 ); t2.setEditable( false );

add( t1 ); add( t2 ); add( boton ); }

public boolean action( Event evt,Object obj ) { if( evt.target instanceof Button ) { if( "Aceptar".equals( obj ) )

Page 17: tarea de componentes

{ String texto = t1.getText();

System.out.println( texto ); } }

return true; } }

Para acceder al texto actual de una zona de texto se utiliza el método getText(), tal como muestra el código anterior. Las áreas de texto no generan eventos por sí solas, por lo que hay que utilizar eventos externos, para saber cuando tenemos que acceder a la información contenida en el TextArea. En este caso hemos utilizado un botón que generará un evento al pulsarlo que hará que se recoja el texto que haya escrito en el área de texto que constituye el applet.

2.6 Canvas

Si tenemos un applet que trabaja con imágenes directamente, ya sea un applet gráfico o de dibujo, los lienzos o zonas de dibujo (Canvas) resultan muy útiles.

Los Canvas son un Componente básico que captura eventos de exposición (expose), de ratón y demás eventos relacionados. La clase base Canvas no responde a estos eventos, pero se puede extender esa clase base creando subclases en las que controlemos eseos eventos.

Al permitir saltarse el manejo normal de eventos, y junto con los métodos de representación gráfica, los canvas simplifican la producción de applets que necesitan una única funcionalidad para distintas áreas. Por ejemplo, el applet Lienzo.java, contiene un manejador de eventos que controla el evento mouseDown en el canvas. Si el evento no se genera en el canvas, se le pasa al applet que lo tratará como un evento de ratón normal.

import java.awt.*;import java.applet.Applet;

public class Lienzo extends Applet { Button boton;

public void init() { setLayout( new BorderLayout( 15,15 ) ); boton = new Button( "Test" ); MiCanvas canv = new MiCanvas( 100,100 );

add( "Center",canv ); add( "South",boton ); }

public boolean action( Event evt,Object obj ) { System.out.println( "Evento: " + obj ); return true; }

Page 18: tarea de componentes

public boolean mouseDown( Event evt,int x, int y ) { System.out.println( "Raton: ("+x+","+y+")" ); return true; } }

class MiCanvas extends Canvas { private int ancho; private int alto;

public MiCanvas( int anc,int alt ) { ancho = anc; alto = alt;

reshape( 0,0,anc,alt ); }

public void paint( Graphics g ) { g.setColor( Color.blue ); g.fillRect( 0,0,ancho,alto ); }

public boolean mouseDown( Event evt,int x, int y ) { if( x < ancho && y < alto ) { System.out.println( "Raton en Canvas: ("+x+","+y+")" ); return true; } return false; } }

2.7 Barras de desplazamiento

En determinados applets que necesiten realizar el ajuste de valores lineales en pantalla, resulta útil el uso de barras de desplazamiento (Scrollbar). Las barras de desplazamiento proporcionan una forma de trabajar con rangos de valores o de áreas como el Componente TextArea, que proporciona dos barras de desplazamiento automáticamente.

Si queremos implementar un selector de color, como en el applet Slider.java, podemos utilizar una barra de desplazamiento para cada uno de los colores primarios.

import java.awt.*;import java.applet.Applet;

public class Slider extends Applet { Scrollbar rojo,verde,azul;

public void init() { rojo = new Scrollbar( Scrollbar.VERTICAL,0,1,0,255 );

Page 19: tarea de componentes

verde = new Scrollbar( Scrollbar.VERTICAL,0,1,0,255 ); azul = new Scrollbar( Scrollbar.VERTICAL,0,1,0,255 );

add( rojo ); add( verde ); add( azul ); } }

Este tipo de interfaz proporciona al usuario un punto de referencia visual de un rango y al mismo tiempo la forma de cambiar los valores. Por ello, las barras de desplazamiento son Componentes un poco más complejos que los demás, reflejándose esta complejidad en sus constructores. Al crearlos hay que indicar su orientación, su valor inicial, los valores mínimo y máximo que puede alcanzar y el porcentaje de rango que estará visible.

También podríamos utilizar una barra de desplazamiento para un rango de valores de color, tal como se muestra en el ejemplo Ranger.java.

import java.awt.*;import java.applet.Applet;

public class Ranger extends Applet { Scrollbar rango;

public void init() { rango = new Scrollbar( Scrollbar.HORIZONTAL,0,64,0,255 );

add( rango ); } }

Como se puede ver, el ancho de la barra es mayor, en relación al Scrollbar. En este caso, maxValue representa el valor máximo para el lado izquierdo de la barra. Si se quieren representar 64 colores simultáneamente, es decir [0-63] a [192-255], maxValue debería ser 192.

Igual que otros Componentes, las barras de desplazamiento generan eventos; pero al contrario que en el resto, se tiene que utilizar el método handleEvent() directamente, en lugar del método action(). El destino del evento será un objeto de la clase Scrollbar, a partir de éste se obtiene la posición de la barra de desplazamiento.

Como se habrá podido observar en los applets anteriores, las barras de desplazamiento no disponen de un display o zona donde se muestren directamente los valores asociados a los desplazamientos. Al contrario, si se desea eso, es necesario añadir explícitamente una caja de texto, tal como se muestra en el ejemplo RangoRojo.java.

import java.awt.*;import java.applet.Applet;

Page 20: tarea de componentes

public class RangoRojo extends Applet { Scrollbar rango; TextField valor; Label etiqueta;

public void init() { rango = new Scrollbar( Scrollbar.HORIZONTAL,0,1,0,255 ); valor = new TextField( "0",5 ); etiqueta = new Label( "Rojo (0-255)" );

setLayout( new GridLayout( 1,3 ) ); valor.setEditable( false );

add( etiqueta ); add( rango ); add( valor ); }

public boolean handleEvent( Event evt ) { if( evt.target instanceof Scrollbar ) { valor.setText( Integer.toString( ((Scrollbar)evt.target).getValue() ) ); return true; }

return super.handleEvent( evt ); } }

Ese era el código del applet que construye la ventana de la figura y actualiza el campo de texto asociado. No implementa ninguna otra acción o event

2.8 Diseño de componentes propios

También podemos atrevernos en el diseño de Componentes propios. Deberán ser una subclase de Canvas o Panel. Para mostrarlos en pantalla deberemos sobreescribir los métodos paint(), update(), minimumSize() y preferredSize() y para controlar los eventos tendremos que sobreescribir el método handleEvent().

Para que podamos reusarlos, tenemos que poner cuidado en el diseño, implementando métodos get y set, lanzando excepciones cuando proceda y permitiendo el acceso a los campos de que disponga nuestro Componente.

Veamos un ejemplo, Separador.java, en que a partir de la clase Canvas, creamos un separador que será una línea vertical u horizontal simulando tres dimensiones.

import java.awt.*;

public class Separador extends Canvas { public final static int HORIZONTAL = 0; public final static int VERTICAL = 1;

Page 21: tarea de componentes

int orientacion; Dimension sepTama,sepDim;

public Separador( int lon,int thick,int orient ) { orientacion = orient; if( orient == HORIZONTAL ) sepTama = new Dimension( lon,thick ); else sepTama = new Dimension( thick,lon ); }

public int getOrientacion() { return orientacion; }

public void setOrientacion( int orient) { if( orient > VERTICAL || orient < HORIZONTAL ) throw new IllegalArgumentException( "Orientación ilegal" );

if( orientacion != orient ) { orientacion = orient; sepDim = new Dimension( sepDim.height,sepDim.width ); invalidate(); } }

public Dimension preferredSize() { return sepDim; }

public Dimension minimumSize() { return sepDim; }

public void paint( Graphics g ) { int x1,y1,x2,y2; Rectangle bbox = bounds(); Color c = getBackground(); Color brillo = c.brighter(); Color oscuro = c.darker();

if( orientacion == HORIZONTAL ) { x1 = 0; x2 = bbox.width - 1; y1 = y2 = bbox.height/2 - 1; } else { x1 = x2 = bbox.width/2 - 1; y1 = 0; y2 = bbox.height - 1; }

Page 22: tarea de componentes

g.setColor( oscuro ); g.drawLine( x1,y1,x2,y2 );

g.setColor( brillo ); if( orientacion == HORIZONTAL ) g.drawLine( x1,y1+1,x2,y2+1 ); else g.drawLine( x1+1,y1,x2+1,y2 ); } }

El código que mostramos a continuación EjSeparador.java, muestra un ejemplo de uso de nuestro separador recién creado:

import java.awt.*;import java.applet.Applet;

public class EjSeparador extends Applet { public final static int HORIZONTAL = 0; public final static int VERTICAL = 1;

public void init() { setLayout( new BorderLayout() );

TextField texto1 = new TextField( "Hola",20 ); TextField texto2 = new TextField( "que tal",20 ); Separador raya = new Separador( 8,2,HORIZONTAL );

add( "North",texto1 ); add( "Center",raya ); add( "South",texto2 ); } }

Y ahora podemos complicar nuestro Componente, o utilizarlo como base para el desarrollo de otros más complejos. Por ejemplo, vamos a implementar un Componente de Grupo, que extenderemos de la clase Panel y que la utilizaremos para enmarcar a otros Componentes, tal como se utiliza en otros entornos de ventanas. En el código fuente de la implementación del Componente, Grupo.java, podemos observar que las acciones seguidas son exactamente las mismas que en el caso anterior: sobreescribir los métodos que necesitamos y punto.

import java.awt.*;

public class Grupo extends Panel { String Rotulo; int altoTexto; int longTexto; int offset; static Font defFuente; FontMetrics metFuente;

public Grupo( String titulo ) { altoTexto = 20;

Page 23: tarea de componentes

longTexto = 20; offset = 10; Rotulo = titulo; setLayout( null ); metFuente = getFontMetrics( defFuente ); }

public void paint( Graphics g ) { Dimension d = size(); Font fuente = getFont(); if( fuente != null ) metFuente = getFontMetrics( fuente );

longTexto = metFuente.stringWidth( Rotulo ); altoTexto = metFuente.getHeight(); g.setColor( Color.gray ); g.drawRect( 0, altoTexto/2,d.width-3,d.height-altoTexto/2-3 ); g.setColor( Color.white ); g.drawRect( 1,altoTexto/2+1,d.width-3,d.height-altoTexto/2-3 ); g.setColor( getBackground() ); g.drawLine( offset,altoTexto/2,offset+longTexto+12,altoTexto/2 ); g.drawLine( offset,altoTexto/2+1,offset+longTexto+12,altoTexto/2+1 ); g.setColor( getForeground() ); g.drawString( Rotulo,offset+6,altoTexto-3 ); }

public boolean handleEvent( Event evt ) { return( super.handleEvent( evt ) ); }

public Dimension preferredSize() { return( minimumSize() ); }

public Dimension minimumSize() { return( new Dimension( 100,100 ) ); }

static { defFuente = new Font( "Dialog",0,12 ); } }

Un ejemplo de uso, lo podremos observar compilando y ejecutando el código que se puestra a continuación, EjGrupo.java:

import java.awt.*;import java.applet.Applet

public class EjGrupo extends Applet { Label etiq; Button izqda; Button dcha; Button todo;

Page 24: tarea de componentes

public void init() { setBackground( Color.lightGray ); setLayout( null );

Grupo g = new Grupo( "Etiqueta" ); add( g ); g.setFont( new Font("Dialog",0,14 ) ); g.reshape( insets().left + 10,insets().top + 20,290,40 ); g.setLayout( new FlowLayout( FlowLayout.CENTER,10,10 ) ); etiq = new Label( "Hola Java!" ); g.add( etiq );

Grupo g2 = new Grupo( "Botones" ); add( g2 ); g2.reshape( insets().left+10,80,290,60 ); izqda = new Button( "Botn 1" );� g2.add( izqda ); izqda.reshape( 20,20,70,30 ); dcha = new Button( "Botn 2" );� g2.add( dcha ); dcha.reshape( 110,20,70,30 ); todo = new Button( "Botn 3" );� g2.add( todo ); todo.reshape( 200,20,70,30 ); } }

3. CONTENEDORES

Container es una clase abstracta derivada de Component, que representa a cualquier componente que pueda contener otros componentes. Se trata, en esencia, de añadir a la clase Component la funcionalidad de adición, sustracción, recuperación, control y organización de otros componentes.

El AWT proporciona cuatro clases de Contenedores:

Window Frame Dialog Panel

Además de estos Contenedores, la clase Applet también es un Contenedor, es un subtipo de la clase Panel y puede tener Componentes.

Window

Es una superficie de pantalla de alto nivel (una ventana). Una instancia de la clase Window no puede estar enlazada o embebida en otro Contenedor. Una instancia de esta clase no tiene ni título ni borde.

Page 25: tarea de componentes

Frame

Es una superficie de pantalla de alto nivel (una ventana) con borde y título. Una instancia de la clase Frame puede tener una barra de menú. Una instancia de esta clase es mucho más aparente y más semejante a lo que nosotros entendemos por ventana.

Dialog

Es una superficie de pantalla de alto nivel (una ventana) con borde y título. Una instancia de la clase Dialog no puede existir sin una instancia asociada de la clase Frame.

Panel

Es un Contenedor genérico de Componentes. Una instancia de la clase Panel, simplemente proporciona un Contenedor al que ir añadiendo Componentes.

Crear un Contenedor

Antes de poder incorporar Componentes a la interface de usuario que se desea implementar, el programador debe crear un Contenedor. Cuando se construye una aplicación, el programador debe crear en primer lugar una instancia de la clase Window o de la clase Frame. Cuando lo que se construye es un applet, ya existe un Frame (la ventana del navegador). Debido a que la clase Applet está derivada de la clase Panel, el programador puede ir añadiendo Componentes directamente a la instancia que se crea de la clase Applet.

En el siguiente ejemplo se crea un Frame vacío. El título del Frame, que corresponderá al título de la ventana, se fija en la llamada al constructor. Un Frame inicialmente está invisible, para poder verlo es necesario invocar al método show():

import java.awt.*; public class Ejemplo1 { public static void main( String args[] ) { Frame f = new Frame( "Ejemplo 1" ); f.show(); } }

En el código de ejemplo que sigue, extendemos el código anterior para que la nueva clase sea una subclase de la clase Panel. En el método main() de esta nueva clase se crea una instancia de ella y se le incorpora un objeto Frame llamando al método add(). El resultado de ambos ejemplos es idéntico a efectos de apariencia en pantalla:

import java.awt.*; public class Ejemplo2 extends Panel { public static void main( String args[] ) { Frame f = new Frame( "Ejemplo 2" ); Ejemplo2 ej = new Ejemplo2();

Page 26: tarea de componentes

f.add( "Center",ej ); f.pack(); f.show(); } }

Derivando la nueva clase directamente de la clase Applet en vez de Panel, nuestro ejemplo puede ahora ejecutarse tanto como una aplicación solitaria como dentro de una página HTML en un navegador. El código siguiente muestra esta circunstancia:

import java.awt.*; public class Ejemplo3 extends java.applet.Applet { public static void main( String args[] ) { Frame f = new Frame( "Ejemplo 3" ); Ejemplo3 ej = new Ejemplo3(); f.add( "Center",ej ); f.pack(); f.show(); } }

Un objeto Window, y en algunos casos incluso un objeto Dialog, pueden reemplazar al objeto Frame. Son Contenedores válidos y los Componentes se añaden en ellos del mismo modo que se haría sobre un Frame.

Añadir Componentes a un Contenedor

Para que la interface sea útil, no debe estar compuesta solamente por Contenedores, éstos deben tener Componentes en su interior. Los Componentes se añaden al Contenedor invocando al método add() del Contenedor. Este método tiene tres formas de llamada que dependen del manejador de composición o layout manager que se vaya a utilizar sobre el Contenedor.

En el código siguiente, incorporamos dos botones al código del último ejemplo. La creación se realiza en el método init() porque éste siempre es llamado automáticamente al inicializarse el applet. De todos modos, al inciarse la ejecución se crean los botones, ya que el método init() es llamado tanto por el navegador como por el método main():

import java.awt.*; public class Ejemplo4 extends java.applet.Applet { public void init() { add( new Button( "Uno" ) ); add( new Button( "Dos" ) ); } public static void main( String args[] ) { Frame f = new Frame( "Ejemplo 4" ); Ejemplo4 ej = new Ejemplo4(); ej.init();

Page 27: tarea de componentes

f.add( "Center",ej ); f.pack(); f.show(); } }

4. CREACION DE APLICACIONES CON AWT

Para crear una aplicación utilizando AWT, vamos a ver en principio cómo podemos generar el interface de esa aplicación, mostrando los distintos elementos del AWT, posteriormente volveremos hacia la implementación de la aplicación.

Interface

Crear el Marco de la aplicación (Frame) Inicializar Fuentes, Colores, Layouts y demás recursos Crear menús y Barras de Menús Crear los controles, diálogos, ventanas, etc.

Implementación

Incorporar controladores de eventos Incorporar funcionalidad (threads, red, etc.) Incorporar un controlador de errores (excepciones)

4.1 Crear el marco de la aplicacion

El Contenedor de los Componentes es el Frame. También es la ventana principal de la aplicación, lo que hace que para cambiar el icono o el cursor de la aplicación no sea necesario crear métodos nativos; al ser la ventana un Frame, ya contiene el entorno básico para la funcionalidad de la ventana principal.

Vamos a empezar a crear una aplicación básica, a la que iremos incorporando Componentes. Quizás vayamos un poco de prisa en las explicaciones que siguen; no preocuparse, ya que lo que no quede claro ahora, lo estará más tarde. El problema es que para poder profundizar sobre algunos aspectos de Java, necesitamos conocer otros previos, así que proporcionaremos un ligero conocimiento sobre algunas características de Java y del AWT, para que nos permitan entrar a fondo en otras; y ya conociendo estas últimas, volveremos sobre las primeras. Un poco lioso, pero imprescindible.

En el archivo AplicacionAWT.java, se encuentra el código completo de la aplicación que vamos ir construyendo a lo largo de este repaso general por las características de que dispone el AWT.

Comenzaremos el desarrollo de nuestra aplicación básica con AWT a partir del código que mostramos a continuación:

import java.awt.*;

public class AplicacionAWT extends Frame {

Page 28: tarea de componentes

static final int HOR_TAMANO = 300; static final int VER_TAMANO = 200;

public AplicacionAWT() { super( "Aplicación Java con AWT" );

pack(); resize( HOR_TAMANO,VER_TAMANO ); show(); }

public static void main( String args[] ) { new AplicacionAWT(); } }

La clase anterior es un Frame, ya que extiende esa clase y hereda todas sus características. Tiene un método, el constructor, que no admite parámetros.

Además, se hace una llamada explícita al constructor super, es decir, al constructor de la clase padre, para pasarle como parámetro la cadena que figurará como el título de la ventana.

La llamada a show() es necesaria, ya que por defecto, los Contenedores del AWT se crean ocultos y hay que mostrarlos explícitamente. La llamada a pack() hace que los Componentes se ajusten a sus tamaños correctos en función del Contenedor en que se encuentren situados.

La ejecución de la aplicación mostrará la siguiente ventana en pantalla:

Los atributos fundamentales de la ventana anterior son:

Marco de 300x200 pixels No tiene barra de menú No tiene ningún Componente Título "Aplicación Java con

AWT" Color de fondo por defecto Layout por defecto Fondo de la ventana vacío

4.2 Inicializar fuentes, colores y recursos

Vamos a ir alterando los recursos de la ventana de la aplicación Java que estamos desarrollando con el AWT, para ir incorporando y visualizando los distintos Componentes que proporciona AWT. Insertemos algunas líneas de código en el constructor para inicializar la aplicación:

Cambiemos el font de caracteres a Times Roman de 12 puntos

Page 29: tarea de componentes

setFont( new Font( "TimesRoman",Font.PLAIN,12 ) );

Fijemos los colores de la ventana para que el fondo sea Blanco y el texto resalte en Negro

setBackground( Color.white ); setForeground( Color.black );

Seleccionemos como disposición de los Componentes el BorderLayout para este Contenedor

setLayout( new BorderLayout() );

Incorporemos gráficos. Usamos métodos definidos en la clase Graphics; por ejemplo, reproduzcamos el título de la ventana en medio con una fuente Time Roman de 24 puntos en color Azul. Es necesario utilizar new con Font ya que en Java, todo son objetos y no podríamos utilizar un nuevo font de caracteres sin antes haberlo creado

public void paint( Graphics g ) { g.setFont( new Font( "TimesRoman",Font.BOLD,24 ) ); g.setColor( Color.blue ); g.drawString( getTitle(),30,50 ); }

Incorporemos en la parte inferior de la ventana dos botones: Aceptar y Cancelar

Panel p = new Panel(); p.add( new Button( "Aceptar" ) ); p.add( new Button( "Cancelar" ) ); add( "South",p );

Los Componentes se incorporan al Contenedor a través de los dos métodos add() que hay definidos:

add( Component c ); add( String s,Component c );

Los Componentes también se podían haber insertado en el Frame, organizándolos en una cierta forma, teniendo en cuenta que su manejador de composición es un BorderLayout. Por ejemplo:

add( "South",new Button( "Aceptar ) ); add( "South",new Button( "Cancelar" ) );

Hemos utilizado un Panel y no el segundo método, porque es más útil el organizar los Componentes en pequeñas secciones. Así, con nuestro código podemos considerar al Panel como una entidad separada del Frame, lo cual permitiría cambiar el fondo, layout, fuente, etc. del Panel sin necesidad de tocar el Frame.

Si ejecutamos de nuevo la aplicación con los cambios que hemos introducido, aparecerá ante nosotros la ventana que se muestra a continuación.

Page 30: tarea de componentes

Si intentásemos en esta aplicación cerrar la ventana, no sucede nada. Cuando se intenta cerrar la ventana, el sistema envía un evento que no se está tratando. Incorporemos pues un controlador de eventos y empecemos tratando el evento WINDOW_DESTROY, generado cuando se intenta cerrar la ventana:

public boolean handleEvent( Event evt ) { switch( evt.id ) { case Event.WINDOW_DESTROY: { System.exit( 0 ); return true; } default: return false; } }Si ahora ejecutamos de nuevo la aplicación y cerramos la ventana... Efectivamente se cierra, tal como se suponía.

4.3 Crear menús y barras de menús

En la actual versión del AWT que se proporciona con el JDK, sólo se permite crear menús a través de código, ya que Java todavía no dispone de un formato de recursos y tampoco hay un diseñador como pueden ser AppStudio, Delphi o X-Designer; aunque terminará habiendo uno, con seguridad.

No hay ningún método para diseñar una buena interface, todo depende del programador. Los menús son el centro de la aplicación. La diferencia entre una aplicación útil y otra que es totalmente frustrante radica en la organización de los menús, pero eso, las reglas del diseño de un buen árbol de menús, no están claras. Hay un montón de libros acerca de la ergonomía y de cómo se debe implementar la interacción con el usuario. Lo cierto es que por cada uno que defienda una idea, seguro que hay otro que defiende la contraria. Todavía no hay un acuerdo para crear un estándar, con cada Window Manager se publica una guía de estilo diferente. Así que, vamos a explicar lo básico, sin que se deba tomar como dogma de fe, para que luego cada uno haga lo que mejor le parezca.

La interface MenuContainer solamente se puede implementar sobre un Frame. Un applet que desee tener un menú, debe crear un Frame en primer lugar. El código de la función que vamos a ver, crea una barra de menús y se llama desde el constructor del

Page 31: tarea de componentes

Frame. La función es private porque no queremos que se pueda llamar desde ninguna otra clase.

private void InicializaMenus() { mbarra = new MenuBar(); Menu m = new Menu( "Archivo" ); m.add( new MenuItem( "Nuevo") ); m.add( new MenuItem( "Abrir") ); m.add( new MenuItem( "Guardar") ); m.add( new MenuItem( "Guardar como") ); m.add( new MenuItem( "Imprimir") ); m.addSeparator(); m.add( new MenuItem( "Salir") ); mbarra.add( m );

m = new Menu( "Ayuda" ); m.add( new MenuItem( "Ayuda!" ) ); m.addSeparator(); m.add( new MenuItem( "Acerca de..." ) ); mbarra.add( m );

setMenuBar( mbarra ); }

El menú que crea esta función tendrá la apariencia que muestra la figura siguiente:

Los eventos generados por las opciones de un menú se manejan del mismo modo que los Botones, Listas, etc. En el caso de menús, es el evento ACTION_EVENT de la clase java.awt.MenuItem el que se genera y en evt.target se nos indica la opción seleccionada.

case Event.ACTION_EVENT: { if( evt.target instanceof MenuItem ) { if( "Nuevo".equals( evt.arg ) ) AplicacionAWT aAwt = new AplicacionAWT();; if( "Abrir".equals( evt.arg ) ) System.out.println( "Opcion -Abrir-" ); if( "Guardar".equals( evt.arg ) ) System.out.println( "Opcion -Guardar-" ); if( "Guardar como".equals( evt.arg ) ) System.out.println( "Opcion -Guardar como-" ); if( "Imprimir".equals( evt.arg ) ) System.out.println( "Opcion -Imprimir-" ); if( "Salir".equals( evt.arg ) ) System.exit( 0 ); if( "Ayuda!".equals( evt.arg ) ) System.out.println( "No hay ayuda" ); if( "Acerca de".equals( evt.arg ) )

Page 32: tarea de componentes

System.out.println( "Opcion -Acerca de-" ); } }

En el código anterior hemos tratado los eventos del menú. Para más seguridad, aunque no sea estrictamente necesario, lo primero que hacemos es asegurarnos de que el objeto evt.target es realmente un objeto MenuItem, es decir, procede de la barra de menús; y después comprobamos la opción que se ha seleccionado.

Como todo, también se puede rizar el rizo y conseguir reproducir los sistemas de menús que estamos acostumbrados a ver en las aplicaciones que manejamos habitualmente. Un ejemplo de ello son los menús en cascada, semejantes al que muestra la figura y que ha sido generado mediante la aplicación Cascada.java.

Básicamente se utiliza la técnica ya descrita, pero en vez de crear un nuevo MenuItem se crea un nuevo Menu, lo que origina el menú en cascada.

No obstante, y volviendo al diseño de interfaces, no debe abusarse de este tipo de menús, porque pueden crear mucha más confusión al usuario. Siempre se debe tener en mente que los usuarios tienen que navegar habitualmente por una gran cantidad de menús en las más diversas aplicaciones, por lo que no debemos esconderles demasiado las opciones que les pueden interesar.

4.4 Diálogos y ventanas

Una Ventana genérica, Window, puede utilizarse simplemente para que sea la clase padre de otras clases y se puede intercambiar por un Diálogo, Dialog, sin pérdida de funcionalidad. No se puede decir lo mismo de un Frame.

Se podría crear un menú pop-up con una Ventana, pero lo cierto es que en esta versión del JDK hay un montón de bugs y no merece la pena el enfrascarse en el intento. No obstante, hay ciertos métodos que están en la clase Window y que no están presentes en otras clases que pueden resultar interesantes y necesitar una Ventana si queremos emplearlos. Son:

getToolkit() getWarningString() pack() toBack() toFront()

Un Diálogo es una subclase de Window, que puede tener un borde y ser modal, es decir, no permite hacer nada al usuario hasta que responda al diálogo. Esto es lo que se usa en las cajas de diálogo "Acerca de...", en la selección en listas, cuando se pide una entrada numérica, etc.

Page 33: tarea de componentes

El código Java que se expone a continuación, implementa el diálogo Acerca de para la aplicación. Esta clase se crea oculta y necesitaremos llamar al método show() de la propia clase para hacerla visible.

class AboutDialog extends Dialog { static int HOR_TAMANO = 300; static int VER_TAMANO = 150;

public AboutDialog( Frame parent ) { super( parent,"Acerca de...",true ); this.setResizable( false ); setBackground( Color.gray ); setLayout( new BorderLayout() );

Panel p = new Panel(); p.add( new Button( "Aceptar" ) ); add( "South",p ); resize( HOR_TAMANO,VER_TAMANO ); }

public void paint( Graphics g ) { g.setColor( Color.white ); g.drawString( "Aplicación Java con AWT", HOR_TAMANO/4,VER_TAMANO/3 ); g.drawString( "Versión 1.00", HOR_TAMANO/3+15,VER_TAMANO/3+20 ); }

public boolean handleEvent( Event evt ) { switch( evt.id ) { case Event.ACTION_EVENT: { if( "Aceptar".equals( evt.arg ) ) { hide(); return true; } } default: return false; } } }

La ventana que aparece en pantalla generada por la clase anterior es la que muestra la figura:

Las aplicaciones independientes deberían heredar tomando como padre la ventana principal de esa aplicación. Así pueden implementar la interface MenuContainer y proporcionar menús.

No hay razón aparente para que sea una subclase de la clase Frame, pero si se quiere proporcionar funcionalidad extra, sí debería serlo, en vez de serlo de su padre: Window. Esto es así porque Frame implementa la interface MenuContainer, con lo

Page 34: tarea de componentes

cual tiene la posibilidad de proporcionar menús y cambiar el cursor, el icono de la aplicación, etc.

4.5 Paneles

La clase Panel es el más simple de los Contenedores de Componentes gráficos. En realidad, se trataba de crear una clase no-abstracta (Container sí lo es) que sirviera de base a los applet y a otras pequeñas aplicaciones. La clase Panel consta de dos métodos propios: el constructor, cuyo fin es crear un nuevo Panel con un LayoutManager de tipo FlowLayout (el de defecto), y el método addNotify() que, sobrecargando la función del mismo nombre en la clase Container, llama al método createPanel() del Toolkit adecuado, creando así un PanelPeer. El AWT enviará así al Panel (y por tanto al applet) todos los eventos que sobre él ocurran. Esto que puede parecer un poco rebuscado, obedece al esquema arquitectónico del AWT; se trata del bien conocido esquema de separación interface/implementación que establece por un lado una clase de interface y por otras distintas clases de implementación para cada una de las plataformas elegidas.

El uso de Paneles permite que las aplicaciones puedan utilizar múltiples layouts, es decir, que la disposición de los componentes sobre la ventana de visualización pueda modificarse con mucha flexibilidad. Permite que cada Contenedor pueda tener su propio esquema de fuentes de caracteres, color de fondo, zona de diálogo, etc.

Podemos, por ejemplo, crear una barra de herramientas para la zona superior de la ventana de la aplicación o incorporarle una zona de estado en la zona inferior de la ventana para mostrar información útil al usuario. Para ello vamos a implementar dos Paneles:

class BarraHerram extends Panel { public BarraHerram() { setLayout( new FlowLayout() ); add( new Button( "Abrir" ) ); add( new Button( "Guardar" ) ); add( new Button( "Cerrar" ) );

Choice c = new Choice(); c.addItem( "Times Roman" ); c.addItem( "Helvetica" ); c.addItem( "System" ); add( c ); add( new Button( "Ayuda" ) ); } }

class BarraEstado extends Panel { Label texto; Label mas_texto;

public BarraEstado() { setLayout( new FlowLayout() ); add( texto = new Label( "Creada la barra de estado" ) ); add( mas_texto = new Label( "Información adicional" ) ); }

Page 35: tarea de componentes

public void verEstado( String informacion ) { texto.setText( informacion ); } }

Ahora, para dar funcionalidad, debemos crear los objetos correspondientes a la barra de herramientas y a la barra de estado con new; al contrario que en C++, en Java todos los objetos deben ser creados con el operador new:

add( "North",tb = new ToolBar() ); add( "South",sb = new StatusBar() );

También vamos a incorporar un nuevo evento a nuestro controlador, para que maneje los eventos de tipo ACTION_EVENT que le llegarán cuando se pulsen los botones de la barra de herramientas o se realice alguna selección, etc.

case Event.ACTION_EVENT: { be.verEstado( evt.arg.toString() ); return true; }

Cuando la aplicación reciba este tipo de evento, alterará el contenido de la barra de estado para mostrar la información de la selección realizada o el botón pulsado.

Al final, la apariencia de la aplicación en pantalla es la que presenta la figura anterior.

4.6 LAYOUTS

Los layout managers o manejadores de composición, en traducción literal, ayudan a adaptar los diversos Componentes que se desean incorporar a un Panel, es decir, especifican la apariencia que tendrán los Componentes a la hora de colocarlos sobre un Contenedor. Java dispone de varios, en la actual versión, tal como se muestra en la imagen:

Page 36: tarea de componentes

¿Por qué Java proporciona estos esquemas predefinidos de disposición de componentes? La razón es simple: imaginemos que deseamos agrupar objetos de distinto tamaño en celdas de una rejilla virtual: si confiados en nuestro conocimiento de un sistema gráfico determinado, codificamos a mano tal disposición, deberemos preveer el redimensionamiento del applet, su repintado cuando sea cubierto por otra ventana, etc., además de todas las cuestiones relacionadas con un posible cambio de plataforma (uno nunca sabe a donde van a ir a parar los propios hijos, o los applets).

En el applet que genera la figura siguiente, se utilizan los diferentes tipos de layouts que proporciona el AWT.

El ejemplo AwtGui.java, ilustra el uso de paneles, listas, barras de desplazamiento, botones, selectores, campos de texto, áreas de texto y varios tipos de layouts.

En el tratamiento de los Layouts se utiliza un método de validación, de forma que los Componentes son marcados como no válidos cuando un cambio de estado afecta a la geometría o cuando el Contenedor tiene un hijo incorporado o eliminado. La validación se realiza automáticamente cuando se llama a pack() o show(). Los Componentes visibles marcados como no válidos no se validan automáticamente.

En el ejemplo de control de eventos que se muestra a continuación, la llamada a validate() hace que se realicen en un bloque, en un solo paso, todos los cálculos necesarios para la validación del layout.

public boolean action( Event evt,Object obj ) {

Page 37: tarea de componentes

if( obj.equals( "Cambia Font" ) ) { boton1.setFont( nuevoFont ); boton2.setFont( nuevoFont ); etiqueta.setFont( nuevoFont ); campoTexto.setFont( nuevoFont );

validate(); return true; } }

4.6.1 FlowLayout

Es el más simple y el que se utiliza por defecto en todos los Paneles si no se fuerza el uso de alguno de los otros. Los Componentes añadidos a un Panel con FlowLayout se encadenan en forma de lista. La cadena es horizontal, de izquierda a derecha, y se puede seleccionar el espaciado entre cada Componente.

Por ejemplo, podemos poner un grupo de botones con la composición por defecto que proporciona FlowLayout:

import java.awt.*;import java.applet.Applet;

public class AwtFlow extends Applet { Button boton1,boton2,boton3;

public void init() { boton1 = new Button( "Aceptar" ); boton2 = new Button( "Abrir" ); boton3 = new Button( "Cerrar" );

add( boton1 ); add( boton2 ); add( boton3 ); } }

Este código, AwtFlow.java, construye tres botones con un pequeño espacio de separación entre ellos.

Page 38: tarea de componentes

4.6.2 BorderLayout

La composición BorderLayout (de borde) proporciona un esquema más complejo de colocación de los Componentes en un panel. La composición utiliza cinco zonas para colocar los Componentes sobre ellas: Norte, Sur, Este, Oeste y Centro. Es el layout o composición que se utilizan por defecto Frame y Dialog.

El Norte ocupa la parte superior del panel, el Este ocupa el lado derecho, Sur la zona inferior y Oeste el lado izquierdo. Centro representa el resto que queda, una vez que se hayan rellenado las otras cuatro partes.

Con BorderLayout se podrían representar botones de dirección:

import java.awt.*;import java.applet.Applet;

public class AwtBord extends Applet { Button botonN,botonS,botonE,botonO,botonC;

public void init() { setLayout( new BorderLayout() );

botonN = new Button( "Norte" ); botonS = new Button( "Sur" ); botonE = new Button( "Este" ); botonO = new Button( "Oeste" ); botonC = new Button( "Centro" );

add( "North",botonN ); add( "South",botonS ); add( "East",botonE ); add( "West",botonO ); add( "Center",botonC ); } }

Este es el código, AwtBord.java, que genera el applet de botones de dirección:

4.6.3 GridBagLayout

Es igual que la composición de GridLayout, con la diferencia que los Componentes no necesitan tener el mismo tamaño. Es quizá el layout más sofisticado y versátil de los que actualmente soporta AWT.

En la figura siguiente vemos la imagen generada por un panel que usa el GridBagLayout para soportar diez botones en su interior:

import java.awt.*;import java.applet.Applet;

Page 39: tarea de componentes

public class AwtGBag extends Applet {

public void init() { GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints gbc = new GridBagConstraints();

setLayout( gridbag ); gbc.fill = GridBagConstraints.BOTH; gbc.weightx = 1.0; Button boton0 = new Button( "Botón 0" ); gridbag.setConstraints( boton0,gbc ); add( boton0 ); Button boton1 = new Button( "Botón 1" ); gridbag.setConstraints( boton1,gbc ); add( boton1 ); Button boton2 = new Button( "Botón 2" ); gridbag.setConstraints( boton2,gbc ); add( boton2 );

gbc.gridwidth = GridBagConstraints.REMAINDER; Button boton3 = new Button( "Botón 3" ); gridbag.setConstraints( boton3,gbc ); add( boton3 );

gbc.weightx = 0.0; Button boton4 = new Button( "Botón 4" ); gridbag.setConstraints( boton4,gbc ); add( boton4 );

gbc.gridwidth = GridBagConstraints.RELATIVE; Button boton5 = new Button( "Botón 5" ); gridbag.setConstraints( boton5,gbc ); add( boton5 );

gbc.gridwidth = GridBagConstraints.REMAINDER; Button boton6 = new Button( "Botón 6" ); gridbag.setConstraints( boton6,gbc ); add( boton6 );

gbc.gridwidth = 1; gbc.gridheight = 2; gbc.weighty = 1.0; Button boton7 = new Button( "Botón 7" ); gridbag.setConstraints( boton7,gbc ); add( boton7 );

gbc.weighty = 0.0; gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.gridheight = 1; Button boton8 = new Button( "Botón 8" ); gridbag.setConstraints( boton8,gbc ); add( boton8 ); Button boton9 = new Button( "Botón 9" ); gridbag.setConstraints( boton9,gbc );

Page 40: tarea de componentes

add( boton9 ); } }

Este es el código, AwtGBag.java, que utilizamos para generar la imagen del ejemplo.

Para aprovechar de verdad todas las posibilidades que ofrece este layout, hay que pintar antes en papel como van a estar posicionados los Componentes; utilizar gridx, gridy, gridwidth y gridheight en vez de GridBagConstraints.RELATIVE, porque en el proceso de validación del layout pueden quedar todos los Componentes en posición indeseable. Además, se deberían crear métodos de conveniencia para hacer más fácil el posicionamiento de los Componentes. En el ejemplo siguiente, AwtGBagConv.java, creamos el método de conveniencia addComponente() para la incorporación de nuevos Componentes al layout, lo que hace más sencillo el manejo de los Constraints:

import java.awt.*;import java.applet.Applet;

public class AwtGBagConv extends Applet { GridBagLayout gridbag = new GridBagLayout();

void addComponente( Component comp,int gridx,int gridy, int gridw,int gridh ) { GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = gridx; gbc.gridy = gridy; gbc.gridwidth = gridw; gbc.gridheight = gridh; gridbag.setConstraints( comp,gbc ); add( comp ); }

public void init() { setLayout( gridbag ); addComponente( new Label( "Nombre:" ),0,0,1,1 ); addComponente( new TextField( 12 ),1,0,2,1 ); addComponente( new TextArea( 5,20 ),0,1,2,2 ); addComponente( new Checkbox( "Sí?" ),2,1,1,1 ); addComponente( new List(),2,2,1,1 ); } }

4.6.4 CardLayout

Este es el tipo de composición que se utiliza cuando se necesita una zona de la ventana que permita colocar distintos Componentes en esa misma zona. Este layout suele ir asociado con botones de lista (Choice), de tal modo que cada selección determina el panel (grupo de componentes) que se presentarán.

En la figura siguiente mostramos el efecto de la selección sobre la apriencia de la ventana que contiene el panel con la composición CardLayout:

Page 41: tarea de componentes

import java.awt.*;import java.applet.Applet;

public class AwtCard extends Applet { Panel card; final static String PanelBoton = "Panel con Botones"; final static String PanelTexto = "Panel con Campo de Texto";

public void init() { setLayout( new BorderLayout() );

Panel ac = new Panel(); Choice c = new Choice();

c.addItem( PanelBoton ); c.addItem( PanelTexto ); ac.add( c ); add( "North",ac );

card = new Panel(); card.setLayout( new CardLayout() );

Panel p1 = new Panel(); p1.add( new Button( "Botón 1" ) ); p1.add( new Button( "Botón 2" ) ); p1.add( new Button( "Botón 3" ) ); Panel p2 = new Panel(); p2.add( new TextField( "Texto",20 ) );

card.add( PanelBoton,p1 ); card.add( PanelTexto,p2 ); add( "Center",card ); }

public boolean action( Event evt,Object arg ) { if( evt.target instanceof Choice ) { ( (CardLayout)card.getLayout() ).show( card,(String)arg ); return true; } return false; } }

Este es el código, AwtCard.java, que hemos utilizado para generar las dos imágenes que muestran el funcionamiento de la composición CardLayout.

Page 42: tarea de componentes

4.6.5 CREAR UN LAYOUT

Se puede crear un Layout personalizado en base a la interface LayoutManager. Hay que redefinir los cinco métodos que utiliza este interface, lo cual puede no resultar sencillo, así que en lo posible se deben utilizar los métodos de colocación de componentes que proporciona AWT, fundamentalmente en el momento en que parezca que ninguno de los Layouts que hemos visto satisface nuestras exigencias, deberíamos volver a comprobar que el GridBagLayout, que es el más flexible, de verdad no cumple nuestros requerimientos.

No obstante, vamos a implementar un layout propio, MiLayout.java, para poder colocar los Componentes en posiciones absolutas del panel que contenga a este layout. Derivamos nuestro nuevo layout de LayoutManager y redefinimos los cinco métodos de la clase para que podamos posicionar los Componentes.

import java.awt.*;

public class MiLayout implements LayoutManager {

public MiLayout() { }

public void addLayoutComponent( String name,Component comp ) { }

public void removeLayoutComponent( Component comp ) { }

public Dimension preferredLayoutSize( Container parent ) { Insets insets = parent.insets(); int numero = parent.countComponents(); int ancho = 0; int alto = 0;

for( int i=0; i < numero; i++ ) { Component comp = parent.getComponent( i ); Dimension d = comp.preferredSize(); Point p = comp.location(); if( ( p.x + d.width ) > ancho ) ancho = p.x + d.width; if( ( p.y + d.height ) > alto ) alto = p.y + d.height; } return new Dimension( insets.left + insets.right + ancho, insets.top + insets.bottom + alto ); }

public Dimension minimumLayoutSize( Container parent ) { Insets insets = parent.insets(); int numero = parent.countComponents(); int ancho = 0; int alto = 0;

Page 43: tarea de componentes

for( int i=0; i < numero; i++ ) { Component comp = parent.getComponent( i ); Dimension d = comp.preferredSize(); Point p = comp.location(); if( ( p.x + d.width ) > ancho ) ancho = p.x + d.width; if( ( p.y + d.height ) > alto ) alto = p.y + d.height; } return new Dimension( insets.left + insets.right + ancho, insets.top + insets.bottom + alto ); }

public void layoutContainer( Container parent ) { int numero = parent.countComponents();

for( int i=0; i < numero; i++ ) { Component comp = parent.getComponent( i ); Dimension d = comp.preferredSize(); comp.resize( d.width,d.height ); } } }

Y ahora vamos a ver un ejemplo en que utilicemos nuestro Layout. Posicionaremos tres botones en el panel y un campo de texto con una etiqueta precediéndolo. La apriencia que tendrá en pantalla será la que se muestra en la figura:

import java.awt.*;import java.applet.Applet;

public class AwtLibre extends Applet { Button boton1,boton2,boton3; Label etiqueta; TextField texto;

public void init() { setLayout( new MiLayout() );

boton1 = new Button( "Aceptar" ); boton2 = new Button( "Abrir" ); boton3 = new Button( "Cerrar" ); etiqueta = new Label( "Texto" );

Page 44: tarea de componentes

texto = new TextField( "",20 );

add( boton1 ); add( boton2 ); add( boton3 ); add( etiqueta ); add( texto );

boton1.move( 0,10 ); boton2.move( 70,10 ); boton3.move( 30,40 ); etiqueta.move( 75,70 ); texto.move( 120,70 ); } }

Este código, AwtLibre.java, es el que genera la imagen anterior creando un applet que utiliza nuestro recién creado layout.

4.7 Control de eventos

En la práctica, esto significaba que una vez inicializado, el programa entraba en un gran bucle en el que continuamente se bloqueaba para comprobar que el usuario estuviese haciendo algo interesante (por ejemplo, pulsar un botón, pulsar una tecla, mover una barra o mover el ratón) y tomar las acciones oportunas. Esta técnica se conoce como polling.

El polling funciona, pero se vuelve demasiado difícil de manejar con las aplicaciones modernas por dos razones fundamentales: Primero, el uso de polling tiende a colocar todo el código de control de eventos en una única localización (dentro de un gran bucle); segundo, las interacciones dentro del gran bucle tienden a ser muy complejas. Además, el polling necesita que el programa esté ejecutando el bucle, consumiendo tiempo de CPU, mientras está esperando a que el usuario se decida a hacer algo, lo que supone un gran despilfarro de recursos.

El AWT resuelve estos problemas abrazando un paradigma diferente, en el que están basados todos los sistemas modernos de ventanas: la orientación a eventos. Dentro del AWT, todas las acciones que pueda realizar el usuario caen dentro de un gran saco que son los eventos. Un evento describe, con suficiente detalle, una acción particular del usuario. En lugar de que el programa activamente recoja todos los eventos generados por el usuario, el sistema Java avisa al programa cuando se produce un evento interesante.

4.7.1 LA CLASE Event

Un contenedor soltado en un entorno gráfico se convierte en rápido receptor de eventos de todo tipo, singularmente de los relacionados con el movimiento del ratón, pulsaciones de teclas, creación/movimiento/destrucción de partes gráficas y, por último, los referidos a acciones del usuario respecto de componentes (elección de un menú, pulsación de un botón, etc.).

Page 45: tarea de componentes

La clase Event es el jugador principal en el juego de los eventos. Intenta capturar las características fundamentales de todos los eventos que genera el usuario. Los datos miembro de la clase Event son los que se indican a continuación:

id - El tipo de evento que se ha producido

target - Componente sobre el que se ha producido el evento

x, y - Las coordenadas en donde se ha producido el evento relativas al Componente que actualmente está procesando ese evento. El origen se toma en la esquina superior izquierda del Componente

key - Para eventos de teclado, es la tecla que se ha pulsado. Su valor será el valor Unicode del carácter que representa la tecla. Otros valores que puede tomas son los de las teclas especiales como INICIO, FIN, F1, F2, etc.

when - Instante en que se ha producido el evento

modifiers - La combinación aritmética del estado en que se encuentran las teclas modificadoras Mays, Alt, Ctrl.

clickCount - El número de clicks de ratón consecutivos. Sólo tiene importancia en los eventos MOUSE_DOWN

arg - Es un argumento dependiente del evento. Para objetos Button, este objeto arg es un objeto String que contiene la etiqueta de texto del botón

evt - El siguiente evento en una lista encadenada de eventos

Una instancia de la clase Event será creada por el sistema Java cada vez que se genere un evento. Es posible, sin embargo, que un programa cree y envíe eventos a los Componentes a través de su método postEvent().

4.7.2 TIPOS DE EVENTOS

Los eventos se catalogan por su naturaleza, que se indicará en el miembro id de su estructura. Los grandes grupos de eventos son:

Eventos de Ventana

Son los que se generan en respuesta a los cambios de una ventana un frame o un dialogo.

WINDOW_DESTROY WINDOW_EXPOSE WINDOW_ICONIFY WINDOW_DEICONIFY WINDOW_MOVED

Page 46: tarea de componentes

1. Eventos de Teclado

Son generados en respuesta a cuando el usuario pulsa y suelta una tecla mientras un Componente tiene el foco de entrada.

KEY_PRESS KEY_RELEASE KEY_ACTION KEY_ACTION_RELEASE

2. Eventos de Ratón

Son los eventos generados por acciones sobre el ratón dentro de los límites de un Componente.

MOUSE_DOWN MOUSE_UP MOUSE_MOVE MOUSE_ENTER MOUSE_EXIT MOUSE_DRAG

3. Eventos de Barras

Son los eventos generados como respuesta a la manipulación de barras de desplazamiento (scrollbars).

SCROLL_LINE_UP SCROLL_LINE_DOWN SCROLL_PAGE_UP SCROLL_PAGE_DOWN SCROLL_ABSOLUTE

4. Eventos de Lista

Son los eventos generados al seleccionar elementos de una lista.

LIST_SELECT LIST_DESELECT

5. Eventos Varios

Son los eventos generados en función de diversas acciones.

ACTION_EVENT LOAD_FILE SAVE_FILE GOT_FOCUS LOST_FOCUS

El applet EventosPrnt.java está diseñado para observar los eventos que se producen sobre él. Cada vez que se genera un evento, el applet responde imprimiendo el evento que ha capturado en la línea de comandos desde donde se ha lanzado el applet.

Page 47: tarea de componentes

Una vez que se haya compilado el código y cargado el applet en el appletviewer o en un navegador con soporte Java, jugar un poco con el applet. Mover el cursor dentro del applet, picar con el ratón, picar y arrastrar, teclear algo, cambiar el tamaño de la ventana y taparla y destaparla con otra. Las acciones anteriores harán que en la ventana en donde se haya lanzado el appletviewer, o en la consola Java en caso de Netscape, vayan apareciendo los textos que indican los eventos que está recibiendo el applet.

Lo cierto es que el uso de System.out.println() en un applet es algo que no debería utilizarse, e incluso puede llegar a no funcionar en algunos sistemas, pero tenía la ventaja de ser la forma más fácil de ver los eventos. No obstante, vamos a reescribir el código del applet utilizando una Lista.

Una Lista es una lista de cadenas o Strings definidas en java.awt.List. Crearemos una lista de 25 líneas y no permitiremos selección múltiple, que son los dos parámetros que necesita el constructor del objeto List. El código EventosList.java que se muestra a continuación corresponde al anterior ejemplo un poco modificado.

import java.awt.*;import java.applet.Applet;

public class EventosList extends Applet { List lista;

public void init() { lista = new List( 25,false ); add( lista ); lista.addItem( "Evento init" ); }

public void start() { lista.addItem( "Evento start" ); }

public void destroy() { lista.addItem( "Evento destroy" ); }

public void paint( Graphics g ) { lista.addItem( "Evento paint" ); }

public void update( Graphics g ) { lista.addItem( "Evento update" ); }

public boolean mouseUp( Event evt,int x, int y ) { lista.addItem( "Evento mouseUp en ("+x+","+y+")" ); return false; }

public boolean mouseDown( Event evt,int x, int y ) { lista.addItem( "Evento mouseDown en ("+x+","+y+")" ); return false; }

Page 48: tarea de componentes

public boolean mouseDrag( Event evt,int x, int y ) { lista.addItem( "Evento mouseDrag en ("+x+","+y+")" ); return false; }

public boolean mouseMove( Event evt,int x, int y ) { lista.addItem( "Evento mouseMove en ("+x+","+y+")" ); return false; }

public boolean mouseEnter( Event evt,int x, int y ) { lista.addItem( "Evento mouseEnter en ("+x+","+y+")" ); return false; }

public boolean mouseExit( Event evt,int x, int y ) { lista.addItem( "Evento mouseExit" ); return false; }

public boolean keyDown( Event evt,int x ) { lista.addItem( "Evento keyDown,carácter "+(char)x ); return true; }

public void getFocus() { lista.addItem( "Evento getFocus" ); }

public void gotFocus() { lista.addItem( "Evento gotFocus" ); }

public void lostFocus() { lista.addItem( "Evento lostFocus" ); } }

El applet VisorEventos.java muestra los datos que componen cada evento que se produce. Hemos incorporado un botón y una lista, tal como se puede apreciar en la figura, para poder generar diferentes eventos.

Moviendo el ratón o actuando sobre los dos Componentes, botón y lista, podemos observar los datos que el sistema Java envía en la recolección de esos eventos.

Page 49: tarea de componentes

4.7.3 GENERACION Y PROPAGACION DE EVENTOS

Tomemos el applet, EventosPro.java, que aparece en la figura siguiente. Consta de dos instancias de la clase Button, embebidas dentro de una instancia de la clase Panel. Esta instancia está a su vez embebida dentro de otra instancia de la clase Panel. Esta última instancia de la clase Panel está situada junto a una instancia de la clase TextArea, y ambas están embebidas dentro de una instancia de la clase Applet.

La figura siguiente presenta los elementos que conforman este applet en forma de árbol, con el TextArea y Button como hojas y la instancia de Applet como raiz.

Cuando un usuario interactúa con el applet, el sistema Java crea una instancia de la clase Event y rellena sus datos miembro con la información necesaria para describir la acción. Es en ese momento cuando el sistema Java permite al applet controlar el evento. Este control comienza por el Componente que recibe inicialmente el evento (por ejemplo, el botón que ha sido pulsado) y se desplaza hacia arriba en el árbol de Componentes, componente a componente, hasta que alcanza al Contenedor de la raíz del árbol. Durante este camino, cada Componente tiene oportunidad de ignorar el evento o reaccionar ante él en una (o más) de las forma siguientes:

Modificar los datos miembros de la instancia de Event Entrar en acción y realizar cálculos basados en la información contenida en el

evento Indicar al sistema Java que el evento no debería propagarse más arriba en el

árbol

El sistema Java pasa información del evento a un Componente a través del método handleEvent() del Componente. Todos los métodos handleEvent() deben ser de la forma:

public boolean handleEvent( Event evt )

Un controlador de eventos solamente necesita una información: una referencia a la instancia de la clase Event que contiene la información del evento que se ha producido.

Page 50: tarea de componentes

El valor devuelto por el método handleEvent() es importante. Indica al sistema Java si el evento ha sido o no completamente controlado por el controlador. Un valor true indica que el evento ha sido controlado y que su propagación debe detenerse. Un valor false indica que el evento ha sido ignorado, o que no ha sido controlado en su totalidad y debe continuar su propagación hacia arriba en el árbol de Componentes.

El sistema Java continúa de este mismo modo hasta que el evento es controlado en su totalidad o ya no hay Componentes a los que informar. En la figura siguiente mostramos el camino recorrido por el evento en su intento de que algún Componente lo controle.

Cada Componente del applet añade una línea al objeto TextArea indicando que ha recibido un evento. Luego permite que el evento se propague al siguiente Componente.

El código del controlador de eventos usado en el ejemplo es el que muestran las siguientes líneas:

public boolean handleEvent( Event evt) { if( evt.id == Event.ACTION_EVENT ) ta.appendText( "Panel " + str + " recibe action...\n" ); else if( evt.id == Event.MOUSE_DOWN ) ta.appendText( "Panel " + str + " recibe mouse_down...\n" );

return super.handleEvent( evt ); }

4.7.4 METODOS DE CONTROL DE EVENTOS

El método handleEvent() es un lugar para que el programador pueda insertar código para controlar los eventos. A veces, sin embargo, un Componente solamente estará interesado en eventos de un cierto tipo (por ejemplo, eventos del ratón). En estos casos, el programador puede colocar el código en un método de ayuda, en lugar de colocarlo en el método handleEvent().

No hay métodos de ayuda para ciertos tipos de eventos, aquí está la lista de los que están disponibles para los programadores:

action( Event evt,Object obj ) gotFocus( Event evt,Object obj )

Page 51: tarea de componentes

lostFocus( Event evt,Object obj ) mouseEnter( Event evt,int x,int y ) mouseExit( Event evt,int x,int y ) mouseMove( Event evt,int x,int y ) mouseUp( Event evt,int x,int y ) mouseDown( Event evt,int x,int y ) mouseDrag( Event evt,int x,int y ) keyDown( Event evt,int key ) keyUp( Event evt,int key )

false indicará que el método de ayuda no maneja el evento.

La implementación del método handleEvent() proporcionada por la clase Component invoca a cada método de ayuda. Por esta razón, es importante que las implementaciones redefinidas del método handleEvent() en clases derivadas, siempre finalicen con la sentencia:

return( super.handleEvent( evt ) );

El siguiente trozo de código ilustra esta regla.

public boolean handleEvent( Event evt ) { if( evt.target instanceof MiBoton ) { // Hace algo... return true; }

return( super.handleEvent( evt ) ); }

No seguir esta regla tan simple hará que no se invoquen adecuadamente los métodos de ayuda. El applet EventosRaton.java, que controla los eventos de ratón exclusivamente a través de código insertado en sus métodos de ayuda; va dibujando una línea (rubber band) entre el último punto donde se ha producido un click de ratón y la posición actual del cursor.

ACTION_EVENT

Algunos de los eventos que más frecuentemente tendremos que controlar son los siguientes:

ACTION_EVENTMOUSE_DOWNKEY_PRESSWINDOW_DESTROY

En la documentación de la clase Event se encuentra toda la lista de eventos que cualquier aplicación puede necesitar manejar y su documentación; como ejemplo de uso vamos a detenernos en el primero de ellos, ACTION_EVENT.

Page 52: tarea de componentes

Como ejemplo del manejo de eventos vamos a ver este evento que se provoca al pulsar un botón, seleccionar un menú, etc. Para su control podemos manejarlo en el método handleEvent() o en el método action().

Los dos métodos anteriores pertenecen a la clase Component por lo que todas las clases derivadas de ésta contendrán estos dos métodos y se pueden sobrecargar para que se ajuste su funcionamiento a lo que requiere nuestra aplicación.

Veamos el siguiente ejemplo, en que se controla este evento a través del método handleEvent(), que es el método general de manejo de eventos:

public boolean handleEvent( Event evt ) { switch( evt.id ) { case Event.ACTION_EVENT: // evt.arg contiene la etiqueta del botón pulsado // o el item del menú que se ha seleccionado if( ( "Pulsado "+n+" veces" ).equals( evt.arg ) ) return( true ); default: return( false ); } }

Pero en este caso, cuando se produce este evento se llama al método action(), que sería:

public boolean action( Event evt,Object arg ) { if( ( "Pulsado "+n+" veces" ).equals( arg ) ) return( true ); return( false ); }

Como se puede comprobar, incluso si las etiquetas cambian se puede recibir el evento. Los ejemplos anteriores corresponden al control de un evento producido por un botón que cambia su etiqueta cada vez que se pulsa. Aunque esta no es la única forma de manejar eventos; de hecho se puede hacer:

if( evt.target == miBoton )

en donde se comparan objetos en lugar de etiquetas.

MEJORAR EL DISEÑO DE INTERFACES

La interface de usuario es el aspecto más importante de una aplicación, tal como ya hemos repetido. Un diseño pobre de la interface es un grave problema para que el usuario pueda obtener todo el partido posible de la aplicación. Para ser efectivos, no debemos limitarnos a colocar una serie de botones, etiquetas y barras de desplazamiento sobre la pantalla. Desafortunadamente, nadie ha determinado una reglas correctas para del diseño de una buena interface.

Los diseñadores del AWT parece que se pusieron como meta principal que las clases del AWT funcionasen correctamente, dejando un poco de lado su apariencia. Sin embargo, han proporcionado suficientes mecanismos para poder alterar la apariencia de los Componentes del AWT, muchas veces no de forma sencilla, pero ahí están

Page 53: tarea de componentes

para que los programadores podamos alterar la visualización de los Componentes sobre nuestro interface. Vamos a ver unas cuantas formas sencillas de alterar y dar un mejor aspecto a nuestros diseños.

CAMBIO DE FUENTE DE CARACTERES

El font de caracteres con el que se presenta un texto en pantalla influye mucho en el impacto de una interface. Una interface efectiva no debería utilizar una maraña de fuentes, pero sí que debería utilizar dos o tres diferentes para aumentar el atractivo y la efectividad de los textos. El applet Fuentes.java, tal como se muestra en la figura, ilustra este extremo.

Utiliza tres tipos de fonts de caracteres (en diferente estilo y diferente tamaño) para llamar la atención del usuario sobre las tres zonas de la interface. La fuente por defecto para todos los Componentes es la fuente Dialog. Java proporciona otras fuentes con propósitos más especializados, el número exacto de fuentes depende de la plataforma, por ello, se puede utilizar el applet ListaFuentes.java para obtener una lista de las fuentes de caracteres disponibles en el sistema.

Cuando un programador necesita presentar un Componente en pantalla, como un objeto TextArea, en una fuente de caracteres distinta a la de defecto, la nueva fuente debe seleccionarse mediante el método setFont():

public void setFont( Font f )

El método setFont() espera como parámetro una fuente. En el siguiente trozo de código vemos cómo se usa:

TextArea ta = new TextArea(); Font f = new Font( "Helvetica",Font.ITALIC,12 ); ta.setFont( f );Este código con ligeras modificaciones funcionará para cualquier Componente. Si se cambia la fuente de un Contenedor, todos los Componentes colocados dentro del Contenedor automáticamente adoptarán la nueva fuente de caracteres. El siguiente código, CambioFuentes.java, muestra esta circunstancia:

import java.awt.*;import java.applet.Applet;

public class CambioFuentes extends Applet {

public static void main( String args[] ) { Frame fr = new Frame( "Cambio de Fuentes" ); CambioFuentes cf = new CambioFuentes();

Page 54: tarea de componentes

Font f = new Font( "Helvetica",Font.ITALIC,12 ); fr.setFont( f ); fr.setLayout( new FlowLayout() ); Button b = new Button( "Hola" ); fr.add( b ); Checkbox cb = new Checkbox( "Púlsame" ); fr.add( cb ); TextArea ta = new TextArea(); fr.add( ta );

fr.pack(); fr.show(); } }La fuente de caracteres solamente se indica para el objeto Frame, el botón, la caja y el área de texto también utilizarán esta fuente.

COLORES DE FONDO Y TEXTO

El impacto visual del color nunca debe ser desestimado cuando se ataca el diseño de una interface de usuario. El color tiende a atraer la visión y puede utilizarse para llamr la atención sobre una parte importante del interface. En el ejemplo siguiente, el color rojo alrededor del botón hace que la vista se fije inmediatamente en él.

La clase Component proporciona dos métodos para modificar el color de un Componente. A través de los métodos setBackground() y setForeground(), se pueden indicar los colores del fondo y del texto, respectivamente:

public void setBackground( Color c ) public void setForeground( Color c )

Ambos métodos solamente necesitan un parámetro, un objeto Color. A continuación mostramos un ejemplo de su uso:

TextArea ta = new TextArea(); ta.setBackground( Color.blue ); ta.setForeground( Color.red );

Este código funcionará con ligeras modificaciones para casi todos los Componentes del AWT. Si se cambia el Color de un Contenedor, todos los Componentes colocados dentro de ese Contenedor, automáticamente adoptan el nuevo color. El applet, CambioColor.java, ilustra este punto. El Color solamente se fija para el color de fondo del objeto Frame; el botón, la caja y el área de texto usarán ese mismo color de fondo.

import java.awt.*;import java.applet.Applet;

public class CambioColor extends Applet {

Page 55: tarea de componentes

public static void main( String args[] ) { Frame fr = new Frame( "Cambio de Color" ); CambioColor cc = new CambioColor();

fr.setBackground( Color.red ); fr.setLayout( new FlowLayout() ); Button b = new Button( "Hola" ); fr.add( b ); Checkbox cb = new Checkbox( "Púlsame" ); fr.add( cb ); TextArea ta = new TextArea(); fr.add( ta );

fr.pack(); fr.show(); } }

La calidad de soporte del color varía mucho de una plataforma a otra. Bajo Windows '95, la clase Button ignora totalmente los comandos de color y se empeña en permanecer bajo un patrón de grises. Por otro lado, el fondo de la clase Label parece ser transparente. Algunos Componentes no se presentan en pantalla con un mismo color para un mismo objeto Color. Bajo Windows '95, un fondo de color naranja aparece como naranja en muchos Componentes (excepto en los botones), pero se presenta como amarillo cuando se trata de objetos TextArea o TextField. El soporte del color en Solaris parece ser mucho más consistente.

FIJAR EL TAMAÑO PREFERIDO

Otro ingrediente importante de una buena interface es el tamaño con el cual aparecerá un Componente o Contenedor en la pantalla. En el corazón del control de la composición del interface está el layout manager, que es capaz de fijar el tamaño y la posición de los Componentes que se vayan incorporando al layout que está manejando. Esto, indirectamente, también influye en el tamaño del Contenedor.

En este caso, en lugar de llamar a un método para indicar cuál debe ser el tamaño de un Componente, hay que derivar una nueva clase del Componente y redefinir el método preferredSize() que devolverá el tamaño preferido. El layout manager llama al método preferredSize() para determinar cuál debe ser el tamaño preferido para cada Componente.

Hay que redefinir el método:

public Dimension preferredSize()

Uno puede estar tentado a utilizar el método resize() o el método reshape() para especificar un tamaño, pero no debe hacerse. Ambos métodos son usados directamente por el layout manager, y los ajustes de tamaño se reclacularán la próxima vez que el layout manager tenga que recomponer la posición de los Componentes sobre el Contenedor.

Las siguientes líneas de código demuestran el uso del método preferredSize(), convenientemente redefinido en una clase derivada. Este método crea un nuevo

Page 56: tarea de componentes

objeto Dimension con la altura y anchura especificadas y se lo devuelve a quien lo ha llamado (normalmente el layout manager).

public Dimension preferredSize() { return new Dimension( 200,100 ); }

Desde luego, no hay nada para evitar que el tamaño preferido de un botón varíe dinámicamente, como ocurre en el applet CambioBoton.java, mostrado en la figura:

Este applet contiene dos botones. Pulsando sobre uno de ellos se provoca el cambio de tamaño en el otro. El método preferredSize() del applet de la figura anterior utiliza la variable dim, que es una variable privada:

public Dimension preferredSize() { return new Dimension( dim ); }

La variable dim puede ser fijada o cambiada vía una función miembro como la siguiente:

public void newPreferredSize( Dimension dim ) { this.dim = new Dimension( dim ); resize( dim ); }

El Contenedor debe llamar al método anterior antes de su llamada al método layout() para resituar los Componentes después de que haya cambiado el tamaño de uno de ellos.

USO DE Insets

Insets, o borde interior, al igual que el tamaño preferido, se puede utilizar para proporcionar a la interface de usuario una mayor ordenación espacial. El insets de un Contendor, especifica la cantidad de espacio alrededor del borde interior del Contenedor donde no se situará ningún Componente, estará vacío.

El borde interior se especifica para un Contenedor, redefiniendo su método insets():

public Insets insets()

El método insets() no necesita ningún parámetro y devuelve una instancia del objeto Insets. La clase Insets tiene cuatro campos, que especifican el número de pixels que constituirán el borde interior desde arriba, izquierda, abajo y derecha, respectivamente.

Page 57: tarea de componentes

A continuación mostramos el uso típico de insets(). El código de este ejemplo indica que los Componentes contenidos en el layout deberán estar situados a 5 unidades desde cualquiera de los bordes del Contenedor.

public Insets insets() { return new Insets( 5,5,5,5 ); }

En la figura siguiente se muestra el efecto que provoca en la apariencia del interface el uso de insets().

HABILITAR Y DESHABILITAR COMPONENTES

Los Componentes de una interface de usuario que no están actualmente disponibles pero pueden estarlo, en función de alguna acción del usuario, se dice que están deshabilitados. Se presentan con su rótulo en gris y no responden a las acciones del usuario. El deshabilitar Componentes es mucho mejor que ocultarlos, porque el usuario puede ver las operaciones que puede realizar, aunque en ese momento no las tenga disponibles.

La clase Component proporciona tres métodos para llevar a cabo la habilitación y deshabilitación de Componentes:

public void enable() public void disable() public void enable( boolean condicionBooleana )

Los primeros dos métodos habilitan y deshabilitan un Componente. El tercer método también hace eso dependiendo del valor de un parámetro booleano. Veamos un ejemplo:

Button b = new Button( "Púlsame" ); b.enable(); // o, b.disable(); // o, b.enable( true );

Este código funcionará, con muy pocas modificaciones, sobre cualquier Componente. Para un uso efectivo de estos métodos, un programa debe monitorizar el estado de la interface de usuario. Como el usuario interactúa con la interface, su estado interno cambia. Esto hay que reflejarlo en el estado de los Componentes que pasarán de habilitados a deshabilitados, o viceversa, en función de las circunstancias.

Tomemos como ejemplo ahora el applet que aparece en la figura siguiente, obtenido de la ejecución de Habilitar.java:

Page 58: tarea de componentes

En este applet, los botones Añadir y Borrar están deshabilitados hasta que el usuario haya entrado en el campo de texto. Este previene una activación inadvertida de los dos botones en el caso de que no haya texto en el campo. Tan pronto como se teclee el primer carácter en el campo de texto, se habilitan los dos botones, cada cual asumiendo su propio rol. Si, en cualquier momento, el campo de texto se volviese a quedar vacío, los dos botones volverían a estar deshabilitados

Además de que los Componentes deshabilitados tienen una apariencia visual diferente, tampoco reciben eventos desde el sistema Java; los eventos son inmediatamente propagados al Contenedor en que está situado el Componente deshabilitado.

BOTON GRAFICO

AWT adolece de herramientas ya desarrolladas para implementar interfaces al uso, con alta proliferación de imágenes y facilidades para que el programador utilice directamente Componentes. Sin embargo, sí proporciona las herramientas suficientes como para que se puedan implementar cualquier tipo de Componentes, o modificar al gusto los ya existentes. Para mostrar un ejemplo, vamos a implementar un botón gráfico, BotonGrafico.java

Partimos de un botón normal al cual aplicaremos tres imágenes diferentes, para cada uno de los tres estados en que puede encontrarse: pulsado, liberado e inhabilitado.

En el fichero fuente, podemos comprobar que las tres imágenes se pueden pasar cómo parámetro en la llamada APPLET, por ejemplo:

<APPLET CODE=BotonGrafico.class WIDTH=100 HEIGHT=50><PARAM NAME=IMAGEN0 VALUE=boton0.gif><PARAM NAME=IMAGEN1 VALUE=botonup.gif><PARAM NAME=IMAGEN2 VALUE=botondn.gif></APPLET>

Observando el código, se puede comprobar que una vez cargadas la imágenes, solamente se deben controlar los eventos del ratón, para que automáticamente se presenten las imágenes del botón adecuadas y responda correctamente.

import java.awt.*;import java.applet.Applet;

public class BotonGrafico extends Applet { private MediaTracker tracker; private Image imagen[] = new Image[3]; private boolean BotActivo = false; private boolean BotPulsado = false; private boolean tresImg = false;

Page 59: tarea de componentes

private int Estado = 0;

public void init() { String istr; tracker = new MediaTracker( this );

// Recogemos las tres imágenes de los parámentros de llamada // al applet for( int i=0; i < 3; i++ ) { istr = getParameter( "IMAGEN" + i ); if( istr == null ) tresImg = false; else { // Registramos las imágenes con el Media Tracker imagen[i] = getImage( getCodeBase(),istr ); tracker.addImage( imagen[i],0 ); try { tracker.waitForAll(); } catch( InterruptedException e ) { System.out.println( "Error cargando imagen " + i ); } } } }

public void start() { repaint(); }

public void stop(){ }

// Controlamos la pulsación del ratón public boolean mouseDown( Event evt,int x,int y ) { BotPulsado = true; repaint(); return( true ); }

// Controlamos cuando el usuario suelta el botón del ratón public boolean mouseUp( Event evt,int x,int y ) { if( BotPulsado && BotActivo ) { BotPulsado = true; repaint(); } else { BotPulsado = false; repaint(); } return( true );

Page 60: tarea de componentes

} // Controlamos cuando el cursor del ratón entra en el // campo de acción del applet // Presentamos un mensaje en la línea de estado public boolean mouseEnter( Event evt,int x,int y ) { BotActivo = true; showStatus( "Tutorial de Java, Boton Grafico" ); repaint(); return( true ); }

// Controlamos cuando el cursor del ratón abandona el // lugar ocupado por el applet public boolean mouseExit( Event evt,int x,int y ) { BotActivo = false; showStatus( "" ); repaint(); return( true ); }

public void update( Graphics g ) { // Controlamos el estado en que se queda el botón // tras la acción que se haya hecho con el ratón if( !BotActivo ) Estado = 0; else if( BotActivo && !BotPulsado ) Estado = 1; else Estado = 2; paint( g ); }

public void paint( Graphics g ) { g.drawImage( imagen[Estado],0,0,this ); } }

4.8 LIBRERÍAS JAVA

Algunas librerías usadas en AWT en java son:

1. import java.awt.Graphics;Permite dibujar figuras geométricas.

2. import java.awt.Font;Font es la clase que define los caracteres. Define las fuentes que se pueden trabajar con el texto.

3. import java.awt.event.ItemEvent;Cambia de estado un ítem. Cuando se selecciona una casilla de verificación o una lista de ítems.

Page 61: tarea de componentes

4. import java.awt.event.AdjustmentEvent;Ajuste de un valor. Mover una barra de desplazamiento

5. java.awtEl paquete Abstract Windowing Toolkit (awt) con tiene clases para generar widgets y componentes GUI (Interfaz Gráfico de Usuario).Incluye las clases Button, Checkbox, Choice, Component, Graphics, Menu, Panel, TextArea y TextField.

6. import java.awt.BorderLayout;El controlador de posicionamiento de Componentes. La composición BorderLayout (de borde) proporciona un esquema más complejo de colocación de los Componentes en un panel. Controlador de posicionamiento, con separaciones tanto vertical como horizontal.

7. import java.awt.event.AdjustmentListe ner;La clase Listener presenta las coordenadas del cursor sobre el mismo objeto sobre el cual se ha registrado.

8. import java.awt.event.ItemListener;Cambia el estado de las coordenadas de un item.

9. import java.awt.GridLayout;Proporciona gran flexibilidad para situar Componentes. El layout se crea con un número de filas y columnas y los Componentes van dentro de las celdas de la tabla definida.

10. import java.awt.FlowLayout;El controlador de posicionamiento de Componentes sobre un objeto Panel. Los Componentes añadidos a un Panel con FlowLayout se encadenan en forma de lista. La cadena es horizontal, de izquierda a derecha, y se puede seleccionar el espaciado entre cada Componente.

11. import java.awt.Scrollbar;Es el componente que perm ite trabajar con las barras de desplazamiento. Las barras de desplazamiento se utilizan para permitir realizar ajustes de valores lineales en pantalla, porporcionan una forma de trabajar con rangos de valores o de áreas, como en el caso de un área de texto en donde se proporcionan las barras de desplazamiento de forma automática.

12. import java.awt.Button;Botones de pulsación.

13. import java.awt.Checkbox;Permite los botones de comprobación, marcación.

14. import java.awt.Choice;Botones de selección.

15. import java.awt.Label;Permite trabajar con etiquetas. Una etiqueta (Label) proporciona una forma de colocar texto estático en un panel, para mostrar información fija, que no varía (normalmente), al usuario. La clase Label dispone de varias constantes que permiten especificar la alineación del texto sobre el objeto Label.

Page 62: tarea de componentes

16. import java.awt.Panel;Define los contenedores. La clase Panel es un Contenedor genérico deComponentes. Una instancia de la clase Panel, simplementeProporciona un Contenedor al que ir añadiendo Componentes.

17. import java.awt.Button;Los botones de pulsación. La clase Button es una clase que produceUn componente de tipo botón con un título.

18. import java.awt.Graphics;Preserva la posibilidad de seguir pintando en pantalla.

Bibliografía.

http://sunsite.dcc.uchile.cl/java/docs/JavaTut/Intro/tabla.html#awt

http://yosh35.tripod.com/id5.html

http://www.htmlpoint.com/guidajava/java_21.htm