?? java13.htm
字號:
<HTML>
<HEAD>
<TITLE>Curso Java desde Cero</TITLE>
<META NAME="GENERATOR" CONTENT="Internet Assistant for Microsoft Word 2.0z">
</HEAD>
<BODY background=/iconos/1.gif TEXT=000000 LINK=FF0000 VLINK=A62A2A>
<H1>DibuJava II<BR>
</H1>
<P>
Vamos a retocar un poquito nuestro <FONT FACE="Arial">ejemplo15</FONT>
para que no se borren los rectángulos cuando queremos dibujar
uno nuevo. Aprenderemos algo sobre la clase <B>Vector</B>, perteneciente
al paquete java.util.
<H2>Vectores en acción</H2>
<P>
Los vectores nos permiten hacer arreglos de cualquier tipo de
objeto, y referirnos individualmente a cualquier elemento del
vector, aunque para utilizarlos (debido a que para java el vector
contiene objetos genéricos) tendremos que decirle qué
clase de objeto es mediante un "cast". Vamos a ver cómo
quedan nuestras clases <FONT FACE="Arial">Ejemplo16</FONT> (ex
Ejemplo15) y <FONT FACE="Arial">miCanvas</FONT>:<BR>
<PRE>
<FONT SIZE=2>import java.awt.*;
<B>import java.util.*;
</B>import java.applet.Applet;
public class <B>Ejemplo16</B> extends Applet {
public void init() {
<I>................ (esta parte no cambia)................
</I> }
}
class miCanvas extends Canvas {
<B> Vector v = new Vector(); // inicializamos con tamaño indeterminado
// Java se encarga de manejar la memoria necesaria!
</B> public boolean mouseDown(Event e, int x, int y) {
<B> v.addElement( new Rectangle(x, y, 0, 0) ); // nuevo elemento!
</B> repaint();
return false;
}
public boolean mouseDrag(Event e, int x, int y) {
<B> Rectangle r = (Rectangle)v.lastElement(); // cast: <I>v</I> son rectángulos
r.resize( x - r.x, y - r.y ); // (creé <I>r</I> sólo por claridad)
</B> repaint();
return false;
}
public boolean mouseUp(Event e, int x, int y) {
<B> Rectangle r = (Rectangle)v.lastElement(); // cast: <I>v</I> son rectángulos
r.resize( x - r.x, y - r.y ); // (creé <I>r</I> sólo por claridad)
</B> repaint();
return false;
}
public void paint(Graphics g) {
<B> int i; // contador de rectángulos
</B> Dimension d = size();
g.setColor(Color.red);
g.drawRect(0, 0, d.width-1, d.height-1);
g.setColor(Color.blue);
<B> if (v.size() > 0)
for (i=0; i<v.size(); i++) {
Rectangle box = cortarRect( (Rectangle)v.elementAt( i ), d);
</B> g.drawRect(box.x, box.y, box.width-1, box.height-1);
}
}
<I>........................ (el resto no cambia) ........................
</I>}
Les sugiero utilizar un HTML que reserve espacio suficiente para ver todo el applet, como:
<HTML>
<HEAD>
<TITLE>Ejemplo 16 - Ejemplo con canvas</TITLE>
</HEAD>
<BODY>
<applet code="Ejemplo16.class" <B>width=300 height=250</B>>
</applet>
</BODY>
</HTML><BR>
</FONT>
</PRE>
<P>
Veamos los pasos ahora. En primer lugar creamos una variable (global
a la clase) llamada <FONT FACE="Arial">v</FONT>, de clase <FONT FACE="Arial">Vector</FONT>,
y sin asignarle un tamaño definido:
<PRE>
<FONT SIZE=2>Vector v = new Vector();<BR>
</FONT>
</PRE>
<P>
Al crear un nuevo rectángulo agregamos un elemento (objeto)
al vector mediante el método <FONT FACE="Arial">add</FONT>:
<PRE>
<FONT SIZE=2>v.addElement( new Rectangle(x, y, 0, 0) );<BR>
</FONT>
</PRE>
<P>
Para acceder a un atributo de un objeto del vector no basta utilizar
directamente el vector, como:
<PRE>
<FONT SIZE=2> v.lastElement().x<BR>
</FONT>
</PRE>
<P>
(<FONT FACE="Arial">lastElement()</FONT> nos permite acceder al
último elemento agregado al vector). Es necesario aclarar
explícitamente que el elemento en cuestión es un
rectángulo, ya que el vector puede contener objetos de
cualquier tipo. Para eso usamos el <I>casting</I>:
<PRE>
<FONT SIZE=2> <B>(Rectangle)</B>v.lastElement().x<BR>
</FONT>
</PRE>
<P>
En nuestro código original reemplazaríamos por:
<PRE>
<FONT SIZE=2>(Rectangle)v.lastElement().resize( x - (Rectangle)v.lastElement().x, ......
<BR>
</FONT>
</PRE>
<P>
Pero es más claro si usamos una variable local de clase
Rectangle, le asignamos el mismo objeto que acabamos de agregar
al vector, y lo usamos en su lugar:
<PRE>
<FONT SIZE=2> Rectangle <B>r</B> = (Rectangle)v.lastElement();
<B>r</B>.resize( x - <B>r</B>.x, y - <B>r</B>.y );<BR>
</FONT>
</PRE>
<P>
Finalmente, en el método <FONT FACE="Arial">paint()</FONT>
no podemos asignar el elemento hasta no saber que existe (originalmente
el vector estaba vacío!). Así que un <FONT FACE="Arial">if</FONT>
nos permite verificar que el tamaño del vector es mayor
que cero (tiene elementos), y un <FONT FACE="Arial">for</FONT>
nos permite dibujarlos uno por uno.
<P>
Se puede acceder a todos los elementos, uno por uno, mediante
el método <FONT FACE="Arial">elementAt(x)</FONT>, que nos
da el x-ésimo elemento del vector. El método <FONT FACE="Arial">size()</FONT>
nos da la cantidad de elementos (el primero es el número
0, y así):
<PRE>
<FONT SIZE=2> if (v.size() > 0)
for (i=0; i<v.size(); i++) {
Rectangle box = cortarRect( (Rectangle)v.elementAt( i ), d);
g.drawRect(box.x, box.y, box.width-1, box.height-1);
}<BR>
</FONT>
</PRE>
<P>
Aquí no hemos creado variables intermedias ya que igualmente
es claro (eso creo...).<BR>
<H2>Flicker molesto!</H2>
<P>
Bueno, el problema que nos queda es el molesto "flicker",
o sea la manera en que titila el dibujo cuando movemos el mouse.
Esto es porque cada vez que se llama a <FONT FACE="Arial">paint()</FONT>,
el fondo se borra y se redibuja todo el canvas.
<P>
Básicamente, la manera de evitarlo es reescribiendo el
método <FONT FACE="Arial">update()</FONT>, que es el que
borra el fondo antes de llamar a <FONT FACE="Arial">paint()</FONT>
para que no lo borre; otro método (que es el que vamos
a usar) es dibujar <I>no sobre la pantalla</I> sino sobre un "buffer"
gráfico, y luego copiar ese buffer sobre la pantalla (lo
que es mucho más eficiente que dibujar sobre la misma).
<P>
Para eso vamos a crear un par de objetos:
<PRE>
<FONT SIZE=2>class miCanvas extends Canvas {
Vector v = new Vector();
<B> Image imgBuff;
Graphics grafBuff;
</B>.............................<BR>
</FONT>
</PRE>
<P>
<FONT FACE="Arial">Image</FONT> es una clase abstracta, madre
de todas las clases que representan imágenes gráficas.
<FONT FACE="Arial">Graphics</FONT> es también abstracta
y nos permite obtener un contexto en el cual dibujar.
<P>
Lo que vamos a hacer es modificar nuestro método <FONT FACE="Arial">paint()</FONT>
para que simplemente llame a <FONT FACE="Arial">update()</FONT>,
y redefinir el método <FONT FACE="Arial">update()</FONT>:
<PRE>
<FONT SIZE=2>public void paint(Graphics g) {
<B> update(g);
</B>}<BR>
</FONT>
</PRE>
<P>
El método <FONT FACE="Arial">update()</FONT> es el que
hará todo el trabajo y básicamente es como nuestro
viejo <FONT FACE="Arial">paint()</FONT> con algunos agregados:
<PRE>
<FONT SIZE=2>public void update(Graphics g) {
int i;
Dimension d = size();
<B>if (grafBuff == null) {
imgBuff = createImage(d.width, d.height);
grafBuff = imgBuff.getGraphics();
}
grafBuff.setColor(getBackground());
grafBuff.fillRect(0, 0, d.width, d.height);
</B> <I>grafBuff</I>.setColor(Color.red);
<I>grafBuff</I>.drawRect(0, 0, d.width-1, d.height-1);
<I>grafBuff</I>.setColor(Color.blue);
if (v.size() > 0) for (i=0; i<v.size(); i++) {
Rectangle box = cortarRect((Rectangle)v.elementAt(i), d);
<I>grafBuff</I>.drawRect(box.x, box.y, box.width-1, box.height-1);
}
<B> g.drawImage(imgBuff, 0, 0, this);
</B>}<BR>
</FONT>
</PRE>
<P>
En <B>negrita</B> hemos indicado los agregados.
<P>
Si no está creado todavía (<FONT FACE="Arial">grafBuff==null</FONT>),
creamos nuestro buffer de dibujo. Para crear dicho buffer gráfico
(de clase <FONT FACE="Arial">Graphics</FONT>), primero creamos
una imagen que en este caso tiene las mismas dimensiones que el
canvas (<FONT FACE="Arial">d.width</FONT> x <FONT FACE="Arial">d.height</FONT>),
y luego asignamos a <FONT FACE="Arial">grafBuff</FONT> el contexto
de dicha imagen mediante el método <FONT FACE="Arial">getGraphics()</FONT>.
Imagínense que con <FONT FACE="Arial">createImage(...)</FONT>
crean una "pantalla virtual", y <FONT FACE="Arial">getGraphics()</FONT>
nos da una forma de acceder a esa pantalla como si fuera real.
<P>
Utilizando dicho contexto, elegimos como color el mismo color
de fondo del applet (<FONT FACE="Arial">getBackground()</FONT>)
y dibujamos un rectángulo lleno (<FONT FACE="Arial">fillRect(...)</FONT>),
borrando así cualquier cosa que hubiera estado dibujada.
<P>
En <I>itálica</I> hemos indicado las modificaciones a nuestro
método anterior. Simplemente, en lugar de usar el contexto
de la pantalla (el parámetro <FONT FACE="Arial">g</FONT>
del método), dibujamos sobre nuestro contexto-pantalla
virtual.
<P>
Finalmente, y para poder visualizar nuestro dibujo, usamos el
método <FONT FACE="Arial">drawImage</FONT> sobre el contexto
de la pantalla real (<FONT FACE="Arial">g</FONT>), que copia nuestro
contexto <FONT FACE="Arial">imgBuff</FONT> en las coordenadas
(0,0) sobre la pantalla. Se hace también referencia al
canvas (...<FONT FACE="Arial">this</FONT>): el cuarto parámetro
de <FONT FACE="Arial">drawImage</FONT> es un objeto de clase <FONT FACE="Arial">ImageObserver</FONT>,
una interface que sirve para que el objeto dentro del cual se
dibuja reciba mensajes asincrónicos que le indican cómo
está siendo construida la imagen, y cuándo está
lista.<BR>
<H2>Animate!</H2>
<P>
Si bien puede ser un poco más complejo de entender que
un dibujo directo sobre la pantalla, notarán que la implementación
es directa y no trae ningún problema. Esta misma aproximación
puede utilizarse para crear animaciones.
<P>
En este ejemplo, para manejar la ejecución cuadro a cuadro
de la animación, usamos <I>Threads</I>. No se preocupen
por eso, lo veremos pronto. Únicamente tengan en cuenta
que nuestro applet debe implementar la clase <I>runnable</I>,
y el thread se encarga de ejecutar el método <FONT FACE="Arial">run()</FONT>
que simplemente llama a <FONT FACE="Arial">repaint()</FONT> y
espera 100 milisegundos entre cuadro y cuadro.
<P>
El trabajo de cálculo y dibujo lo hace <FONT FACE="Arial">update()</FONT>.
Se los dejo para que lo estudien; no es nada complicado y también
usa doble buffering (como el ejemplo anterior).<BR>
<PRE>
<FONT SIZE=2>import java.awt.*;
import java.util.*;
import java.applet.Applet;
public class Ejemplo18 extends Applet implements Runnable {
Thread animador;
Image imgBuff;
Graphics grafBuff;
double ang = 0.0;
public void init() {
resize(new Dimension (200,200));
}
public void start() {
if (animador == null) animador = new Thread(this);
animador.start();
}
public void run() {
while (Thread.currentThread() == animador) {
repaint();
try {
Thread.sleep(100);
}
catch (InterruptedException e) {
break;
}
}
}
public void update(Graphics g) {
int i;
int dx, dy;
Dimension d = size();
if (grafBuff == null) {
imgBuff = createImage(d.width, d.height);
grafBuff = imgBuff.getGraphics();
}
grafBuff.setColor(getBackground());
grafBuff.fillRect(0, 0, d.width, d.height);
grafBuff.setColor(Color.red);
grafBuff.drawRect(0, 0, d.width-1, d.height-1);
grafBuff.setColor(Color.blue);
dx = (int)(50 * Math.abs(Math.cos(ang)));
dy = (int)(50 * Math.abs(Math.sin(ang)));
ang = ang + 0.1;
if (ang>2*Math.PI) ang = 0.0;
grafBuff.drawRect(100-dx, 100-dy, 2*dx, 2*dy);
g.drawImage(imgBuff, 0, 0, this);
}
}<BR>
<BR>
</FONT>
</PRE>
<P>
Como siempre, me despido hasta la clase que viene...<BR>
<BR>
<P>
Jorge Bourdette
<P>
<A HREF="jpb@amarillas.com" >jpb@amarillas.com</A><BR>
</BODY>
</HTML>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -