Gráficos en java

De ChuWiki
Saltar a: navegación, buscar

Lo básico para hacer un gráfico

Lo básico para hacer un gráfico consiste en hacer una clase que herede de Canvas y redefinir el método paint(). Lo que dibujemos en este método usando el Graphics que nos pasan, se dibujará sobre el Canvas.

El siguiente programa dibuja una línea roja del punto (0,0), en la esquina superior izquierda, al punto (100,100), más abajo y a la derecha.

import java.awt.*;
public class MiLienzo extends Canvas
{
    public void paint (Graphics g)
    {
        g.setColor (Color.RED);
        g.drawLine (0,0,100,100);
    }
}

Es importante dibujar redefiniendo el método paint() para que el dibujo se mantenga al redimensionar la ventana, al minimizarla y luego maximizarla o pasárle otra ventana por encima. El motivo es que java llama a este método cada vez que el Canvas necesita repintado por cualquiera de estos motivos.

Filosofía del pintado

En todos los componentes java hay tres métodos importantes para el pintado: repaint(), update(Graphics g) y paint(Graphics g).

El primer método es al que llamamos en código cuando necesitamos que un componente se repinte. Si es un componente normal (JTable, JButton, JLabel, etc), no es necesario llamarlo. Cuando cambiemos algo de dicho componente, automáticamente se repinta.

Es necesario llamar a repaint() sobre todo para el tema de gráficos. Si estamos dibujando unos datos en un Canvas, como comentamos en el punto anterior, cuando cambien los datos a dibujar, debemos llamar al método repaint() del Canvas.

La llamada a repaint() únicamente avisa a la máquina virtual java que ese componente necesita repintado. El método en sí mismo no borra ni dibuja nada. La máquina virtual encola dicha petición en su cola de eventos de pantalla, junto a eventos de teclado y ratón. Cuando llegue el momento, se comienza el redibujado en un hilo distinto, El hilo de awt.

Cuando la máquina virtual decide repintar el componente, lo que hace es llamar a su método update(Graphics g). Este método, por defecto, borra el componente por completo, rellenándolo con un rectángulo relleno del tamaño de componente y del color de fondo. Inmediatamente después, el método update(Graphics g) llama al método paint(Graphics g).

El método paint(Grahics g) es el que realmente dibuja el componente.


Qué métodos es interesante redefinir. El doble buffer

A la hora de hacer nuestros propios gráficos (o dibujos concretos sobre componentes), es casi obligatorio heredar del componente y redefinir el método paint(Graphics g). Como ya comentamos antes, esta es la única manera de conseguir que nuestro gráfico se refresque sólo cuando sea necesario. La máquina virtual, a través del método update(Graphics g) se encargará de llamarlo cuando sea necesario.

Si sólo hacemos esto, es posible que en cada repintado veamos parpadear el componente, al borrarse y volver a pintarse desde cero. Salvo técnicas más avanzadas, una posible solución es redefinir también el método update (Graphics g) de forma que NO borre nada, sino que directamente llame a paint(Graphics g). Esto, obviamente, da buen resultado si el método paint(Graphics) rellena TODOS los pixels del componente. Si no es así, vermos como se dibujan los nuevos datos sobre los antiguos.

Para conseguir que el método paint(Graphics g) dibuje todos los pixels, suele hacerse que paint(Graphics g) dibuje sobre un BufferedImage (o clase similar) y luego "pegar" esa imagen directamente sobre el componente. El código para esto puede ser parecido a esto

public class MiCanvas extends Canvas
{
   // Llama directamente a paint(), evitando el borrado del componente
   public void update (Graphics g)
   {
      paint (g);
   }

   public void paint (Graphics g)
   {
      // Se crea una imagen del mismo tamaño que el Canvas
      BufferedImage imagen = new BufferedImage (getWidth(), getHeight(), ...);

      // Se dibuja en la imagen
      imagen.getGraphics().draw...

      // Se "pega" la imagen sobre el componente
      g.drawImage(imagen, 0, 0, this);
   }
}

Al no borrar el componente y dibujar la imágen encima directamente, se evita el parpadeo.

Esta técnica se conoce como doble buffer. Un buffer son los pixels en pantalla, el otro es el BufferedImage. Lo que se hace es dibujar sobre uno de ellos (el BufferedImage), evitando dibujar sobre la pantalla. Cuando el dibujo sobre el BufferedImage está completo, se pega directamente encima de la pantalla.

¿¿¿ En los JComponent ya tenemos todo esto implementado. Basta con llamar al método

unJComponent.setDoubleBuffered(true);

y no hay que hacer más, salvo redefinir el método paint(Graphics g) de la forma habitual. El Graphics que nos pase la máquina virtual en este método ya será el de un Buffer, y no el de la pantalla. ??? (sin verficar).

Librerías adicionales para el dibujo de gráficos

Java standard no viene con nada que nos permita hacer gráficos cómodamente, pero hay muchas librerías externas que nos podemos bajar.