Java para programadores. (5.4):Los métodos Graphics y Paint

TODO LO QUE VE APARECER EN LA PANTALLA DE SU ORDENADOR se ha de dibujar, incluso el texto. El API de Java incluye una gran cantidad de clases y métodos que están dedicados a dibujar en la pantalla. En esta sección, podremos ver los mas importantes.(Observe que todas las clases mencionadas en esta sección están definidas en el paquete Java.awt y antes de usarse, debe ser importado.)

Para poder dibujar con Java cualquier cosa, necesitará un contexto gráfico. Un contexto gráfico es un objeto perteneciente a la clase Graphics. En esa clase se proporcionan  métodos para dibuja figuras, textos, y dibujos. La clase Graphics es abstracta, esto significa que no puede crear un contexto gráfico directamente. Hay dos formas para definir el contexto gráfico: El sistema proporciona un contexto gráfico cuando llama al método paint(). Para dibujar fuera del método paint(), existe la función getGraphics() que devuelve un contexto gráfico.

Hay dos tipos de contextos gráficos. Uno para dibujar con  Component y otro para dibujar con Images en off-screen. La clase Component es una superclase de Applet. La mayoría de los métodos que ha visto en applets, incluido el paint() y todos los métodos de manejo de eventos, realmente, son heredados de Component. Un objeto de tipo Component representa un componente de la interface gráfica del usuario, esto es, algo que es visible en pantalla. El método instanciable getGraphics() esta definido en la clase Componet. Devuelve un contexto gráfico que puede usarse para dibujar un componente en particular. Esto es, si comp es un objeto del tipo Component y usted escribe

Graphics g = comp.getGraphics();

entonces g podrá ser usado para dibujar en el área rectangular de la pantalla que representa el componente comp. Cuando llama getGraphics() en un applet, devuelve el contexto gráfico para dibujar en el applet (el cual, recuerde que es de tipo Component, dado que Applet es una subclase de Components).

El otro tipo de contexto gráfico es para dibujar con Images fuera de pantalla (off-screen). Un contexto gráfico de ese tipo se obtiene llamando a la función getGraphics() definida en la clase Image. Las imágenes off-screen son realmente datos almacenados en la memoria y no son visible en la pantalla. Sin embargo, pueden ser copiados a la pantalla muy rápidamente, por lo que se usan generalmente para hacer mover las animaciones: en lugar de dibujar algo en la pantalla y hacer esperar al usuario, puede dibujar en off-screen y copiar el resultado a la pantalla de golpe. Hablaremos mas de esto al final de la sección. Por ahora, recuerde únicamente que podemos emplear los mismos comandos para dibujar en pantalla con Components que para dibujar off-screen con Images.

Si g es un contexto gráfico que ha obtenido con un método getGraphics() es una buena idea el llamar al método g.dispose()después que haya terminado de utilizarlo. Este método libera todos los recursos del sistema que haya usado en el contexto gráfico. Es una buena idea, porque en muchos sistemas estos recursos son limitados. (Pero nunca deberá llamar al método dispose() para contextos gráficos obtenidos en el método paint()).


Paint, Repain y Update

Muchos applets pueden hacer todas las operaciones de dibujo en el método paint(). Este método debe ser lo bastante astuto como para redibujar correctamente el applet continuamente, empleando los datos guardados en variables instanciables si fuera necesario. Si en mitad de cualquier otro método realiza alguna acción que deba hacer cambiar la apariencia del applet, deberá modificar la instancia de las variables que controlan el contenido, y volver a dibujar la pantalla llamando al método repaint() para decirle al sistema  que debe redibujar el applet en cuanto tenga oportunidad (llamando al método paint()) Esto es únicamente una aproximación, que hemos estado utilizando hasta ahora, de como dibuja el applet. En la mayoría de casos es satisfactoria. Sin embargo, en algunos momentos, querrá ir mas lejos que este simple modelo, para crear algo de fantasía. Al llegar a este punto, necesitara conocer mejor como se dibujan realmente las cosas en la pantalla.

