Una ventana con vida

Antes que nada, vamos a crear una página HTML para cargar nuestra clase Ejemplo8, que será un applet (aunque también la podremos ejecutar en forma standalone con "java Ejemplo8"), por ejemplo:

<!-- Archivo Ejemplo8.htm - HTML de ejemplo -->
<HTML>
<HEAD>
<TITLE>Ejemplo 8 - Ventana de datos</TITLE>
</HEAD>
<BODY>
Aqu&iacute; se tiene que abrir una ventana de entrada de datos
<applet code="Ejemplo8.class" width=170 height=150>
</applet>
</BODY>
</HTML>


Nuestro applet será muy sencillo, ya que utilizará clases que iremos definiendo en este capítulo; por empezar sólo creará una ventana que definiremos en la clase Ventana8:

// Archivo: Ejemplo8.java
// Compilar con "javac Ejemplo8.java"

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

public class Ejemplo8 extends Applet {

public static void main (String arg[]) {	// para poder llamarla con "java Ejemplo8"
new Ventana8("Ejemplo Standalone", true);
}

public void init() {			// se ejecuta al abrirse un applet
new Ventana8("Ejemplo Applet", false);
}

}


Con los parámetros que le pasamos a la clase Ventana8 le indicamos el título de la ventana y si se carga como applet o no (ya que el método de cierre varía).

Viajando con Java

Ahora vamos a trabajar con nuestra clase Ventana8, una ventana que nos permita seleccionar una fecha y dos ciudades (desde y hasta) que simula una ventana de compra de pasajes de, por ejemplo, una terminal de ómnibus.

El ejemplo está basado en uno del libro "Programación Java" de Macary y Nicolas, aunque algo mejorado y ampliado.

En nuestra ventana podremos entrar una fecha a mano o directamente mediante los botones Hoy y Mañana, elegiremos la ciudad de salida y la de llegada de dos listas, y presionaremos luego un botón que nos mostrará los servicios disponibles, nos permitirá comprar los pasajes, etc.

A medida que entramos los datos, en el botón se irá mostrando el detalle de lo que se fue seleccionando.

Nuestra ventana quedará más o menos así:


Empecemos por armar la estructura de la clase Ventana8:

import java.awt.*;

class Ventana8 extends Frame {	// hija de Frame

// aquí agregaremos luego
// algunas variables para guardar datos
// (ciudades de salida y llegada, fecha)
button		ok;		// también el botón de compra de pasajes
boolean	enApplet;	// y otra para indicar si es un applet o no

Ventana8 (String titulo, boolean enApplet) {	// un constructor
super(titulo);				// llama al de Frame
this.enApplet = enApplet;		// guardamos esto
// aquí crearemos los botones, listas, etc
// con sus valores iniciales
// y los pondremos en la ventana.
// por ejemplo:
ok = new Button("Viaje: de ? a ? el ?/?/?");
add("South",ok);
pack();					// dimensionamos la ventana
show();					// y la mostramos!
}

public boolean handleEvent(Event e) {		// para manejar los eventos
if (e.id == Event.WINDOW_DESTROY) {	// cerrar la ventana
if (enApplet) dispose();
else System.exit(0);
}
// aquí miraremos si se presionó un botón
// o se eligió algo de una lista
// y actuaremos en consecuencia
return super.handleEvent(e);	// los demás eventos los maneja Frame
}

void ActualizaBoton() {
	// aquí pondremos un método que servirá
	// para actualizar el botón de compra de pasajes,
	// ya que el texto del mismo se actualiza cada
	// vez que se selecciona una ciudad o se cambia la fecha
}

void Activar() {
	// y aquí un método para cuando se presione
	// dicho botón, que se supone que va a consultar
	// una base de datos y abrir una ventana
// para vendernos el pasaje
}
}


Nuestro programa ya funciona! Aunque un poquito incompleto, claro...

Igual vamos a analizarlo un poco el constructor, que es lo más interesante aquí.

Primero llamamos al constructor de la clase madre, que se encarga de crear la ventana:

Ventana8 (String titulo, boolean enApplet) {	// un constructor
super(titulo);				// llama al de Frame


Esto sería como llamar a super.Frame(titulo), o bien Frame(titulo), ya que el método constructor tiene el mismo nombre de la clase. Luego, con:

this.enApplet = enApplet;		// guardamos esto


asignamos a nuestra variable enApplet de la clase el valor del parámetro que se pasó al constructor, que se llama igual. El prefijo this, que se refiere a la instancia particular de la clase, permite diferenciar uno de otro (esto es válido tanto para variables como para métodos).

ok = new Button("Viaje: de ? a ? el ?/?/?");
add("South",ok);


Aquí hemos creado un botón ubicado al pie de la ventana (por ahora lo único que pusimos), y luego dimensionamos la ventana y la mostramos:

pack();			// dimensionamos la ventana
show();			// y la mostramos!


Preparando listas

Ahora vamos a empezar a crear otros objetos para ir completando nuestra aplicación. Comencemos con las listas de ciudades.

Para eso, vamos a crear un objeto descendiente de Panel que simplemente contenga una lista de ciudades predefinidas y un título que diga "Seleccione ciudad de", y a continuación "salida" o "llegada".

También agregaremos un método

import java.awt.*;

class SelecPueblo extends Panel {
private List	listaPueblos;

SelecPueblo (String salidaOllegada) {
setLayout (new BorderLayout (20,20));

// armamos el título, que va a ser un Label:
StringBuffer titulo = new StringBuffer();
titulo.append("Seleccione ciudad de ");
titulo.append(salidaOllegada);
titulo.append(": ");
add("North", new Label(titulo.toString()));

// armamos la lista de ciudades, que va a ser un List:
listaPueblos = new List (4, false);
listaPueblos.addItem("Buenos Aires");
listaPueblos.addItem("La Plata");
listaPueblos.addItem("Azul");
listaPueblos.addItem("Rosario");
listaPueblos.addItem("Cordoba");
listaPueblos.addItem("Bahía Blanca");
add("South", listaPueblos);
}

public String getDescription() {
return listaPueblos.getSelectedItem();
}
}


No hay mucho para analizar aquí, creo. La variable listaPueblos es privada, pero puede consultarse cuál es la ciudad seleccionada mediante getDescription (que es public). Este método llama al método getSelectedItem de la lista, que devuelve el texto seleccionado.

En el constructor, armamos el texto del título como un StringBuffer. Los objetos StringBuffer son similares a los de clase String pero pueden ser modificados. En cambio los objetos String, una vez creados, no pueden ser modificados directamente: sus métodos (concat, toLowerCase, etc.) simplemente crean un nuevo String con el nuevo valor.

Esto lo hicimos para introducir esta nueva clase; por supuesto hubiera sido más fácil poner, como pueden comprobar, con el mismo resultado:

String tit = "Seleccione ciudad de "+salidaOllegada+": ";
add("North", new Label(tit));


Por otra parte, creamos el objeto listaPueblos como new List(4, false), que indica que la lista va a tener 4 renglones y sólo se puede seleccionar un ítem por vez. Agregamos luego 6 ítems mediante addItem y la agregamos al panel.

Ahora ya podemos agregar las listas a nuestra ventana y poner un par de variables para guardarlas:

class Ventana8 extends Frame {	// hija de Frame

SelecPueblo	cs;		// ciudad de salida
SelecPueblo	cl;		// ciudad de llegada
button		ok;		// también el botón de compra de pasajes
boolean	enApplet;	// y otra para indicar si es un applet o no

Ventana8 (String titulo, boolean enApplet) {	// un constructor
super(titulo);				// llama al de Frame
this.enApplet = enApplet;		// guardamos esto
cs = new SelecPueblo("SALIDA");	// CIUDAD DE SALIDA
add ("Center", cs);			
cl = new SelecPueblo("LLEGADA");	// CIUDAD DE LLEGADA
add ("East", cl);
ok = new Button("Viaje: de ? a ? el ?/?/?");
add("South",ok);
pack();					// dimensionamos la ventana
show();					// y la mostramos!
}

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

Ya pueden ir probando cómo queda, aunque por ahora mucha funcionalidad no tenemos...

Agregando fechas

Otro panel más nos servirá para seleccionar o entrar la fecha:

import java.util.*;
import java.awt.*;

class DiaPartida extends Panel {
private TextField	elDia;
private Button		hoy;
private Button		diasiguiente;

DiaPartida() {
setLayout (new GridLayout (4,1));
elDia = new TextField();
elDia.setText(GetHoy());
hoy = new Button ("Hoy");
diasiguiente = new Button ("Mañana");
add (new Label ("Día salida: "));
add (elDia);
add (hoy);
add (diasiguiente);
}

private String GetHoy() {
Date d = new Date();
int dia = d.getDate();
int mes = d.getMonth();
int ano = d.getYear();
return dia+"/"+mes+"/"+ano;
}

private String GetManana() {
Date d = new Date();
int dia = d.getDate();
int mes = d.getMonth();
int ano = d.getYear();
dia = dia++;
switch (mes) {
case (1):
	case (3):
	case (5):
	case (7):
	case (8):
	case (10): if (dia>31) {
			dia = 1;
					mes++;
}
		break;
	case (12): if (dia>31) {
					dia = 1;
			mes = 1;
					ano++;
				}
				break;
case (4):
	case (6):
	case (9):
	case (11): if (dia>30) {
					dia = 1;
			mes++;
				}
				break;
	default: if (dia>28) {	// ojo, hay que corregir para bisiestos!
					dia = 1;
			mes++;
				}
}
return dia+"/"+mes+"/"+ano;
}

public String getDescription() {
return elDia.getText();
}

public boolean handleEvent (Event e) {
if (e.target == hoy)
elDia.setText(GetHoy());
if (e.target == diasiguiente)
elDia.setText(GetManana());
return super.handleEvent(e);
}

}


Este es un poco más largo pero no más complejo. Vamos por parte:

DiaPartida() {
setLayout (new GridLayout (4,1));
elDia = new TextField();
elDia.setText(GetHoy());
hoy = new Button ("Hoy");
diasiguiente = new Button ("Mañana");
add (new Label ("Día salida: "));
add (elDia);
add (hoy);
add (diasiguiente);
}


El constructor crea un panel con cuatro campos en forma de grilla vertical, donde mostrará el texto "Día salida: ", el campo de entrada de texto elDia y los botones hoy y diasiguiente.

El método privado getHoy usa los métodos getDate, getMonth y getYear de la clase date para armar un String con la fecha actual. El método privado getManana hace lo mismo para leer la fecha actual, y le suma 1 al día para tener el día siguiente. El switch siguiente verifica que si pasó de fin de mes tome el primer día y el mes siguiente (o el primer día del año siguiente si es en diciembre). Notar que no se consideraron los años bisiestos en febrero para no complicar el método, pero no es difícil de corregir.

Otra manera sería armar un array con los días de cada mes, corregir los días de febrero para los años bisiestos, y comparar contra este array en lugar de usar un switch. La idea siempre es la misma: devolver un String con la fecha del día siguiente.

Notar algo interesante: como estas clases se cargan y ejecutan en la máquina cliente, la fecha que aparece es la del cliente y no la del servidor (que puede ser diferente depende la hora y el lugar del mundo en que estén ambas máquinas).

El método getDescription es público y se usa para acceder a la fecha que se ha ingresado desde las demás clases; simplemente devuelve el contenido del campo elDia, de clase TextField.

Aquí hemos desarrollado también el método handleEvent:

public boolean handleEvent (Event e) {
if (e.target == hoy)
elDia.setText(GetHoy());
if (e.target == diasiguiente)
elDia.setText(GetManana());
return super.handleEvent(e);
}


En caso de alguna acción sobre uno de los botones, el método setText (de la clase TextField) pone en el campo de texto elDia el valor del día actual o el siguiente.

Notar que sólo hemos considerado que haya algún evento y no un tipo de evento en particular; en realidad el método va a actuar por ejemplo tanto al presionar el mouse sobre el botón como al soltarlo. Pero esto no nos molesta.

super.handleEvent se encarga de otros eventos dirigidos al panel, como la entrada de datos por teclado al campo de texto por ejemplo.

Juntando todo hasta aquí

Bueno, ahora vamos a reunir las piezas que tenemos hasta ahora agregando estos métodos a nuestra clase Ventana8 para ver cómo queda la ventana completa:

class Ventana8 extends Frame {	// hija de Frame

SelecPueblo	cs;		// ciudad de salida
SelecPueblo	cl;		// ciudad de llegada
DiaPartida	dp;		// día de salida
button		ok;		// botón de compra de pasajes
boolean	enApplet;	// para indicar si es un applet o no

Ventana8 (String titulo, boolean enApplet) {	// un constructor
super(titulo);				// llama al de Frame
this.enApplet = enApplet;		// guardamos esto
dp = new DiaPartida();			// DIA DE SALIDA
add ("West", dp);
cs = new SelecPueblo("SALIDA");	// CIUDAD DE SALIDA
add ("Center", cs);			
cl = new SelecPueblo("LLEGADA");	// CIUDAD DE LLEGADA
add ("East", cl);
ok = new Button("Viaje: de ? a ? el ?/?/?");
add("South",ok);
pack();					// dimensionamos la ventana
show();					// y la mostramos!
}

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

Nos detenemos aquí por ahora; compilen y prueben las clases y en la próxima agregaremos a Ventana8 métodos para procesar el texto del botón de compra y para actuar cuando se lo presiona.

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