Java a través de la ventana

Para hacer algo un poco más divertido, vamos a empezar a trabajar con la biblioteca java.awt, que es la que contiene todo un grupo de objetos para trabajar con ventanas y sus contenidos: botones, listas, etc.

Nuestra primera ventana

En Java, la clase Window (descendiente de Container), en la biblioteca java.awt, permite implementar ventanas "peladas", es decir, sin bordes ni menús. Son la base para cualquier tipo de ventanas (normales, pop-up, diálogos, etc.). El otro descendiente de Container, Panel, es más sencillo aún y sirve como espacio para que una aplicación incorpore dentro suyo otros elementos (incluyendo otros paneles).

La interface Java dirige tanto a uno como a otro todos los eventos de teclado, mouse y foco que los afecten (en seguida veremos cómo usar estos eventos).

De la clase Window descienden Dialog (para implementar diálogos) y Frame, que es una ventana algo más completa: ya tiene borde y menú, así como los botones de cerrar, maximizar, etc.

El siguiente ejemplo crea una ventana que no hace nada pero contiene varios elementos; se puede usar directamente (desde la ventana DOS o Unix con java Ejemplo7) o como applet dentro de una página HTML.

Si bien los elementos no disparan ninguna acción, se pueden utilizar con toda su funcionalidad (por ejemplo, editar el texto dentro de los cuadros de texto o presionar el botón).

// grabar como "Ejemplo7.java"
// compilar con "javac Ejemplo7.java"
import java.awt.*;

public class Ejemplo7 extends Frame {
    boolean inAnApplet = true;

    public static void main(String args[]) {
        Ejemplo7 window = new Ejemplo7();
        window.inAnApplet = false;
        window.setTitle("Ejemplo");
        window.pack();
        window.show();
    }

    public Ejemplo7() {
	Panel panelAlto = new Panel();
	panelAlto.add("West", new Label("Cartel", Label.CENTER));
	panelAlto.add("East", new TextArea("Area de texto", 5, 20));
	add("North", panelAlto);

	Panel panelBajo = new Panel();
	panelBajo.add(new TextField("Campo de Texto"));
	panelBajo.add(new Button("Botón"));
	add("South",panelBajo);
    }

    public boolean handleEvent(Event ev) {
        if (ev.id == Event.WINDOW_DESTROY) {
            if (inAnApplet) {
                dispose();
            } else {
                System.exit(0);
            }
        }
        return super.handleEvent(ev);
    }
}


Un poco de detalle

La clase desciende de Frame (o sea que será una ventana con borde, aunque no le vamos a poner menú).

Vamos a usar el flag inAnApplet para saber si se arrancó como applet o como aplicación standalone (hay que cerrarla en manera diferente en cada caso)

public class Ejemplo7 extends Frame {
    boolean inAnApplet = true;

Si se llama como aplicación standalone, lo primero que se ejecuta es main(...); en este caso la aplicación crea una instancia de Ejemplo7 (ejecutando el constructor Ejemplo7() a través de new), define que no es un applet, y llama a tres métodos de la "abuela" window:

setTitle que define cuál va a ser el título que aparece en la ventana
pack que dimensiona los elementos que componen la ventana a su tamaño preferido
show que muestra la ventana
public static void main(String args[]) {
        Ejemplo7 window = new Ejemplo7();
        window.inAnApplet = false;
        window.setTitle("Ejemplo");
        window.pack();
        window.show();
    }


Ojo! No confundir el objeto (instancia) window con la clase Window!

Si se carga como applet, entonces se ejecuta el constructor Ejemplo7() como en el caso anterior:

public Ejemplo7() {
	Panel panelAlto = new Panel();
	panelAlto.add("West", new Label("Cartel", Label.CENTER));
	panelAlto.add("East", new TextArea("Area de texto", 5, 20));
	add("North", panelAlto);

	Panel panelBajo = new Panel();
	panelBajo.add(new TextField("Campo de Texto"));
	panelBajo.add(new Button("Botón"));
	add("South",panelBajo);
    }


Este constructor define dos paneles que forman el contenido de la ventana (panelAlto y panelBajo), los llena con un par de componentes y los pone dentro de la ventana (recordar que Ejemplo7 es una ventana!).

Para verlo más claro, se crea el panel (o espacio para contener objetos) con:

	Panel panelAlto = new Panel();


Se agregan componentes al panel con el método add:

	panelAlto.add("West", new Label("Cartel", Label.CENTER));
	panelAlto.add("East", new TextArea("Area de texto", 5, 20));



Se agregan el panel dentro de nuestro objeto con:

