next up previous contents index
Next: 29 Couleurs et Fontes Up: Java: Programmation graphique Previous: 27 Ranger les widgets

Subsections

28 Dessiner sur une fenêtre graphique

 

28.1 Introduction

La classe java.awt.Graphics fournit un ensemble de méthodes pour dessiner des formes géométriques, du texte ainsi que des images sur une fenêtre graphique:

 

   public abstract class Graphics {
      public Graphics()
      public void clearRect(int x, int y, int width, int height)
      public void clipRect(int x, int y, int width, int height)
      public void copyArea(int x, int y, int width, int height, int dx, int dy)
      public Graphics create()
      public Graphics create(int x, int y, int width, int height)
      public void dispose()
      public void draw3DRect(int x, int y, int width, int height, boolean raised)
      publicvoid drawArc(int x, int y, int width, int height, int startAngle, int arcAngle)
      public void drawBytes(byte[] data, int offset, int length, int x, int y)
      public void drawChars(char[] data, int offset, int length, int x, int y)
      public boolean drawImage(Image img, int x, int y, ImageObserver observer)
      public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)
      public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer)
      public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer)
      public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer)
      public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer)
      public void drawLine(int x1, int y1, int x2, int y2)
      public void drawOval(int x, int y, int width, int height)
      public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints)
      public void drawPolygon(Polygon p)
      public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints)
      public void drawRect(int x, int y, int width, int height)
      public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
      public void drawString(String str, int x, int y)
      public void fill3DRect(int x, int y, int width, int height, boolean raised)
      public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle)
      public void fillOval(int x, int y, int width, int height)
      public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints)
      public void fillPolygon(Polygon p)
      public void fillRect(int x, int y, int width, int height)
      public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
      public void finalize()
      public Shape getClip()
      public Rectangle getClipBounds()
      public Rectangle getClipBounds(Rectangle r)
      public Rectangle getClipRect()
      public Color getColor()
      public Font getFont()
      public FontMetrics getFontMetrics()
      public FontMetrics getFontMetrics(Font f)
      public boolean hitClip(int x, int y, int width, int height)
      public void setClip(int x, int y, int width, int height)
      public void setClip(Shape clip)
      public void setColor(Color c)
      public void setFont(Font font)
      public void setPaintMode()
      public void setXORMode(Color c1)
      public String toString()
      public void translate(int x, int y)
   }

Pour effetuer une opération graphique sur une composant, il nous faut disposer d'une instance de la classe Graphics. Cette instance est créée par la classe Component (qui crée un instance d'une classe dérivée de Graphics) et est passée en argument des méthode paint et update.

Chaque composant possède son propre système de coordonnées: le coin gauche supérieur est en (0,0) et le coin droit inférieur est en (width-1, height-1). L'échelle de mesure est en pixels.

Voici une exemple simple qui illustre l'utilisation des objets de type Graphics et de la méthode drawLine. Cette applet est une feuille de dessin simplifiée pour faire des dessins à main levée avec la souris.

 

public class Draw extends java.applet.Applet {
  int x=0, y=0;
  public Draw() {
    super();
    setBackground(java.awt.Color.cyan);
    addMouseListener(new java.awt.event.MouseAdapter() {
      public void mousePressed(java.awt.event.MouseEvent e) {
        x = e.getX(); y = e.getY();
      }
    });
    addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
      public void mouseDragged(java.awt.event.MouseEvent e) {
        java.awt.Graphics g = getGraphics();
        g.drawLine(x, y, e.getX(), e.getY());
        x = e.getX(); y = e.getY();
      }
    });
  }
}

Feuille de dessin
Votre browser ne peut exécuter les applets Java

28.2 Dessiner des formes géométriques

Des lignes

La méthode drawLine dessiner une ligne d'un pixel de large entre les points (x0, y0) et (x1, y1).

 

void drawLine(int x0, int y0, int x1, int y1)

Cette ligne n'est dessiné que dans les limites du composant sur lequel cette méthode s'applique. La couleur du dessin est désignée par le terme foregound color et peut être modifiée par la méthode setForeground.

