La función paint()


Hemos visto brevemente como actúa el applet ante los eventos básicos del sistema. El inconveniente de nuestro applet es que los métodos estándares están vacios, no hacen nada. Por ello, si abrimos la página HTML veremos un applet vacio.Vamos a introducir algo de miga en nuestro ejemplo:



	public void paint( Graphics g ) {
		Date d = new Date();
		g.drawString( "Hola, la hora actual es ...", 10, 25 );
		g.drawString( d.toString(), 10, 50 );
	}

Paint1

Para usar Date debe importar "java.util.Date". Hasta ahora hemos visto una serie de métodos básicos (init, etc.) que se ejecutan como consecuencia de eventos. En este ejemplo vemos otro método importante, aquel que es llamado cuando la máquina virtual Java le pide al applet que se redibuje: paint(). Hay eventos que implican una llamada al applet para su redibujo: el más común es la aparición del applet en primer plano después de permanecer minimizado. Otro evento menos frecuente es el cambio de tamaño del navegador que afecta a la zona del applet. La máquina virtual (JVM) capta el evento y llama al método paint(). Se pueden generar muchos eventos que impliquen un repintado, en estas ocasiones la JVM los trata de agrupar, con lo que puede haber más eventos de repintado que llamadas a paint(). Esto no es inadecuado, es la forma que tiene el sistema Java de optimizar para evitar excesivas llamadas a paint(). Una explicación de mayor profundidad sobre paint() exigiría hablar de Threads, pero esto queda para otro momento.

paint() debe ser público, como el resto de métodos estándar, ya que es llamado directamente por JVM.

El parámetro del paint() es un objeto de la clase Graphics. Observa que todo lo que pintamos se hace a través de llamadas a drawString(), un método de la clase Graphics. ¿Qué es Graphics? Dicho de forma técnica, es un contexto gráfico independiente del dispositivo (si ha programado en C/C++ sobre el API de Windows esto le tiene que resultar familiar). Pero como estamos en una introducción vamos a tratar de explicarlo de forma sencilla: lo que recibe paint() es el "lienzo" o "papel" sobre el que dibujaremos. Mediante llamadas a este contexto pintamos multitud de cosas: texto, líneas, figuras, imágenes, etc. El segundo y tercer argumento de drawString() representa la posición (x,y) en pixels (en puntos de pantalla) que tiene su origen (0,0) en la esquina superior izquierda.

Vamos a aprovechar que podemos pintar en el applet para observar como se comportan los eventos. En el siguiente ejemplo registramos en una lista de String cada evento que llega al applet. ¿Cómo se hace este registro de eventos? Los métodos init(), start() y run() registran cada uno un String que representa al evento en la lista de Strings. Cuando el applet se debe repintar (llamada a paint()) el applet lista todos los eventos recibidos y ordenados cronológicamente. De esta forma comprobamos los eventos que se producen y el orden en que se producen:



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

public class mi_applet extends Applet {
    String lista[] = new String[10];   // Vector donde registro cadenas
    int contador_ev = 0;               // Contador de eventos

    /***** Crear el applet ****/
    public mi_applet() { }

    /***** Inicializar el applet ********/
    public void init() {
		try { jbInit(); }
		catch(Exception e) { e.printStackTrace(); }
    }

    /***** Registro el evento init en la lista ******/
    private void jbInit() throws Exception {
		if ( contador_ev < lista.length ) {
			lista[contador_ev] = new String("Init. Ev " + String.valueOf(contador_ev));
			contador_ev++;
		}
	}

    /***** Registro el evento start en la lista ******/
    public void start() {
		if ( contador_ev < lista.length ) {
			lista[contador_ev] = new String("Start. Ev " + String.valueOf(contador_ev));
			contador_ev++;
		}
    }

    /***** Registro el evento stop en la lista ******/
    public void stop() {
		if ( contador_ev < lista.length ) {
			lista[contador_ev] = new String("Stop. Ev " + String.valueOf(contador_ev));
			contador_ev++;
		}
    }

    /***** No registro evento Destroy, ya que se destruye el applet *****/
    public void destroy() { }

    /***** Registro el evento paint e imprimo la lista de eventos ******/
    public void paint( Graphics g ) {
		if ( contador_ev < lista.length ) {
			lista[contador_ev] = new String("Paint. Ev " + String.valueOf(contador_ev));
			contador_ev++;
		}
		g.drawString( "Lista ordenada cronológicamente de eventos:", 10, 25);
		for (int i = 0; i < contador_ev; i++ )
			g.drawString( lista[i], 10, 50+(i*25));
    }
}   // Fin de clase mi_applet

Vea lo que ocurre cuando realizamos las siguientes acciones:

  1. Abrimos la página: desencadena init, start y paint
  2. Minimizamos (iconizamos) la página:desencadena stop
  3. Maximizamos la página: desencadena start y paint

Paint3

Hay que destacar que el ejemplo se ha realizado con el AppletViewer. Pero con determinadas configuraciones y versiones de navegadores, como Explorer, la minimización no implica stop (pero si paint) y la maximización no implica start (pero ésta última si implica paint).

Hay dos eventos muy frecuentes que generan secuencias de llamadas en el applet:

El programador puede provocar una llamada a paint(), utilizando repaint(). Esta función pone una petición más en la cola de peticiones de repintado. Recuerda que es el JVM el que gestiona esta cola y trata de optimizarla: en ocasiones agrupa peticiones y no siempre puedes esperar que una llamada a repaint() produzca un repintado inmediato.

En el siguiente ejemplo se utiliza repaint() para que el reloj nos de la hora cada segundo (durante un periodo aproximado de quince segundos):



	public class mi_applet extends Applet {
		int x = 10;
		...

		public void paint( Graphics g ) {
			Date d = new Date();
			g.drawString( "Hola, la hora actual es ...", x, 25 );
			g.drawString(d.toString(), x, 50);
			System.out.println( "Redibujo. x="+x );

			if ( x < 150 ) {
				try { Thread.sleep(1000); } catch (InterruptedException e) { }
				repaint();
			}
			x += 10;
		}
	}   // Fin de clase mi_applet

Notas al ejemplo:

Al final se vería algo así:

Paint2

Una última nota: los ejemplos de visualización que ve en esta página se realizan con AppletViewer. Cuando los quiera ver con su navegador tenga en cuenta que estos programas tienen un sistema de caché (archivos históricos) que puede llevarle a engaño: por ejemplo, supongamos que no ha modificado la página HTML pero si ha modificado el applet, a continuación, con la finalidad de visualizar la nueva versión, hace doble clic en el archivo HTML que hemos llamado mi_applet.html (puede realizar esta operación desde el explorador de windows, por ejemplo). Si en su navegador ve la nueva versión, estupendo; pero lo más probable es que su navegador este configurado de tal forma que este viendo la antigua versión del applet. ¿Por qué? El navegador ha guardado en un registro histórico la página y el antiguo applet. El usuario le vuelve a pedir la apertura de la misma página, pero el navegador con la finalidad de optimizar (aligerar) la descarga le abre la página y el applet que tenía guardados en el registro histórico. Solución sencilla para ver la nueva versión del applet: pulse la opción de "Actualizar" o "Refrescar" que tiene su navegador. Con AppletViewer no ocurre esto porque es de una sencillez aplastante: no tiene caché y sólo lee las etiquetas HTML imprescindibles para ejecutar el applet.


Volver al índice