begin process at 2012 02 15 19:52:00
  Trouver un code source :
 
dans
 
Accueil > 

Tutoriels

 > 

Swing

 > DESSINER AVEC SWING (1): LE MÉCANISME DE BASE

DESSINER AVEC SWING (1): LE MÉCANISME DE BASE


 Information sur le tutoriel

Note :
Aucune note

 Description

Cette première partie montre l'utilisation correcte des méthodes "paintComponent" et "repaint" dans une application Swing.

Tutorial

La méthode paintComponent

Pour dessiner dans Swing, le système utilise un mécanisme de "callback" ("visite répétée"). Cela signifie, qu'un programme doit mettre le code d'affichage du composant à l'intérieur d'une certaine méthode surchargée, et le Toolkit appelle cette méthode quand il est temps de dessiner. La méthode à surcharger est dans javax.swing.JComponent:

protected void paintComponent(Graphics g)

Quand le système appelle cette méthode, le paramètre Graphics g est préconfiguré avec l'état adéquat pour le dessin sur ce composant précis:

    * La couleur de l'objet Graphics est préconfigurée suivant la propriété "foreground" du composant.
    * L'écriture de l'objet Graphics est préconfigurée suivant la propriété "font" du composant.
    * La "translation" de l'objet Graphics est préconfigurée de sorte que la coordonné (0,0) représente le coin supérieur gauche du composant.
    * Le rectangle "clip" de l'objet Graphics est préconfigurée suivant la zone du composant qui doit être redessinée.

Les programmes doivent obligatoirement utiliser cet objet Graphics (ou un dérivant) pour dessiner la surface graphique, mais ils sont libres de modifier l'état de l'objet Graphics suivant leurs besoins. Il est util de savoir que l'objet Graphics dans paintComponent est en réalité un Graphics2D, une extension de Graphics avec des améliorations concernant la gestion de la géométrie, des coordonnés, des couleurs et des textes. Graphics2D est la classe de base pour l'affichage de formes 2D, textes et images sur la plateforme Java. Pour pouvoir l'utiliser dans paintComponent, nous devons convertir l'objet Graphics vers Graphics2D:

Graphics2D g2d = (Graphics)g;

En général, les programmes doivent éviter de mettre du code de dessin à un endroit où il peut être appelé de l'extérieur du domaine de la méthode paintComponent. Pourquoi? Parce qu'un tel code peut parfois être appelé quand il n'est pas opportun de dessiner -- par exemple avant que le composant soit visible ou avant qu'il a accès à un objet Graphics valable. Il n'est pas recommandé que les programmes appellent paintComponent() directement.

Dans un processus de dessin déclenché par le système, le système demande au composant d'afficher son contenu, normalement pour une des causes suivantes:

    * Le composant est affiché à l'écran pour la première fois.
    * Le composant est modifié dans sa dimension.
    * Le composant a été endommagé et doit être réparé (par exemple une fenêtre qui cachait le composant a été déplacée, et la partie cachée du composant devient visible).

Dans les trois cas, le système appelle automatiquement la méthode paintComponent du composant concerné.

L'exemple de programmation SwingPaintDemo montre l'utilisation simple de la méthode "callback" paintComponent() de Swing:

import java.awt.*;
import javax.swing.*;

/*
 ***************************************************************
 * Silly Sample program which demonstrates the basic paint
 * mechanism for Swing components.
 ***************************************************************
 */
public class SwingPaintDemo {

    public static void main(final String[] args) {
        Runnable gui = new Runnable() {

            @Override
            public void run() {
                JFrame f = new JFrame("Aim For the Center");
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                Container panel = new BullsEyePanel();
                panel.add(new JLabel("BullsEye!", SwingConstants.CENTER), BorderLayout.CENTER);
                f.getContentPane().add(panel, BorderLayout.CENTER);
                f.pack();
                f.setLocationRelativeTo(null);
                f.setVisible(true);

            }
        };
        //GUI must start on EventDispatchThread:
        SwingUtilities.invokeLater(gui);
    }
}

/**
 * A Swing container that renders a bullseye background
 * where the area around the bullseye is transparent.
 */