Des rectangles

 

void drawRect(int x, int y, int w, int h)
void fillRect(int x, int y, int w, int h)
void drawRoundRect(int x, int y, int w, int h, int arcWidth, int arcHeight)
void fillRoundRect(int x, int y, int w, int h, int arcWidth, int arcHeight)
void draw3DRect(int x, int y, int w, int h, boolean raised)
void fill3DRect(int x, int y, int w, int h, boolean raised)

Des ovals et des arcs

 

void drawOval(int x, int y, int w, int h)
void fillOval(int x, int y, int w, int h)
void drawArc(int x, int y, int w, int h, int startAngle, int arcAngle)
void fillArc(int x, int y, int w, int h, int startAngle, int arcAngle)

Des polygônes

 

void drawPolygon(int xPoints[], int yPoints[], int nPoints)
void drawPolygon(Polygon p)
void fillPolygon(int xPoints[], int yPoints[], int nPoints)
void fillPolygon(Polygon p)

28.3 Les méthodes repaint, paint et update

Chaque fois qu'une application graphique a besoin de se dessiner ou se redessiner, le composant principal commence par se dessiner. Puis, il dessine un à un tous les composants qu'il contient et ce de manière récursive et sans aucune interruption.

Outre les moments ``naturels'' (démarrage de l'application et composants redevenus visibles), on peut demander à un composant de se redessiner par un appel explicite à l'une des méthodes repaint.

 

public void repaint()
public void repaint(long time)
public void repaint(int x, int y, int width, int height)
public void repaint(long time, int x, int y, int width, int height)

Lorsque l'application décide de se redessiner (automatiquement ou par un appel explicite à la méthode repaint), les composants se redessinent en faisant appel à la méthode update. La classe Component (classe mère de toutes les objets graphiques) fournit une implantation de la méthode update qui se consiste à

La méthode paint possède un argument qui est un objet de type java.awt.Graphics:

 

public void paint(java.awt.Graphics g) { ... }

et toutes opérations de dessin que l'on veut réaliser devra se faire sur cet objet.

 

g.drawString("Bonjour !", 10, 10);

Dans l'exemple qui suit, l'applet décide d'afficher une chaîne de caractères; il faut donc redéfinir la méthode paint.


 

public class Bonjour extends java.applet.Applet {
    public void paint(java.awt.Graphics g) {
        g.drawString("Bonjour", 50, 50) ;
    }
}

Reprenons l'exemple de la feuille de dessin [*]. Si l'on exécute ce programme, on voit apparaître une fenêtre dans un fond bleu turquoise (setBackground(java.awt.Color.cyan)) et à l'aide la souris on arrive à dessiner des formes.

A présent, si l'on cache cette fenêtre par une autre fenêtre ou qu'on ``l'iconifie'' et qu'on la fait fait réapparaître, tous nos dessins ont disparus. Par contre, le fond de la fenêtre est toujours bleu turquoise.

Que s'est-il passé ? N'ayant pas implanté la méthode paint, Java ne sait pas ce qu'il faut redessiner. Tout ce qu'il est capable de faire (comportement par défaut) consiste à redonner à la fenêtre l'apparence qu'il avait avant que l'on commence à dessiner: il a bien mis un fond bleu turquoise (et non pas gris comme tous les widgets par défaut).

Pour corriger ceci, il faut absolument définir la méthode paint pour que, à tout moment, on puisse être capable de refaire le dessin réalisé par l'utilisateur. Une manière simple (voire simpliste) consiste à mémoriser tous les points par lesquels la souris est passée. On pourra alors refaire le dessin réalisé par l'utilisateur chaque fois que le contenu de la fenêtre doit être redessiné.

On prendra une implantation simpliste : Deux tableaux d'entiers pouvant contenir les coordonnées des points; tableaux qu'on limitera à ne contenir qu'un millier de points.

 

public class Persistant extends java.applet.Applet {
  private final int maxpts = 1000;
  int [] x = new int[maxpts];
  int [] y = new int[maxpts];
  int sommet;
  public Persistant() {
    super();
    sommet = 0;
    setBackground(java.awt.Color.cyan);
    addMouseListener(new java.awt.event.MouseAdapter() {
      public void mousePressed(java.awt.event.MouseEvent e) {
        empiler(-1, -1);
        empiler(e.getX(), e.getY());
      }
      public void mouseReleased(java.awt.event.MouseEvent e) {
        java.awt.Graphics g = getGraphics();
        empiler(e.getX(), e.getY());
        System.out.println(x[sommet-2] + " " + y[sommet-2]  + " " + x[sommet-1]  + " " + y[sommet-1]);
        g.drawLine(x[sommet-2], y[sommet-2], x[sommet-1], y[sommet-1]);
      }
    });
    addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
      public void mouseDragged(java.awt.event.MouseEvent e) {
        java.awt.Graphics g = getGraphics();
        empiler(e.getX(), e.getY());
        g.drawLine(x[sommet-2], y[sommet-2], x[sommet-1], y[sommet-1]);
      }
    });
  }
  void empiler(int i, int j) {
    if (sommet < maxpts) { x[sommet] = i; y[sommet++] = j; }
  }
  public void paint(java.awt.Graphics g) {
    for (int i=0; i<sommet; )
      if (x[i]==-1) i+=2;
      else  { g.drawLine(x[i-1], y[i-1], x[i], y[i]); i++; }
  }
}

