# Programmation Java Swing/Créer un composant

* Introduction
* Composants
** Arbre
* Fenêtres
* Menus
* Actions
* Types de données et constantes
* Gestion de la disposition des composants
* Gestion des évènements
* Gestion des actions asynchrones
* Apparence de l'interface
* Contenu en HTML
* Créer un composant
** Une case à cocher à trois états
** Dessin d'un composant
** Dessin d'une étoile
** Dessin de texte et effets
* Créer une image
* Introduction
* Composants
** Arbre
* Fenêtres
* Menus
* Actions
* Types de données et constantes
* Gestion de la disposition des composants
* Gestion des évènements
* Gestion des actions asynchrones
* Apparence de l'interface
* Contenu en HTML
* Créer un composant
** Une case à cocher à trois états
** Dessin d'un composant
** Dessin d'une étoile
** Dessin de texte et effets
* Créer une image
[ Modifier le sommaire ]
La bibliothèque Swing propose une riche collection de composants répondant aux besoins les plus courants concernant la création d'une interface graphique. Cependant, certaines applications peuvent avoir besoin de composants spécifiques, qu'il soit basé sur un composant existant (comme une case à cocher à trois états par exemple), ou qu'il ne soit basé sur aucun composant existant. C'est par exemple le cas d'un feu tricolore utilisé dans un jeu sur la circulation routière, qui sera développé dans ce chapitre par étapes.

## Création de la classe

La première version de la classe ci-dessous définit les couleurs utilisées et l'état courant du feu tricolore. La méthode statique principale crée une fenêtre pour tester le composant. Le composant n'est pas visible pour le moment avec cette première version du code.

## État du composant

L'état du composant est représenté par une énumération de type EtatFeu
On ajoute également des accesseurs permettant de lire et écrire l'état courant du feu :
La méthode de changement d'état appelle la méthode repaint pour mettre à jour l'affichage aussitôt.

## Dessin du composant

Le dessin d'un composant Swing est effectué par la méthode publique void paint(Graphics g). Cette méthode délègue le travail à trois autres méthodes protégées appelées dans l'ordre suivant :

### void paintComponent(Graphics g)

    Affiche le contenu du composant lui-même.

### void paintBorder(Graphics g)

    Affiche les bords du composant dans la zone définie par les marges de type Insets.

### void paintChildren(Graphics g)

    Affiche les composants contenus dans celui-ci, par appel à leur méthode paint.
Cet ordre assure que les composants contenus soit affichés au-dessus du composant. Pour définir l'apparence du composant, une sous-classe ne doit redéfinir que la méthode void paintComponent(Graphics g). Celle-ci doit éviter de dessiner dans la zone définie par les marges de type Insets qui est réservée aux bords (Border).
Il existe aussi une méthode void paintComponents(Graphics g) permettant de redessiner les composants contenus, au lieu du composant. Si le composant n'affiche rien, vérifiez qu'il n'y a pas de 's' à la fin du nom de la méthode. Cette erreur peut arriver facilement avec un IDE proposant la complétion automatique.
Le code ci-dessous ajoute la méthode de dessin du feu tricolore. Elle utilise un tableau à deux dimensions définissant la couleur de chaque feu pour chaque état possible.

## Gestion des évènements

Dans l'état actuel du code, le feu affiche l'état courant initial éteint. Cette section définit le code pour changer l'état courant sur réception d'un évènement de clic de souris.
On ajoute la méthode qui met l'état suivant selon l'état courant :
Cette méthode est appelée par le gestionnaire de clic de souris ajouté au composant dans le constructeur de la classe :

## Améliorations

La classe peut être améliorée sur divers points abordés dans cette section.

### Changement d'état

Afin que le feu ait un comportement plus proche de la réalité, le changement d'état du feu devrait se faire sans intervention de l'utilisateur, au bout d'un certain temps dépendant de l'état courant.
Tout d'abord, afin d'éviter de réafficher le composant trop souvent, la méthode de changement d'état est modifiée comme ci-dessous :
On supprime donc le changement d'état par clic de souris, c'est à dire le code ajouté dans la section « Gestion des évènements » :
* Supprimer la méthode cycleEtatCourant,
* Et supprimer l'appel à la méthode addMouseListener dans le constructeur.
La gestion d'un croisement implique plusieurs feux, affectés à différentes voies de circulation (à double sens ou non). Il est donc préférable que le changement d'état d'un feu soit géré par une nouvelle classe afin de synchroniser les feux du carrefour. Il faut donc déplacer l'énumération dans une classe à part :
Ensuite, il faut créer une nouvelle classe qui sera nommée GestionnaireFeu, qui va gérer une liste de feux par voie de circulation, définir la voie de circulation active (cycle rouge-vert-orange-rouge) les autres étant inactives (feu rouge), et définir un thread mettant à jour le cycle courant :
La méthode statique de lancement permettant de montrer trois feux sur trois voies de circulation différentes est la suivante :
Les cycles des feux sur les trois voies pour les temps par défaut sont résumés dans le tableau ci-dessous :
* Voie 1
* Voie 2
* Voie 3

### Qualité de l'affichage du composant

Le dessin circulaire du composant montre un crénelage causé par le tracé pixel par pixel. Il est possible d'améliorer l'affichage en utilisant l'antialiasing, en modifiant légèrement le code.
Tout d'abord, il faut définir les propriétés de rendu dans un dictionnaire utilisant des clés de la classe RenderingHints. La partie de code ci-dessous est à ajouter dans la classe FeuTricolore, et montre les clés modifiables. Celles sur l'affichage du texte (KEY_TEXT_ANTIALIASING, KEY_FRACTIONALMETRICS) ne sont pas nécessaires pour cette classe mais illustrent les possibilités.
On peut aussi améliorer le tracé en utilisant une plus grande épaisseur de trait. Les paramètres de trait sont gérés par la classe abstraite Stroke, dont la classe BasicStroke est une implémentation.
Il ne reste plus qu'à utiliser ces éléments dans la méthode paintComponent. Cependant ils ne sont utilisables qu'avec une instance de la classe Graphics2D, qui est la classe réelle de l'objet Graphics passé à la méthode. Il faut donc convertir l'objet pour définir les options de rendu et le style des traits.

### Préservation du contexte graphique

La méthode paint étant implémentée par appels à d'autres méthodes, elle leur passe le même contexte graphique de classe Graphics2D. Cela signifie que le dernier changement de paramètre (couleur, police de caractère, style de trait, région clip, ...) est celui par défaut pour la méthode appelée après. Il est donc recommandé de restaurer les valeurs d'origine avant de terminer. Cependant, cela implique de lire la valeur au début de la méthode pour la restaurer à la fin, ce qui complique le codage vu le nombre possible de paramètres auquel cela s'applique.
La solution est de créer un contexte dérivé de celui passé en paramètre, et de ne plus utiliser l'original ensuite. Il faudra cependant libérer les ressources prises par le nouveau contexte en appelant sa méthode dispose à la fin.

### Orientation du composant

Le feu tricolore utilisé dans une simulation de circulation réaliste avec vue de dessus d'un carrefour devrait pouvoir être positionné horizontalement de haut en bas et inversement, ou verticalement de droite à gauche et inversement.

## Génération d'évènements spécifiques

Dans une utilisation réaliste du composant, le feu tricolore utilisé pour simuler la circulation devrait être capable de notifier ses changements d'états aux automobilistes pour qu'ils puissent s'arrêter ou redémarrer.