Primero de todo, no es exacto que un applet este obligado a tener un método paint(). Un applet puede contener otros componentes como botones y algo llamado “canvasses”, que es justamente un área rectangular para dibujar. Cada componente tiene su propio método paint() que es llamado por el sistema cuando el componente necesita ser redibujado.Así que los componentes son responsables de su propio redibujado. De esta manera, por ejemplo, si tiene que añadir un botón a un applet, no deberá preocuparse de redibujarlo, el solo lo hará. (o, si lo prefiere, el sistema dibujara el botón llamando a su método paint(). Si quiere utilizar un área de dibujo en un applet, deberá definir una subclase de la clase Canvas y para poder dibujar proporcionarle un método paint() en esa subclase . Así es como enseña a Canvas a dibujarse por el solo. El método paint() del applet , solo es responsable de los elementos que el directamente ha dibujado, no de todos los subcomponentes contenidos en el applet. Aprenderá mas acerca de los componentes en el siguiente capítulo

Otra complicación proviene del hecho de que el sistema, realmente, no llama al método paint() de un applet o de un componente de forma directa. Es otro método llamado update() el que es llamado por el sistema. Este procedimiento integrado, lo primero que hace es borrar todo lo que hay dibujado por el applet y por los componentes. Entonces llama a los paint()para redibujar el contenido. Normalmente, el empezar borrando el contenido es algo correcto dado que los componentes contenidos pueden haber cambiado. Sin embargo, en algunos casos deseara saltarse ese paso. (Esto, por ejemplo, es normal si esta trabajando con imágenes off-screen). En ese caso, puede sobregrabar el método update()para hacerlo mas sencillo:

        public void update(Graphics g) {
           paint(g); // just call paint, without erasing first
        }

Coordenadas

La pantalla del ordenador es una rejilla de cuadrados muy pequeños llamados pixeles. El color de cada pixel se puede estableces de forma individual y podemos dibujar en la pantalla cambiando el color de ciertos pixeles.Curso de java. Dibujando la pantalla

En un contexto gráfico, se dibuja en un rectángulo hecho de pixeles. La posición en el rectángulo se especifica por medio de dos enteros que son las coordenadas (x,y). La esquina superior izquierda es la coordenada (0,0). Para componentes, puede averiguar el tamaño del rectángulo llamando al método size(). La anchura del rectángulo es size().widthpixels y la altura  es size().heigth pixeles La figura de la derecha, presenta un componente de 12 por 8 pixeles (con pixeles muy grandes). Se presenta una línea pequeña, un rectángulo y una elipse que se han dibujado coloreando individualmente los pixeles.(Note que, hablando con propiedad, las coordenadas no pertenecen a los pixeles sino a las líneas que hay entre ellos.)

Cuando está escribiendo un applet, no conoce el tamaño. El tamaño se especifica en el tag y no es demasiado correcto es depender que se especifique un tamaño concreto. También es cierto que los applets pueden redimensionarse mientras están funcionando. (Por el momento, esto no es cierto para los applets que están en las paginas web, pero en el futuro puede que cambie). Si quiere dibujarlo todo en el método paint() de su applet, deberá comprobar el tamaño y basar su dibujo en la altura y anchura actual del applet:

      public void paint(Graphics g) {
         int width = size().width;     // tomar anchura real del applet
         int height = size().height;   // tomar altura real
         . . .   // dibujar el contenido
      }

Pero si nunca ha tenido que hacer cálculos basados en el tamaño del applet, puede que quiera hacerlos de una forma semejante a esta:

        int width = -1;  //anchura del applet (inicialmente desconocida)
        int height = -1; //altura del applet (inicialmente desconocida)
            // el valor -1 fuerza a checkSize() a poner el valor 
            // correcto la primera vez que se llama

        void checkSize() {  // Comprobar si ha cambiado el tamaño
           int w = size().width;   // anchura actual
           int h = size().height;  // altura actual
           if ( w != width  ||  h != height ) {  // cambio el tamaño!
              width = w;  // grabar nueva anchura
              height = h; // grabar nueva altura
              . . .  // recalcula todo lo dependiente del tamaño
           }
        }

        public void paint(Graphics g) {
           checkSize();  // comprobar siempre antes de dibujar
           . . .  // dibuja el contenido del applet
        }

El método checkSize() también puede ser llamado desde otros métodos que dependan del tamaño del applet. Es una pequeña trampa, pero probablemente sea la manera mas fácil de controlar los cambios de tamaño de un applet.


Figuras

La clase Graphics proporciona una gran cantidad de métodos para dibujar distintas formas como líneas, rectángulos, y elipses. Las formas se deben indicar utilizando el sistema de  coordenadas comentado anteriormente.  El dibujo se realiza en el color establecido en cada momento para dibujar con el método setColor(). A continuación tiene una lista de los métodos mas importantes. Tenga en cuenta que todos estos métodos entran en la clase Graphics por lo que se deben llamar por medio de un objeto de tipo Graphics (Por ejemplo g.drawLine(0,0,w,h);)

drawLine(int x1, int y1, int x2, int y2)
Dibuja una línea desde el punto (x1,y1) al punto (x2,y2).
drawRect(int x, int y, int width, int height)
Dibuja las líneas de un rectángulo. La esquina superior izquierda esta en (x,y), y se especifica la anchura (width) y la altura (height). El ancho y el alto debe ser positivo o no se dibuja nada.
drawOval(int x, int y, int width, int height)
Dibuja las líneas de una elipse. La elipse es justo la que cabe en el rectángulo especificado por x, y, width, y height. Si la anchura y la altura son iguales, se dibuja la circunferencia.
drawRoundRect(int x, int y, int width, int height, int xdiam, int ydiam)
Dibuja un rectángulo con las esquinas redondeadas. El grado de redondez esta definido por xdiam y ydiam. Las esquinas son arcos de una elipse con el diámetro horizontal xdiam y el vertical ydiam. (Un valor razonable para xdiam y ydiam si 16.)
draw3DRect(int x, int y, int width, int height, boolean raised)
Dibuja un rectángulo que se supone tiene efecto tridimensional, como si estuviera sobresaliendo o empotrado en la ventana.
fillRect(int x, int y, int width, int height)
Dibuja un rectángulo relleno
fillOval(int x, int y, int width, int height)
Dibuja una elipse rellena
fillRoundRect(int x, int y, int width, int height, int xdiam, int ydiam)
Dibuja un rectángulo redondeado relleno
fill3DRect(int x, int y, int width, int height, boolean raised)
Dibuja un rectángulo tridimensional relleno
drawString(String str, int x, int y)
Dibuja la string str, empieza en el punto (x,y). x define la posición de la izquierda de la String. y define la altura para la línea base, lo que es muy semejante a la línea que escribe cobre una guía. (Hay mas cosas sobre dibujo de texto mas adelante en esta misma sección.)
drawImage(Image img, int x, int y, ImageObserver observer)
Dibuja la copia de una imagen. La esquina superior izquierda de la imagen se coloca en   (x,y). Los cuatro parámetros misteriosos, se ha de establecer misteriosamente a la variable especial  this. (Hay mas sobre imágenes mas adelante en esta misma sección)


Colores

Java esta diseñado para trabajar con algo que se llama colores RGB. Un color RGB se especifica por tres números que definen el nivel que tiene el color de rojo,verde y azul respectivamente. En Java el color es un objeto de la clase Color. Puede construir un color nuevo especificando los componentes de rojo, verde y azul. Por ejemplo.

Color miColor=new Color(r,g,b);

Realmente, hay dos constructores a los que puede llamar para realizar esto. En uno r,g y b son enteros del rango 0 a 255; en otro esos números son del tipo float del rango 0.0 a 1.0. A menudo, puede evitar tener que construir colores completamente, dado que la clase Color define algunas constantes que representan los colores normales: Color.white, Color.black, Color.red, Color.green, Color.blue, Color.cyan, Color.magenta, Color.yellow, Color.pink, Color.orange, Color.lightGray, Color.gray, y Color.darkGray.

Una de las variables instanciables del contexto gráfico, es el color para dibujar, que se emplea para todos los comando listados anteriormente (excepto drawImage). Si g es un contexto gráfico, puede cambiar el color actual de dibujo de g utilizando el método g.setColor(c), donde c es un Color. Por ejemplo, si usted quiere dibujar en verde, puede decir g.setColor(Color.green). El contexto gráfico continua usando el color hasta que se lo cambie de forma explícita. Si quiere saber cual es el color actual que se esta empleando para dibujar, puede llamar a la función g.getColor()que le devolverá un valor de tipo Color.

Cada componente tiene asociado un color de pluma (foreground color) y un color de fondo (background color).   Cuando se borra un componente por el método Update(), se hace rellenándolo con el color de fondo. Cuando se crea un nuevo contexto gráfico para un componente, El color de dibujo es igual al color de pluma. Puede establecer los colores de pluma y de fondo para os componentes, llamando a los métodos

void setForeground(Color c)

y

void setBackground(Color c)

Tenga en cuenta que estos métodos instanciables con de la clase Component y no de la clase Graphics. Entonces, en un applet, como es un tipo de componente, podrá decir simplemente setBackground(Color.white), y no g.setbackground(Color.white). si quisiera establecer el color de fondo del applet en blanco.


Fuentes y FontMetrics

Un fuente representa un tipo de texto determinado. El mismo carácter puede aparecer distinto en diferentes fuentes.  En Java los fuentes se caracterizan por un nombre, un estilo y un tamaño. La lista de nombres de fuentes disponibles, dependen del sistema, pero siempre puede usar los siguientes tres nombres: TimesRoman, Helvetica y Courier. El estilo del fuente es uno de los valores:

  • Font.PLAIN,
  • Font.ITALIC,
  • Font.BOLD, o
  • Font.BOLD + Font.ITALIC.

El tamaño del fuente es un numero entero. El rango típico para tamaños esta entre 10 y 36, sin embargo, también se pueden emplear tamaños mayores.El tamaño de un fuente, normalmente es el valor de la altura en pixeles del carácter mas grande, pero esto no es una regla segura.

Java tiene una clase llamada Font para representar los fuentes. Puede construir un nuevo fuente especificando su nombre, estilo y tamaño.

         Font plainFont = new Font("TimesRoman",Font.PLAIN,12);
         Font boldFont = new Font("Helvetica",Font.BOLD,12);

Un contexto gráfico tiene una fuente activa, que se usara en el método drawString(). Se puede establecer el fuente activo con el método setFont(). Por ejemplo, si g es un contexto gráfico y boldFont es un fuente, el comando g.setFont(boldFont) asignara como fuente activo para g boldFont. Por ejemplo, puede presentar un grande y destacado “Hola Mundo” diciendo:

         Font boldFont = new Font("Helvetica", Font.BOLD, 24);
         g.setFont(boldFont);
         g.drawString("Hola Mundo!", 20, 30);

Puede también averiguar el fuente que esta activo en un momento determinado con el método g.getFont(), que devuelve un objeto del tipo Font. Un contexto recién creado tiene establecido un fuente por defecto, que depende del sistema. Aveces, puede querer usar una versión modificada del mismo fuente. Hay métodos en la clase Font para hacerlo posible. Aquí tenemos un ejemplo:

          Font F = g.getFont();
          Font boldFont = new Font(F.getName(), Font.BOLD, F.getSize());
          Font bigFont = new Font(F.getName(), F.getStyle(), 2 * F.getSize());

Cuando dibuja una String, aveces necesita conocer cuanto espacio va a ocupar. Para hacer esto en Java, necesita un objeto de tipo FontMetrics, que contiene información acerca del tamaño que ocupara el texto dibujado con un fuente determinado. Si F es un fuente,  puede definir un objeto FontMetrics para ese fuente realizando una llamada g.getFontMetrics(F). Cuando tenga el objeto FontMetrics, FM,puede llamar FM.stringWidth(str) para saber el ancho de la string str cuando se dibuje con el fuente activo, así mismo FM.getHeight() define la distancia estándar entre dos líneas bases de dos líneas consecutivas de texto.


Imágenes

En Java, un objeto tipo Images es una fotografía que puede ser copiada a un contexto gráfico usando el método drawImage(). Java esta pensado para poder trabajar con fotografías que pueden ser descargadas por la red cuando sea necesario. Para potenciar esa posibilidad, el método drawImage() es un poco extraño. No necesita dibujar la imagen de forma inmediata. Si la imagen no está disponible,–por ejemplo si no ha sido descargada correctamente– el método drawImage() vuelve sin dibujarla y el sistema la dibujara mas tarde, cuando este disponible. Normalmente,solo tiene que llamar a drawImage() y dejar que el sistema haga el resto.

En algunos casos, no me interesa descargar las imágenes así. Puedo estar interesado en imágenes off-screen, que son imágenes que han de ser dibujadas usando un contexto gráfico. Puede crear tal imagen con el método createImage(), que esta definido en la clase Component (y por consiguiente esta disponible, sobre todo en applets). Lo que si tendrá que especificar es la anchura y la altura que quiera para la imagen . Para ello puede decir:

Image OSC = createImage(width,heigth);

Una vez haya creado una imagen off-screen, OSC, puede llamar al método getGraphics() para establecer el contexto gráfico para dibujar la imagen:

Graphics g = OSC.getGraphics();

Y una vez tiene g, Puede dibujar lo que quiera. Puede usar drawImage para copiar la imagen que ha creado, en la pantalla o en cualquier otro contexto gráfico.

En la técnica de doble-buffer, se construye una copia entera del componente como una imagen off-screen, Todas las operaciones de dibujo se realizan sobre esa copia. El método paint unicamente copia la imagen a la pantalla. A continuación les presento las líneas generales de como podria trabajar esto:

       Image OSC = null;  // el off-screen imagen para doble-buffering
Graphics OSC_g;    //contexto gráfico para OSC

public void update(Graphics g) {
// redefine la rutina para evita que borre el dibujo
paint(g);
}

public void paint(Graphics g) {
if (OSC != null) {  // por seguridad,
g.drawImage(OSC,0,0,this);   // copia la imagen a pantalla
}
}

void drawStuff() {
if (OSC == null) { // crea off-screen canvas, si no existe
OSC = createImage(size().width, size().height());
OSC_g = OSC.getGraphics();
OSC_g.setColor(Color.white);   // llena OSC con blanco
OSC_g.fillRect(0,0,size().width,size.height());
}
. . .  // draw to OSC_g
repaint();  // le indica al sistema que llame a paint
}
Para un applet, puede crear la imagen off-screen en el método init(). Otros componentes sin embargo, no los va a poder crear allí y tendrá que tener cuidado con crear la imagen off-screen antes de conocer el tamaño de los componentes.Y si quiere cambiar el tamaño de los componentes, entonces el trabajo será un poco mas duro.

El doble-buffer puede ser usado para mover las animaciones. Consigue que los gráficos tengan un aspecto muy profesional. Sin embargo, debe tener en cuenta que hacer esto come cantidades importantes de memoria y además penaliza el rendimiento del equipo por el tiempo que emplea en copiar la imagen a la pantalla. Se requiere experiencia y habilidad para saber como emplear bien el doble-buffer y otras técnicas gráficas. El mejor aviso es la practica. Escriba un pequeño applet de ejemplo, y vea lo que pasa.

Acerca de Miguel Garcia

Programador, Desarrollador web, Formador en distintas areas de informatica y director de equipos multidisciplinares.
Esta entrada fue publicada en Formacion, Java y etiquetada , , , . Guarda el enlace permanente.

4 respuestas a Java para programadores. (5.4):Los métodos Graphics y Paint

  1. javy66 dijo:

    muy interesante los metodos graficos utilizados para java de manera simplificada y detallada
    me a ayudado mucho en verdad muchas gracias.
    Saludos desde Ambato – Ecuador

  2. Pingback: Java para programadores (6.3):Eventos de ComponentRecursos para formacion

  3. Pingback: Java - Un resumen organizado....o no.Recursos para formacion

  4. Muchas gracias por la información muy buena y precisa por fin pude pintar sobre un JPanel 😀

Deja un comentario