DibuJava

Además de los componentes estándar (botones, listas, etc.), hay un componente para dibujo "libre" que nos permite implementar cualquier otro tipo de control: la clase Canvas. Típicamente se usa para dibujar, y corresponde a una zona rectangular dentro de una ventana.

La clase en sí no hace prácticamente nada; el programador debe definir una subclase de Canvas a la que el AWT le envía todos los eventos de mouse y teclado. Redefiniendo los métodos gotFocus, lostFocus, keyDown, keyUp, mouseEnter, mouseExit, mouseMove, mouseDrag, mouseDown y mouseUp, el programador puede hacer lo que se le ocurra dentro de ese rectángulo. Vamos a hacer uso de un Canvas para generar un applet donde habrá una zona rectangular dentro de la que, haciendo click con el mouse y moviéndolo sin soltar el botón, dibujaremos un rectángulo dinámicamente.

Esto nos permitirá ver cómo usar un Canvas para dibujar, capturar eventos, etc. El borde tiembla un poco al redibujar, pero ya veremos cómo evitar eso.

Canvas en acción

Primero vamos a poner, como ya se está haciendo costumbre, el código del applet (Recordar que debe cargarse desde una página html para verlo! Aquí no creamos ninguna ventana y no podremos verlo como aplicación standalone) y luego intentaremos explicar cómo funciona.

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

public class Ejemplo15 extends Applet {
    public void init() {
	Label label = new Label("Pique y arrastre con el mouse!");
	miCanvas zonaDib = new miCanvas();
	zonaDib.resize(new Dimension (200,200));
	add("North", label);
	add("Center", zonaDib);
	resize(300,250);
   }
}

class miCanvas extends Canvas {
    Rectangle rectActual;

    public boolean mouseDown(Event e, int x, int y) {
	rectActual = new Rectangle(x, y, 0, 0);
	repaint();
	return false;
    }

    public boolean mouseDrag(Event e, int x, int y) {
	rectActual.resize(x-rectActual.x, y-rectActual.y);
	repaint();
	return false;
    }

    public boolean mouseUp(Event e, int x, int y) {
	rectActual.resize(x-rectActual.x, y-rectActual.y);
	repaint();
	return false;
    }

    public void paint(Graphics g) {
	Dimension d = size();
	g.setColor(Color.red);
	g.drawRect(0, 0, d.width-1, d.height-1);
	g.setColor(Color.blue);
	if (rectActual != null) {
		Rectangle box = cortarRect(rectActual, d);
		g.drawRect(box.x, box.y, box.width-1, box.height-1);
	}
    }

    Rectangle cortarRect(Rectangle miRect, Dimension areaDib) {
        int x = miRect.x;
        int y = miRect.y;
        int ancho = miRect.width;
        int alto = miRect.height;

        if (ancho < 0) {
            ancho = -ancho;
            x = x - ancho + 1;
            if (x < 0) {
                ancho += x;
                x = 0;
            }
        }
        if (alto < 0) {
            alto = -alto;
            y = y - alto + 1;
            if (y < 0) {
                alto += y;
                y = 0;
            }
        }

        if ((x + ancho) > areaDib.width) {
            ancho = areaDib.width - x;
        }
        if ((y + alto) > areaDib.height) {
            alto = areaDib.height - y;
        }

        return new Rectangle(x, y, ancho, alto);
    }
}


El applet-container

En primer lugar hemos tenido en cuenta que un Applet es un Panel, y por lo tanto también un Container, así que en lugar de crear una ventana aparte simplemente le agregamos dos componentes: un Label y un Canvas.

	zonaDib.resize(new Dimension (200,200));
	add("North", label);
	add("Center", zonaDib);
	resize(300,250);


El método rezise, sobre la clase miCanvas, nos permite redimensionar el mismo al tamaño deseado. Igualmente, usamos resize sobre el applet para darle un tamaño adecuado. Si se modifica el tamaño de la ventana en el appletviewer se observará un comportamiento algo extraño en cuanto al posicionamiento relativo del rectángulo y el cartel, pero para simplificar esto bastará.

Nuestro Canvas a medida

Como no vamos a tomar ninguna acción especial al crear el canvas, no hemos definido el constructor (se utiliza el constructor por defecto de la clase Canvas).