class BullsEyePanel extends JPanel {

    public BullsEyePanel() {
        super();
        setOpaque(false); // we don't paint all our bits
        setLayout(new BorderLayout());
        setBorder(BorderFactory.createLineBorder(Color.black));
    }

    @Override
    public Dimension getPreferredSize() {
        // Figure out what the layout manager needs and
        // then add 100 to the largest of the dimensions
        // in order to enforce a 'round' bullseye
        Dimension layoutSize = super.getPreferredSize();
        int max = Math.max(layoutSize.width, layoutSize.height);
        return new Dimension(max + 200, max + 200);
    }

    @Override
    protected void paintComponent(final Graphics g) {
        super.paintComponent(g);
        Dimension size = getSize();
        int x = 0;
        int y = 0;
        int i = 0;
        while (x < size.width && y < size.height) {
            g.setColor(i % 2 == 0 ? Color.RED : Color.WHITE);
            g.fillOval(x, y, size.width - (2 * x), size.height - (2 * y));
            x += 10;
            y += 10;
            i++;
        }
    }
}

La méthode repaint

Dans le cas d'un processus de dessin déclenché par l'application, c'est le composant qui décide qu'il doit actualiser son contenu, parceque son état interne s'est modifié. Exemple: un bouton remarque que la souris a été actionnée et décide qu'il doit dessiner un bouton "enfoncé".

Pour permettre aux applications de déclencher un processus de dessin, le Toolkit fournit la méthode java.awt.Component suivante, pourque les programmes puissent demander un processus asynchrone de dessin:

    public void repaint()

L'appelle de la méthode repaint déclenche indirectement l'appelle de la méthode paintComponent du composant concerné, mais à un moment qui est jugé le meilleur par le système. On peut en déduire, que la méthode repaint ne doit jamais être appelée par la méthode paintComponent elle-même!

Le code suivant montre un exemple simple d'un MouseListener qui utilise repaint() pour déclencher des mises à jour sur un bouton théorique, quand on presse et relâche la souris:

        MouseListener l = new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                MyButton b = (MyButton)e.getSource();
                b.setSelected(true);
                b.repaint();
            }
 
            public void mouseReleased(MouseEvent e) {
                MyButton b = (MyButton)e.getSource();
                b.setSelected(false);
                b.repaint();
            }
        };

La méthode MyButton#setSelected positionne la variable d'instance "selected" qui est utilisée par paintComponent pour savoir ce qu'il faut dessiner.

Dans le cas où paintComponent a besoin d'autres variables ou objets pour dessiner, nous pouvons les transmettre au composant par les paramètres du constructeur, par des méthodes "setter" ou des méthodes semblables. Les références sont alors mémorisées dans des variables d'instance, pourque paintComponent puisse les utiliser sans problème. Le composant suivant utilise p.ex. un objet d'une classe "Animal" (non montrée). L'objet est transmis au composant par le constructeur ou à l'aide de la méthode "setAnimal(..)":

class Cage extends JComponent {

private Animal animal;

public Cage(final Animal animal) {

this.animal = animal;

}

@Override

protected void paintComponent(final Graphics g) {

super.paintComponent(g);

g.setColor(Color.BLACK);

if (animal.getSpecies().equals(Animal.LION)) {

g.setColor(Color.RED);

}

if (animal.getSpecies().equals(Animal.TIGER)) {

g.setColor(Color.GREEN);

}

g.fillOval(animal.getCoord().x, animal.getCoord().y, 10, 10);

}

public void setAnimal(final Animal animal) {

this.animal = animal;

}

}

//Exemple pour la transmission de l'objet:

Cage cage = new Cage(new Animal(Animal.LION));// par le constructeur

//...

cage.setAnimal(new Animal(Animal.TIGER));// à l'aide de la méthode setAnimal(..)

L'appel de repaint sur le composant a pour effet l'appel de la méthode paintComponent, qui se charge de la représentation correcte de l'objet "Animal" actualisé:

cage.repaint();

 

La méthode repaint avec arguments


