El control de la animación

prev.gif (997 bytes)chapter.gif (1105 bytes)home.gif (1232 bytes)next.gif (1211 bytes)

Subprocesos (threads)

El diseño del applet

Los botones que controlan la animación

La clase que describe el canvas


Finalizamos el estudio del movimiento de una figura por el área de trabajo del applet, poniendo un panel con tres botones sobre el applet cuyo cometido será el de controlar la animación: empezar la animación, establecer una pausa o continuar la animación en estado de pausa, y finalmente, parar la animación.

disco.gif (1035 bytes)anima3: AnimaApplet3.java, MiCanvas.java

 

El diseño del applet

En la página anterior, el movimiento de la pelota se inicia cuando aparece la página que contiene el applet en la ventana del navegador, es decir, cuando se llama a la función start miembro de la clase que describe el applet. El movimiento se para cuando la página desaparece de la ventana del navegador, es decir, se llama a la función stop miembro de la clase que describe el applet.

En algunas aplicaciones, necesitamos que el usuario controle la animación, es decir, que pueda iniciarla pulsando un botón, suspenderla pulsando otro botón, reanudarla, o que pueda pararla. Para este propósito, diseñamos un applet en cuya parte superior se dispone un panel en el que se sitúan tres botones titulados Empieza, Pausa y Para, y un canvas en la parte inferior ocupando la mayor parte de la superficie del applet.

Los pasos para crear el applet son los siguientes:

Cear el applet, y en el modo de diseño (pestaña Design) situar un panel en la parte superior del applet y tres botones sobre el panel.

 diseño1.gif (2603 bytes)

Establecer FlowLayout como gestor de diseño del panel, de modo que los botones quedan centrados en el panel.

Establecer BorderLayout como gestor de diseño del applet, de modo que el panel quede al norte (NORTH)

Añadir al proyecto una clase derivada de Canvas denominada MiCanvas

En el modo código fuente (pestaña Source), crear un objeto de la clase MiCanvas y añadir dicho objeto al applet en la posición central (CENTER).

        this.add(panel1, BorderLayout.NORTH);
        this.add(canvas, BorderLayout.CENTER);

En modo diseño, hacer doble-clic sobre cada uno de los botones. JBuilder genera el código de la función respuesta (Anonymous Adapter), asociando mediante addActionListener el control botón con un objeto de una clase anónima que implementa el interface ActionListener y que define actionPerformed.

 

Los botones que controlan la animación

Cuando se pulsa el botón titulado Empieza se llama a la función respuesta btnEmpezar_actionPerformed. La tarea que realiza esta función es similar a la función start miembro de la clase que describe el applet: crea un subproceso u objeto anima de la clase Thread, si no existe, y lo pone en marcha, llamando a su función miembro start.

    void btnEmpezar_actionPerformed(ActionEvent e) {
//...
        bPausa=true;
        if(anima ==null){
            anima=new Thread(this);
            anima.start();
        }
    }

Cuando se pulsa el botón Para, se llama a la función respuesta btnPara_actionPerformed, que realiza la misma tarea que la función stop miembro de la clase que describe el applet: para el subproceso anima llamando a su función miembro stop.

    void btnPara_actionPerformed(ActionEvent e) {
        if(anima!=null){
            anima.stop();
            anima=null;
        }
    }

Cuando se pulsa el botón titulado Pausa se llama a la función respuesta btnPausa_actionPerformed. La variable de control bPausa de tipo boolean que inicialmente toma el valor true, cambia a false y a la vez, el título del botón cambia de Pausa a Continua. Al pulsar de nuevo el botón, bPausa que vale false cambia a true, y el título del botón vuelve a ser Pausa.

    void btnPausa_actionPerformed(ActionEvent e) {
         if(bPausa==true){
            btnPausa.setLabel("Continua");
            bPausa=false;
        }else{
            btnPausa.setLabel("  Pausa  ");
            bPausa=true;
        }
    }

La definición de la función run es similar a la estudiada en la página anterior. La diferencia estriba en que el movimiento de la pelota tiene lugar en el canvas, y que solamente se mueve (se llama a la función miembro mover) si bPausa toma el valor true, es decir, no se ha pulsado el botón titulado Pausa.

  public void run() {
    long t=System.currentTimeMillis();
    while (true) {
        if(bPausa){
            canvas.mover();
        }
        try{
            t+=retardo;
            Thread.sleep(Math.max(0, t-System.currentTimeMillis()));
        }catch(InterruptedException ex){
            break;
        }
    }
  }


La clase que describe el canvas

El movimiento de la pelota no tiene lugar en la ventana del applet, sino en un canvas. Copiamos del ejemplo anterior AnimaApplet2.java la definición de mover y la redefinición de update y la pegamos en la clase que describe el canvas denominada MiCanvas.

La función miembro inicio que obtiene las dimensiones del canvas y la posición inicial de la pelota, de forma similar a la función init del applet que vimos en la página anterior. La función inicio miembro de MiCanvas nos permitirá en otros programas inicializar el objeto canvas una vez creado, pasándole, por ejemplo, la posición inicial de la pelota que el usuario ha podido introducir en controles de edición.

El código completo de la clase que describe el canvas, es el siguiente.

public class MiCanvas extends Canvas {
    int radio=10;           //radio de la pelota
    int x, y;               //posición del centro de la pelota
    int dx = 1;             //desplazamientos
    int dy = 1;
    int anchoCanvas;
    int altoCanvas;
//Doble buffer
     Image imag;
     Graphics gBuffer;

    public MiCanvas() {
        setBackground(Color.white);
    }
    void inicio(){
//dimensiones del canvas
        anchoCanvas=getSize().width;
        altoCanvas=getSize().height;
//posición inicial de partida
        x=anchoApplet/4;
        y=altoApplet/2;
    }

    public void update(Graphics g){
        if(gBuffer==null){
            imag=createImage(anchoCanvas, altoCanvas);
            gBuffer=imag.getGraphics();
        }
        gBuffer.setColor(getBackground());
        gBuffer.fillRect(0,0, anchoCanvas, altoCanvas);
//dibuja la pelota
        gBuffer.setColor(Color.red);
        gBuffer.fillOval(x-radio, y-radio, 2*radio, 2*radio);
//transfiere la imagen al contexto gráfico del canvas
        g.drawImage(imag, 0, 0, null);
 }
  void mover() {
        x += dx;
        y += dy;
        if (x >= (anchoCanvas-radio) || x <= radio) dx*= -1;
        if (y >= (altoCanvas-radio) ||y <= radio) dy*= -1;
        repaint();      //llama a update
  }
  public void paint (Graphics g) {
//se llama la primera vez que aparece el applet
  }
}