	add("North", panelAlto);

que equivale a:

	this.add("North", panelAlto);

lo que se puede ver (aunque es inválido porque la clase no es static) como:

	Ejemplo7.add("North", panelAlto);

Como nuestra clase Ejemplo7 desciende de Frame, ésta de Window, y ésta de Container, el método add lo está heredando de... su bisabuela! Por otra parte, Panel es hija de Container, y usa el mismo método para agregar sus componentes. Interesante, no? Veamos la estructura:

Object --- Component --- Container --+-- Panel
                                     |
                                     +-- Window --- Frame --- Ejemplo7

Noten que hemos usado dos métodos add con diferente signature:

	panelAlto.add("West", new Label("Cartel", Label.CENTER));
	..........
	panelBajo.add(new Button("Botón"));


El método add(Component) agrega un componente al final; el método add(String,Component) lo agrega en un lugar especificado por una palabra que depende del LayoutManager, el objeto que se encarga de ordenar los componentes dentro del contenedor.

LayoutManager es una interface, y como tal debe implementarse a través de objetos no abstractos de los que hay varios predefinidos en la librería java.awt: BorderLayout, CardLayout, FlowLayout, GridBagLayout y GridLayout.

El Layout por defecto es BorderLayout, que define en el contenedor las áreas "North", "South", "West", "East" y "Center" y es que usamos aquí. CardLayout permite "apilar" los componentes como cartas y ver uno por vez, FlowLayout los ordena de izquierda a derecha como un texto, GridLayout los ordena en una cuadrícula donde cada componente tiene un tamaño fijo y GridBagLayout los pone en una cuadrícula pero cada uno puede tener el tamaño deseado.

Noten que no hace falta llamar, en el caso del applet, a Pack() y Show().

Y los eventos...

Ahora vamos a ver un método que viene de la clase tatarabuela! Hace falta decir que me gusta esto de los objetos?

Vamos a redefinir handleEvent(Event), que es el método que analiza los eventos dirigidos al componente y toma las acciones adecuadas.

La clase Event define básicamente una serie de métodos que permiten saber si hay alguna tecla de control presionada y muchas constantes que indican si se presionó o movió el mouse, si se presionó alguna tecla en particular, si se cambió el tamaño de la ventana, etc. En particular hay algunos atributos interesantes:

id que indica el tipo de evento
target que indica sobre qué componente se produjo el evento
key qué tecla se presionó si fue un evento de teclado

etc.

En los descendientes de Component, el método handleEvent se llama automáticamente cada vez que se produce un evento sobre el componente. En este caso, simplemente vamos a mirar si el evento (sobre nuestro objeto de clase Ejemplo7) fue "cerrar la ventana", que se identifica mediante event.id = WINDOW_DESTROY (una constante estática de la clase Event, y como tal la podemos usar con el nombre de la clase como Event.WINDOW_DESTROY):

public boolean handleEvent(Event ev) {
        if (ev.id == Event.WINDOW_DESTROY) {
            if (inAnApplet) {
                dispose();
            } else {
                System.exit(0);
            }
        }
        return super.handleEvent(event);
    }

En ese caso, si nuestro ejemplo se disparó como aplicación llamamos al método System.exit(0), que cierra la aplicación; y si era un applet llamamos a dispose(), implementación de un método de la interface ComponentPeer que se encarga de remover todos los componentes y la propia ventana.

Noten que cualquier otro tipo de evento deja seguir hasta return super.handleEvent(event), que llama al método handleEvent de la clase madre: así como el prefijo this. se refiere a un método de la propia clase, el prefijo super. llama al método de la clase madre (aunque esté redefinido). En este caso, la llamada se remonta hasta Component.handleEvent, que determina el tipo de evento y llama a uno de los métodos action, gotFocus, lostFocus, keyDown, keyUp, mouseEnter, mouseExit, mouseMove, mouseDrag, mouseDown o mouseUp según sea apropiado (y devuelve true). Si ningún método es aplicable, devuelve false.

Es muy común, al redefinir un método, tener en cuenta llamar antes o después al método de la clase antecesora para inicializar o terminar alguna tarea.

Bueno, hasta aquí hay para jugar un poco. Pronto vamos a ampliar este método para procesar todos los componentes que pusimos dentro de la ventana. Jueguen mientras tanto que pronto nos vemos!

<> Página de tutoriales <> Página Anterior <> Siguiente Capitulo <>