Les composants qui affichent des représentations graphiques compliquées doivent appeler repaint() avec les arguments qui définissent uniquement la zone à actualiser:

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

Ces arguments servent à définir le rectangle "clip", qui représente la zone à actualiser dans l'objet Graphics de la méthode paintComponent.

C'est une erreur générale d'utiliser toujours la version sans arguments, qui redessine toujours le composant dans son intégralité, ce qui conduit souvent à des processus de dessin non nécessaires. Un exemple pour l'utilisation de repaint() avec arguments est introduit dans la seconde partie de ce tutoriel: Dessiner avec Swing (2): un programme de dessin simple

 Historique

01 mai 2009 19:39:13 :
orthographe
01 mai 2009 19:40:25 :
orthographe
01 mai 2009 19:42:26 :
orthographe
01 mai 2009 20:34:08 :
orthographe
01 mai 2009 20:42:09 :
titre
02 mai 2009 10:13:38 :
orthographe
03 mai 2009 22:57:41 :
Graphics2D
17 juin 2009 22:09:30 :
Ajout d'un exemple
17 juin 2009 22:21:22 :
Ajout d'un exemple
17 juin 2009 22:29:54 :
ajout d'un exemple

Commentaires

Commentaire de TheVirus le 17/07/2009 22:51:52

Merci pour ce tutoriel.
Je travaille ces jours là sur un petit projet, non pas de Dessin mais d'animation, et j'ai une petite question:
Maintenant que je peux dessiner, est ce que l'animation se fait en dessinant plusieurs images l'une après l'autre ou il y a une possibilité de définir des objets et d'appliquer des méthodes d'animation?

Commentaire de uhrand le 18/07/2009 17:50:13

En gros il y a deux approches pour dessiner un objet à animer:
1. Dessiner sur un JComponent à part que l'on ajoute au composant de base.
2. Dessiner directement sur le composant de base.
Pour déplacer l'objet on utilise dans le premier cas la méthode "Component#setLocation" et dans l'autre cas on fait varier les coordonnées avant d'appeler "repaint". Cette opération d'animation doit se faire dans un Thread à part. Une façon facile pour réaliser ce Thread est d'utiliser un "javax.swing.Timer".

Commentaire de TheVirus le 18/07/2009 22:09:43

Merci beaucoup,
J'ai pas pensé à la création d'un Thread à part, et en plus il y a l'objet Timer, merci beaucoup pour l'info.

Commentaire de yamql le 28/11/2009 02:22:48

Merci beaucoup,

Commentaire de mmigo le 20/05/2010 21:47:09

merciiii

Commentaire de Zephyr11 le 14/11/2010 15:24:45

Très clair et bien utile, de même pour la seconde partie.
Merci beaucoup.
Il me reste à trouver où pouvoir surcharger paintComponent() dans le code généré par NetBeans, mais c'est une autre histoire... :-)

Commentaire de uhrand le 15/11/2010 05:33:19

Salut Zephyr11,

pour modifier le bloc protégé d'un élément de formulaire de NetBeans:

1. Dans la fenêtre Inspecteur, sélectionnez le composant dont vous souhaitez modifier le code d'initialisation.
2. Cliquez sur le bouton "Code" en haut de la fenêtre Propriétés pour afficher les propriétés "Code".
3. Sélectionnez la propriété que vous souhaitez modifier et entrez la valeur désirée.
NetBeans met à jour le bloc protégé du composant sélectionné par la nouvelle valeur.

Par exemple, pour modifier le code création d'un JPanel, on peut saisir la valeur suivante pour la propriété "Custom Creation Code":
[code]
new JPanel(){
    public Dimension getPreferredSize(){
        Dimension size = super.getPreferredSize();
        size.width = 100;
        size.height = 100;
        return size;
    }
    protected void paintComponent(Graphics g){
        super.paintComponent(g);
        g.drawOval(10,10,10,10);
    }
}
[/code]

Salutations,
André

 Ajouter un commentaire




Nos sponsors


Sondage...

CalendriCode

Février 2012
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
272829    

Consulter la suite du CalendriCode

 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 0,452 sec (3)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales