Eliminar el Parpadeo: Implementar el Doble Buffer

La página anterior mostró cómo eliminar el parpadeo implementando el método update(). Podrías haber observado (dependiendo del rendimiento de tu ordenador) que el applet resultante, aunque no parpadea, se arrastra un poco. Esto es, en lugar de actualizarse completamente el área de dibujo (o marco) de una vez, algunas veces, una parte se actualiza antes que la parte de su derecha, causando un dibujo bacheado entre columnas.

Puedes utilizar el doble buffer paa evitar este efecto de arrastre forzando que todo el marco se dibuje de una sola vez. Para implementar el doble buffer, se necesita crear un buffer fuera de pantalla (normalmente llamado (backbuffer o buffer fuera de pantalla), dibujar en él, y luego mostrar la imagen resultante en la pantalla.

Aquí tienes el código para el ejemplo de animación de gráficos, modificado para implementar el doble buffer. Abajo tienes el applet resultante en acción.

Para crear un buffer fuera de pantalla con el AWT, primero necesitas crear una imagen del tamaño apropiado y luego obtener un contexto gráfico para manipular la imagen. Abajo tiene el código que hace esto:

//Donde se declaren las Variables de ejemplar:
Dimension offDimension;
Image offImage;
Graphics offGraphics;
. . .
//en el método update(), donde d contiene el tamaño del área de dibujo en la pantalla:
if ( (offGraphics == null)
  || (d.width != offDimension.width)
  || (d.height != offDimension.height) ) {
    offDimension = d;
    offImage = createImage(d.width, d.height);
    offGraphics = offImage.getGraphics();
}
Abajo, en negrita, está el nuevo código de dibujo en el método update(). Observa que el código de dibujo ahora limpia completamente el fondo, pero no causa parpadeo poque el código está dibujando en el buffer fuera de pantalla, no en la pantalla. Observa también que todas las llamadas a fillRect() se realizan al buffer fuera de pantalla. El resultado final se muestra en la pantalla justo antes de que el método update() retorne.
public void update(Graphics g) {
    ...//Primero, inicializa las variables y crea el buffer fuera de pantalla 
       //Luego borra al imagen anterior: 
        offGraphics.setColor(getBackground());
        offGraphics.fillRect(0, 0, d.width, d.height);
        offGraphics.setColor(Color.black);

    ...//Hace todo lo que hacia el viejo método paint()  -- hasta que dibujamos el rectángulo
            if (fillSquare) {
                offGraphics.fillRect(x, y, w, h);
                fillSquare = false;
            } else {
                fillSquare = true;
            }

    ...//El resto es exactamente igual que el viejo método paint() hasta casi el final 
       //donde añadimos lo siguiente:
    //dibuja la imgen en la pantalla.
    g.drawImage(offImage, 0, 0, this);
}

No es necesario que el método update() llame al método paint(). Todo lo necesario es que el método update() de alguna forma dibuje su buffer fuera de pantalla en la pantalla, y que el método paint() pueda dibujar la imagen apropiada cuando sea llamado directamente por el AWT.

Podrías preguntarte por qué se crean la imagen fuera de pantalla y el contexto gráfico dentro del método update(), en vez de hacerlo (por ejemplo) en el método start(). La razón es que la imagen y el contexto gráfico dependen del tamaño del área de dibujo del Panel del Applet y del tamaño del área de dibujo del componente y no son válidos hasta que el componente se haya dibujado por primera vez en la pantalla.


Ozito