Votre browser ne peut exécuter les applets Java

28.4 Redessiner tout ou pas ?

Nous avons dit que la méthode paint est utilisée chaque fois que la méthode update; et elles le sont soit automatiquement par Java soit explicitement par le programmeur. La méthode update efface tout le contenu du widget et le redessine entièrement (en utilisant la méthode paint).

Dans beaucoup de cas, l'implanatation par défaut de la méthode update est suffisante. Mais il existe des cas où cette manière de faire peut conduire un appleffet de clignotement désagréable. Voici un exemple qui permet de visualiser cet effet de clignotement.

Votre browser ne peut exécuter les applets Java

 

import java.util.*;
import java.awt.*;
import java.applet.*;
import java.awt.event.*;



public class Cligno extends Applet implements Runnable, ActionListener {
  Thread t;
  Button b;
  Panel p;
  static public int crayon;
  static public int pas;
  public void init() {
    b = new Button("Demarrer"); p = new MonPanel(); add(b);
    add(p); b.addActionListener(this);
  }
  public void stop() { t.stop(); }
  public void run() {
    while (true) {
      crayon = (crayon + 10) % p.getSize().width;
      pas = (pas + 5) % p.getSize().width;;
      try {t.sleep(500);} 
      catch (InterruptedException e){}
      p.repaint();
    }
  }
  public void actionPerformed(ActionEvent e) {
    if ("Demarrer".equals(e.getActionCommand())) {
      b.setLabel("Arreter");
      if (t!=null) t.resume(); 
      else { t = new Thread(this); t.start(); } 
    } else { 
      b.setLabel("Demarrer"); 
      if (t != null) t.suspend(); 
    }
  }
}

class MonPanel extends Panel {
  int pave = 5;
  Font f = new Font("Courier", Font.BOLD, 24);
  static Color [] couleurs = {Color.black, Color.blue, Color.cyan,
                       Color.darkGray, Color.gray, Color.green,
                       Color.lightGray, Color.magenta, Color.orange,
                       Color.pink, Color.red, Color.white, Color.yellow};
  static Vector fond ;
  Dimension minSize = new Dimension(200, 200);
  public MonPanel() {
    fond = new Vector();
    for (int j=0; j<200; j+= pave)
      for (int i=0; i<200; i+=pave) 
	fond.addElement(new Color(i, j, (i+j)%256));
  }
  public boolean blanc = true;
  public Dimension getPreferredSize() { return getMinimumSize(); }
  public synchronized Dimension getMinimumSize() { return minSize; }
  public void paint(java.awt.Graphics g) {
    int c = 0;
    for (int j=0; j<200; j+= pave)
      for (int i=0; i<200; i+=pave) {
	g.setColor((Color)fond.elementAt(c++));
	g.fillRect(j,i, pave, pave);    
      }
    c = Cligno.crayon;
    g.setColor(couleurs[c++%couleurs.length]);
    g.setFont(f);
    g.drawString("Coucou !", Cligno.pas, Cligno.pas);
  }
}