Simplemente hemos redefinido algunos métodos para actuar al presionar, arrastrar y soltar el mouse, para redibujar el área de dibujo (canvas) y para recortar el rectángulo dibujado si nos vamos con el mouse fuera del espacio que ocupa el canvas.

La variable global rectActual, de la clase Rectangle, contendrá las coordenadas del rectángulo que estamos dibujando. El método Paint se llama automáticamente cada vez que es necesario redibujar el componente, o si llamamos explícitamente al método repaint():

public void paint(Graphics g) {
	Dimension d = size();
	g.setColor(Color.red);
	g.drawRect(0, 0, d.width-1, d.height-1);
	g.setColor(Color.blue);
	if (rectActual != null) {
		Rectangle box = cortarRect(rectActual, d);
		g.drawRect(box.x, box.y, box.width-1, box.height-1);
	}
    }


En primer lugar le asignamos a una variable d el tamaño del canvas usando el método size(), luego elegimos un color (rojo) para dibujar un borde y dibujamos un rectángulo del tamaño del componente:

	Dimension d = size();
	g.setColor(Color.red);
	g.drawRect(0, 0, d.width-1, d.height-1);


Dos atributos de la clase Dimension, width y height, se han cargado con el tamaño del canvas y son los que usamos para dar el tamaño del rectángulo.

Luego, si se está dibujando un rectángulo (rectActual != null) simplemente lo recortamos (en caso de que hayamos arrastrado el mouse fuera del canvas) y lo dibujamos.

El método que lo recorta a los límites del canvas, cortarRect, asigna a cuatro variables las coordenadas del rectángulo (que se le pasaron como parámetro miRect al llamarlo):

        int x = miRect.x;
        int y = miRect.y;
        int ancho = miRect.width;
        int alto = miRect.height;


Si el ancho (o el alto) es negativo, simplemente lo cambia de signo y toma como coordenada x (y) de origen el otro vértice del rectángulo, que corresponderá al x que se pasó menos el ancho y más uno (recordar que el origen de coordenadas empieza en cero y no en uno). Si este vértice está fuera del canvas (x<0), lo pone en cero y le resta al ancho la parte recortada (notar que ancho+=x, como x es negativo, es en realidad una resta).

if (ancho < 0) {
            ancho = -ancho;
            x = x - ancho + 1;
            if (x < 0) {
                ancho += x;
                x = 0;
            }
        }


Si nos vamos del área de dibujo por la derecha (o por abajo), simplemente le recortamos al ancho (alto) el exceso de modo que llegue hasta el borde del área de dibujo (que también hemos pasado al método como parámetro):

if ((x + ancho) > areaDib.width) {
            ancho = areaDib.width - x;

}

Sólo nos quedan por ver los métodos que responden al mouse.

Cuando presionamos el mouse dentro del canvas, comenzamos la creación de un nuevo rectángulo de ancho y alto cero que comienza en el punto en que hemos presionado el mouse, y redibujamos el canvas:

public boolean mouseDown(Event e, int x, int y) {
	rectActual = new Rectangle(x, y, 0, 0);
	repaint();
	return false;
    }


Al mover el mouse, redimensionamos el rectángulo con ancho x menos el origen de dibujo (y alto y menos el origen de dibujo), y repintamos:

    public boolean mouseDrag(Event e, int x, int y) {
	rectActual.resize(x-rectActual.x, y-rectActual.y);
	repaint();
	return false;
    }


Finalmente, al soltar el mouse, redimensionamos como antes y redibujamos:

    public boolean mouseUp(Event e, int x, int y) {
	rectActual.resize(x-rectActual.x, y-rectActual.y);
	repaint();
	return false;
    }


Como no se toma ninguna medida para guardar el rectángulo dibujado, al crear uno nuevo (reasignando rectActual a un nuevo rectángulo), el anterior se pierde.

Bueno, para empezar a dibujar no está mal. Partes de este código han sido tomadas de ejemplos del Tutorial de Java del site de Sun. Al final del curso haremos una lista de bibliografía y sitios de interés para profundizar en Java, pero por ahora nos falta bastante: algo más de dibujo, manejo de excepciones, threads... Nos vemos el próximo capítulo!

 

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