Dibujo de Componentes

Cuando un programa Java con un GUI necesita dibujarse a sí mismo -- si es la primera vez, o porque se ha vuelto visible o porque necesita cambiar su apariencia para reflejar algo que ha sucedido dentro del programa -- empieza con el componente más alto que necesita ser redibujado (por ejemplo, el componente superior en el árbol de la herencia) y va bajando hacia los componentes inferiores. Esto está orquestrado por el sistema de dibujo del AWT.

Aquí tienes, como recordatorio, el árbol de herencia del programa conversor:

                             un Frame
                                |
                               ...
                                |
                           un Converter
                                |
                ----------------------------------
                |                                |
 un ConversionPanel (metricPanel)  un ConversionPanel (usaPanel)   
                |                                |
       -------------------              -------------------
       |        |        |              |        |        |
    un Label    |      un Choice   un Label      |    un Choice
                |                                |
          --------------                  ---------------
          |            |                  |             |
    un TextField  un Scrollbar      un TextField    un Scrollbar

Aquí tiene lo que sucede cuando la aplicación Converter se dibuja a sí misma:

  1. Primero se dibuja el Frame (marco).
  2. Luego se dibuja el objeto Converter, dibujando una caja alrededor de su área.
  3. Después se dibuja un de los dos ConversionPanels, dibujando una caja alrededor de su área.
    Nota: No se puede contar con el orden en que se van a dibujar dos componentes con el mismo nivel. Por ejemplo, no se puede contar con que el panel metrico se dibujará antes que el americano. Similarmente, tampoco se puede depender del orden de dibujo de dos componentes de diferentes niveles si el componente inferior no está contendido en el componente superior.
  4. Por último dibujan los contenidos de los ConversionPanel -- Label, TextField, Scrollbar, y Choice.
De esta forma, cada componente se dibuja a sí mismo antes que los componentes que contiene. Esto asegura que el fondo de un Panel, por ejemplo, sólo sea visible cuando no está cubierto por ninguno de sus componentes.

Cómo ocurre la petición de redibujado

Los programas sólo pueden dibujarse cuando el AWT les dice que lo hagan. La razón es que cada ocurrencia de que un dibujo se dibuje a sí mismo debe ejecutarse sin interrupción. De otra forma podrían producirse resultados impredecibles, como que un botón se dibujará sólo por la mitad, cuando fuera interrumpido por un alguna animación lenta. El AWT ordena las peticiones de redibujado mediante su ejecución en un thread. Un componente puede utilizar el método repaint() para pedir que sea programado para redibujarse.

El AWT pide que un componente se redibuje llamando al método update() del componente. La implementación por defecto de este método sólo limpia el fondo del componente (dibujando un rectángulo sobre el área del componente con el color del fondo del propio componente) y luego llama al método paint() del componente. La implementación por defecto del método paint() no hace nada.

El Objeto Graphics

El único argumento para los métodos paint() y update() es un objeto Graphics que representa el contexto en el que el componente puede realizar su dibujo. La clase Graphics proporciona métodos para lo siguiente:
  • Dibujar y rellenar rectángulos, arcos, líneas, óvalos, polígonos, texto e imágenes.
  • Obtener o seleccionar el color actual, la fuente o el área de trabajo.
  • Seleccionar el modo de dibujo.

Cómo dibujar

La forma más sencilla de que un componente pueda dibujarse es poner código de dibujo en su método paint(). Esto significa que cuando el AWT hace una petición de dibujado (mediante la llamada al método update() del componente, que está implementado como se describió arriba), se limpia todo el área del componente y luego se llama al método paint() del método. Para programas que no se redibujan a sí mismos de forma frecuente, el rendimiento de este esquema está bien.

Importante: Los métodos paint() y update() deben ejecutarse muy rápidamente! De otra forma, destruiran el rendimiento percibido del programa. Si se necesita realizar alguna operación lenta como resultado de una petición de dibujado, házlo arrancando otro thread ( o enviando una petición a otro thread) para realizar la operación. Para obtener ayuda sobre la utilización de threads puedes ver Threads de Control.

Abajo hay un ejemplo de implementación del método paint(). Las clases Converter y ConversionPanel dibujan una caja alrededor de su área utilizando este código. Ambas clases también implementan un método insets() que especifica el área libre alrededor del contenido de los paneles. Si no tuvieran este método, el método paint() solaparía los límites externos del panel.

public void paint(Graphics g) {
    Dimension d = size();
    g.drawRect(0,0, d.width - 1, d.height - 1);
}

Los programas que se redibujan muy frecuentemente pueden utilizar dos técnicas para aumentar su rendimiento: implementar los dos método update() y paint(), y utilizar un doble buffer. Estas técncas se explican en Eliminar el Parpadeo.

Para más información sobre cómo dibujar, puedes ver la lección Trabajar con Gráficos.


Ozito