Dans cet exemple, l'effet de clignotement provient du fait que on efface complètement le fond, qu'on le remplit avec la couleur de fond (Background ) fenêtre pour redessiner.

Or, le fond d'écran ne change pas; seul les emplacements des chaînes de caractères diffèrent à chaque rafraîchissement. Il est alors indiqué de restreindre la zone à redessiner à l'aide de la méthode repaint:

 

public void repaint(int x, int y, int width, int height)

Votre browser ne peut exécuter les applets Java

 

import java.awt.*;
import java.applet.*;
import java.awt.event.*;

public class NonCligno extends Applet implements Runnable, ActionListener {
  Thread t;
  Button b;
  Panel p;
  static public int blanc;
  public void init() {
    b = new Button("Demarrer"); add(b); b.addActionListener(this);
    p = new MonPanel(); add(p);
  }
  public void stop() { t.stop(); }
  public void run() {
    while (true) {
      int x = blanc;
      blanc = (blanc + 10) % p.getSize().width;
      try {t.sleep(300);} 
      catch (InterruptedException e){}
      p.repaint(x, x, 70, 30);
    }
  }
  public void actionPerformed(ActionEvent e) {
    if ("Demarrer".equals(e.getActionCommand())) {
      b.setLabel("Arreter");
      if (t!=null) t.resume(); 
      else { t = new Thread(this); t.start(); } 
    } else { 
      b.setLabel("Demarrer"); 
      if (t != null) t.suspend(); 
    }
  }
}

class MonPanel extends Panel {
  Dimension minSize = new Dimension(200, 200);;
  public boolean blanc = true;
  public Dimension getPreferredSize() { return getMinimumSize(); }
  public synchronized Dimension getMinimumSize() { return minSize; }
  public void paint(java.awt.Graphics g) {
    for (int j=0; j<200; j+= 20)
      for (int i=0; i<200; i+=20) {
	g.setColor(new Color(i, j, 25)); g.fillRect(j,i, 20, 20);    
      }
    g.drawString("Coucou !", NonCligno.blanc, NonCligno.blanc);
  }
}

Cette techniwue qui consiste à restreindre à la zone de rafraîssissement est souvent appelé cliping .

28.5 Redéfinir la méthode update

L'exemple précédant est parfaitement adapté pour l'utilisation du cliping . Est-on réduit, dans les cas ou le clipping ne se prête pas, à supporter le clignotement ?

Pas forcément ! Comme on l'a déjà dit, le clignotement est dû à l'implantation par défaut de la méthode update. Une autre manière d'éviter le clignotment, consiste à redéfinir la méthode update pour l'empêcher de réinitialiser la zone à afficher avant l'appel à la méthode paint.

 

public void update(Graphics g) {
  paint(g);
}

Votre browser ne peut exécuter les applets Java

28.6 Le ``double buffering''

 

On diminue encore le clignotement en utilisant la technique du double buffering. Il s'agit de conserver une copie de l'image visible en mémoire.

A TERMIER

 

Image offScreenImage;
...
public void update(java.awt.Graphics g) {
  if (offScreenImage == null)
    offScreenImage = createImage(getSize().width, getSize().height);
  Graphics offGr = offScreenImage.getGraphics();
  paint(offGr);
  g.drawImage(offScreenImage, 0, 0, this);
}

Votre browser ne peut exécuter les applets Java

next up previous contents index
Next: 29 Couleurs et Fontes Up: Java: Programmation graphique Previous: 27 Ranger les widgets
Touraivane
6/12/1998