# Programmation Java Swing/Version imprimable

* Si vous imprimez cette page, choisissez « Aperçu avant impression » dans votre navigateur, ou cliquez sur le lien Version imprimable dans la boîte à outils, vous verrez cette page sans ce message, ni éléments de navigation sur la gauche ou en haut.
* Cliquez sur Rafraîchir cette page pour obtenir la dernière version du wikilivre.
* Pour plus d'informations sur les version imprimables, y compris la manière d'obtenir une version PDF, vous pouvez lire l'article Versions imprimables.
Une version à jour et éditable de ce livre est disponible sur Wikilivres, une bibliothèque de livres pédagogiques, à l'URL : https://fr.wikibooks.org/wiki/Programmation_Java_Swing

# Introduction
## Principes

La bibliothèque de composants Swing est définie dans le package javax.swing et utilise AWT (java.awt) :
* La gestion des évènements est celle des composants AWT basée sur les mêmes écouteurs d'évènements.
* Les types Color, Font, Dimension, Insets... du package java.awt sont utilisés par Swing également.
* Les gestionnaires de disposition des composants définis par AWT sont également utilisés par les conteneurs Swing.
* Les composants Swing dérivent tous du composant de base défini par la classe javax.swing.JComponent. Cette classe dérive de la classe java.awt.Container. Cela signifie que tout composant Swing peut contenir d'autres composants. Cependant, le conteneur privilégié est le panel défini par la classe javax.swing.JPanel.
Le nom des classes Swing reprend le nom de la classe équivalente AWT préfixé par la lettre J : JComponent, JContainer, JPanel, JFrame...

## Fenêtres simples

La fenêtre la plus simple est une classe vide héritant de la classe javax.swing.JFrame qui est vide par défaut.
Pour les opérations de mise à jour graphiques il vaut mieux utiliser le thread dédié (thread de répartition des évènements graphiques) pour éviter les problèmes de synchronisation, comme illustré par la méthode statique main ci-dessous pour créer et afficher la fenêtre :
Pour tester cet exemple :
* Copiez le code source dans un fichier nommé FenetreSimple.java dans une hiérarchie de répertoire org/wikibooks/fr/swing pour le paquetage déclaré ;
* Compilez-le avec la commande
    javac org.wikibooks.fr.swing.FenetreSimple
* Lancez le programme avec la commande
    java -cp . org.wikibooks.fr.swing.FenetreSimple
    ou comme il n'est pas nécessaire d'avoir une console :
    javaw -cp . org.wikibooks.fr.swing.FenetreSimple
La fenêtre ne comporte pas de titre, de taille réduite et située en haut à gauche, le bouton de fermeture permet toutefois de fermer la fenêtre. Cependant, l'application tourne encore. On s'en aperçoit en lançant l'application avec un IDE (tel Eclipse). Le thread de répartition des évènements graphiques continue de tourner.
Pour que l'application s'arrête (fin de tous les threads), il est nécessaire d'ajouter un écouteur d'évènement sur la fermeture de la fenêtre. L'exemple ci-dessous montre cela et comment définir un titre, la taille et la position de la fenêtre dans le constructeur de la classe. La méthode main de lancement de l'application reste la même :

# Composants

De manière générale, un composant dans une interface graphique possède une zone rectangulaire permettant d'afficher une ou plusieurs informations et permet à l'utilisateur d'interagir avec lui en utilisant la souris ou le clavier.

## Divers composants

Cette section liste brièvement quelques composants Swing décrits plus en détails dans les sections suivantes. Les composants Swing sont nombreux, seuls les plus courants seront décrits ici.

### JLabel

    Ce composant permet d'afficher du texte et une icône.

### JButton

    Ce composant (bouton) permet de déclencher une action par un clic de souris.

### JToggleButton

    Bouton basculant entre deux états.

### JTextfield

    Un champ de saisie de texte simple.

### JPasswordField

    Un champ de saisie de mot de passe dont les caractères sont cachés.

### JTextArea

    Une zone de saisie de texte.

### JTextPane

    Une zone d'affichage ou de saisie de texte formaté.

### JCheckBox

    Une case à cocher.

### JRadioButton

    Un item sélectionnable parmi un groupe.

### JComboBox

    Une liste déroulante de choix (un seul choix possible).

### JList

    Une liste d'items, un ou plusieurs items sélectionnable.

### JSpinner

    Champ de saisie avec boutons d'incrémentation et de décrémentation (nombre, items dans une liste prédéfinie, ...).

### JSlider

    Sélection d'une valeur numérique par glissement d'un curseur sur une ligne.

### JProgressBar

    Barre de progression.

### JTree

    Arborescence de nœuds pour les données structurées.

### JTable

    Table de données.

### JScrollbar

    Une barre de défilement.

### JPanel

    Un conteneur générique.

### JScrollPane

    Un conteneur permettant de faire défiler le contenu du composant (la vue) lorsque sa taille dépasse les limites du conteneur.

### JSplitPane

    Un conteneur avec deux composants séparés par un mince composant diviseur dont la position est réglable à la souris. Ce diviseur contient par défaut deux boutons permettant de cacher l'un des deux composants pour afficher l'autre sur toute la surface du conteneur.

## Architecture logicielle

Les composants Swing utilisent l'architecture MVC (modèle vue contrôleur) :
* La vue (view en anglais) contient la présentation de l'interface graphique.
    La vue est gérée par le composant Swing, par exemple : JTable, JTree...
* Le modèle (model en anglais) contient et gère les données à afficher.
    Le modèle est associé au composant : JTableModel, JTreeModel...
* Le contrôleur (controller en anglais) contient la logique concernant les actions effectuées par l'utilisateur.
    Le rôle du contrôleur est réparti entre le code du composant définissant un comportement interne et les écouteurs d'évènements définis par l'application.

## Taille et position

En général et par défaut, la taille et la position des composants dans un conteneur sont définies par un gestionnaire de disposition des composants. Elles ne peuvent donc être modifiées qu'en cas d'absence de gestionnaire de disposition (setLayout(null)). Cependant, il est recommandé d'utiliser l'un des gestionnaires de disposition des composants pré-existants.

### Position

Comme en AWT, la position d'un composant est définie par deux coordonnées x et y, qui peuvent être représentées par un objet de classe java.awt.Point. Cette classe comporte deux champs publics x et y de type entier (int).
| void setLocation(int x, int y) | Cette méthode définit les coordonnées horizontales et verticales (respectivement) du coin supérieur gauche du composant. Les coordonnées sont relatives au conteneur ; c'est à dire que (0,0) correspond au bord supérieur gauche du conteneur, éventuellement espacé de la largeur du bord du conteneur. |
| void setLocation(Point p) | Cette méthode définit les coordonnées en utilisant un objet de classe java.awt.Point. |
| Point getLocation | Cette méthode retourne la position du composant. |

### Taille

La taille d'un composant est définie par la largeur (width en anglais) et la hauteur (height en anglais), qui peuvent être représentées par un objet de classe java.awt.Dimension. Cette classe comporte deux champs publics width et height de type entier (int).
| void setSize(int width, int height) | Cette méthode définit la taille du composant. |
| void setLocation(Dimension size) | Cette méthode définit la taille en utilisant un objet de classe java.awt.Dimension. |
| Dimension getSize | Cette méthode retourne la taille du composant. |
Utiliser un objet au lieu de spécifier directement les valeurs permet de réutiliser les mêmes valeurs pour d'autres composants, ou pour définir la taille minimales, maximales et préférées d'un composants, ou de réutiliser les valeurs retournées par un composant.

### Taille et position

La taille et la position peuvent être définies en une fois en utilisant les méthodes ci-dessous, soit en spécifiant les 4 valeurs, soit en utilisant un objet de classe java.awt.Rectangle. Cette classe comporte quatre champs publics de type entier (int) : x et y pour la position, et width et height pour la taille.
| void setBounds(int x, int y, int width, int height) | Cette méthode définit la taille et la position du composant. |
| void setBounds(Rectangle bounds) | Cette méthode définit la taille et la position en utilisant un objet de classe java.awt.Rectangle. |
| Rectangle getBounds | Cette méthode retourne la taille et la position du composant. |

## Propriétés d'apparence

Certaines propriétés communes aux composants changent leur apparence :

### void setBackground(Color c) / Color getBackground

    Couleur de fond du composant.

### void setForeground(Color c) / Color getForeground

    Couleur de premier plan, utilisée pour le texte.

### void setFont(Font f) / Font getFont

    Police de caractères par défaut pour le texte.

### void setText(String s) / String getText

    Texte affiché (label, bouton, champ de saisie, ...).

### void setBorder(Border b) / Border getBorder

    Ajoute un espace autour du composant pour y dessiner une bordure. La classe BorderFactory définit des méthodes statiques permettant de créer des bordures.

### void setTooltipText(String s) / String getTooltipText

    Texte affiché temporairement au survol de la souris sur le composant.

## Propriétés de comportement (état)

* La méthode void setVisible(boolean b) permet de changer la visibilité du composant. La méthode boolean isVisible retourne l'état de visibilité du composant.
* La méthode void setEnabled(boolean b) permet de désactiver/griser (false) ou (ré)activer (true) un composant. La méthode boolean isEnabled retourne l'état d'activation du composant.
* La méthode void setEditable(boolean b) permet de désactiver (false) ou (ré)activer (true) la modification de la valeur dans un champ de saisie dérivant de la classe JTextComponent (classes JTextArea, JTextField, JFormattedTextField, JPasswordField, JEditorPane, JTextPane, ...). La méthode boolean isEditable retourne l'état d'activation de modification de la valeur.
Suite à la réactivation d'un champ texte, soit par la méthode void setEnabled(true), soit par la méthode void setEditable(true), il peut arriver que le curseur n'apparaisse pas, même quand le focus est sur le champ. La solution, après réactivation, est de rendre visible le curseur explicitement :

## Composants

Pour tester rapidement les composants des sections qui suivent, voici un code source Java créant une fenêtre de test.

## JLabel

Ce composant permet d'afficher du texte et une icône.
Dans la fenêtre de test, une largeur de 600 pixels ne suffit pas à afficher tout le texte. Le composant coupe le texte avant 600 pixels et affiche des points de suspension.
Pour afficher un texte sur plusieurs lignes, l'utilisation de caractère de retour à la ligne \n ou \r n'ont aucun effet. Il faut utiliser un format HTML :
Le constructeur a trois paramètres optionnels : JLabel(String text, Icon icon, int horizontalAlignment).

### text

    Texte affiché, aucun si absent.

### icon

    Image affichée, aucune si absent. Si le texte est aussi spécifié, l'image est à gauche du texte par défaut.
    La classe ImageIcon est une sous-classe Icon et définit l'icône à partir d'une image de classe Image.

### horizontalAlignment

    Alignement du contenu :
   * SwingConstants.LEFT // À gauche
   * SwingConstants.CENTER // Centré
   * SwingConstants.RIGHT // À droite
   * SwingConstants.LEADING // En début de ligne (gauche / droite)
   * SwingConstants.TRAILING // En fin de ligne (droite / gauche)
Quand un texte et une image sont affichés, l'image est à gauche du texte par défaut. La méthode setHorizontalTextPosition(int textPosition) permet de choisir la position du texte relativement à l'image :
   * SwingConstants.LEFT // À gauche
   * SwingConstants.CENTER // Centré
   * SwingConstants.RIGHT // À droite
   * SwingConstants.LEADING // Du côté du début de ligne (gauche / droite)
   * SwingConstants.TRAILING // Du côté de la fin de ligne (droite / gauche)

### Label activant un composant

Un label peut être utilisé pour activer le composant associé. Par exemple, mettre le focus sur un champ de saisie quand l'utilisateur tape la touche mnémonique associée au label.

#### setDisplayedMnemonic(int key)

    Spécifier un code de touche mnémonique à afficher.

#### setLabelFor(Component c)

    Spécifier le composant associé au label.

## JButton

Ce composant permet de déclencher une action par un clic de souris.

## JTextfield

Un champ de saisie de texte simple. La classe JTextfield hérite de JTextComponent définissant les méthodes pour les champs de saisie.

## JPasswordField

Un champ permettant la saisie d'un mot de passe, cachant les caractères tapés. La classe JPasswordField hérite de JTextComponent définissant les méthodes pour les champs de saisie. Cette classe possède donc les mêmes méthodes que JTextfield.

## JTextArea

Une zone de saisie de texte.

## JTextPane

Une zone d'affichage ou de saisie de texte formaté.

## JCheckBox

Une case à cocher.
Ce composant gère deux états, mais pas d'état intermédiaire (coché partiellement ou indéterminé). Mais il est possible de créer un composant ayant ce troisième état : voir le chapitre avancé « Créer une case à cocher à trois états ».

## JRadioButton

Un bouton radio, permettant de sélectionner un item parmi plusieurs dans un groupe. La classe JRadioButton possède plusieurs constructeurs qui permettent d'initialiser le texte affiché, l'image d'icône, et l'état initial (booléen, sélectionné ou non).

### new JRadioButton(String text, Icon icon, boolean selected)

    Tous les paramètres sont optionnels.

### new JRadioButton(Action action)

    Paramétrage avec une action.
La classe ButtonGroup n'est pas un composant mais représente un groupe de boutons abstraits (JRadioButton, JToggleButton, ...) dont seul l'un d'entre eux peut être sélectionné à la fois. La sélection de l'un d'eux entraîne la dé-sélection des autres.
Exemple :
Gestion de sélection :

## JComboBox

Une liste déroulante de choix (un seul choix possible).

## JList

Une liste d'items, un ou plusieurs items sélectionnable.
Le constructeur accepte un tableau d'objets (leur méthode toString est appelé pour le texte à afficher) ou un modèle de liste JListModel. L'utilisation d'un modèle de liste est recommandée quand la liste des items est modifiable par l'utilisateur.
Ce composant a plusieurs modes de sélection :
* un seul item sélectionnable :
    // Mode de sélection : un seul ls_jours.setselectionMode(ListSelectionModel.SINGLE_SELECTION); ls_jours.setSelectedIndex(2); // Mercredi // Plus tard, durant une action : int index_jour = ls_jours.getSelectedIndex;
* un seul intervalle continu sélectionnable :
    // Mode de sélection : un seul intervalle ls_jours.setselectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); ls_jours.setSelectionInterval(2, 4); // Du mercredi au vendredi inclus // Plus tard, durant une action : int indexs_jour = ls_jours.getSelectedIndices;
* plusieurs intervalles sélectionnables :
    // Mode de sélection : plusieurs intervalles ls_jours.setselectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); // Soit tous les indices : ls_jours.setSelectedIndices(new int{0,1,2, 4,5}); // Lundi, mardi, mercredi, vendredi, samedi // Soit les intervalles : ls_jours.setSelectionInterval(0, 2); // Du lundi au mercredi inclus ls_jours.addSelectionInterval(4, 5); // et ajouter du vendredi au samedi inclus // Plus tard, durant une action : int indexs_jour = ls_jours.getSelectedIndices;
La taille de la liste est déterminée par le nombre d'items. Ce composant ne gère pas le défilement. Il faut l'englober dans un composant JScrollPane pour gérer le défilement d'une longue liste dans un espace plus réduit.

## JSpinner

Champ de saisie avec boutons d'incrémentation et de décrémentation (nombre, items dans une liste prédéfinie, ...).

## JSlider

Sélection d'une valeur numérique par glissement d'un curseur sur une ligne.

## JProgressBar

Barre de progression.

## JTree

Arborescence de nœuds pour les données structurées.

## JTable

Table de données.

## JScrollbar

Une barre de défilement.
Ce composant est rarement utilisé directement car il est de bas niveau et est utilisé notamment par JScrollPane qui gère la vue partielle, le défilement et les entêtes.

## JPanel

Un conteneur générique, comme le contentPane de la fenêtre de test.
Un panel, comme sa classe de base java.awt.Container, peut contenir tout type de composant, y compris d'autres panels. L'imbrication de panels permet de subdiviser un panel en plusieurs panels indépendant et réutilisables ayant leur propre configuration (gestionnaire de disposition, couleur de fond, ...).

## JScrollPane

Un conteneur permettant de faire défiler le contenu du composant (la vue) lorsque sa taille dépasse les limites du conteneur. Ce type de panel est notamment utilisé avec les listes ayant plus de 3 ou 5 items, les tables, les arborescences, les panels ayant un contenu occupant une grande surface.
Ce composant comporte en fait neufs zones illustrées ci-dessous :
| Coin supérieur gauche | Entête de colonne | Coin supérieur droit |
| Entête de ligne | Partie principale du composant | Barre de défilement vertical |
| Coin inférieur gauche | Barre de défilement horizontal | Coin inférieur droit |

### Partie principale du composant

    Il s'agit en général du composant lui-même, dont la vue suit le défilement horizontal et vertical des barres situées en bas et à droite respectivement.
    Le composant affiché est celui passé au constructeur :
       new JScrollPane(table_pages) // Pour la table des pages
    Il peut être spécifié ou modifié après construction en utilisant la méthode setViewportView :
       // Convention : sp_ pour scroll pane JScrollPane sp_pages = new JScrollPane; sp_pages.setViewportView(table_pages); // configurer la vue (view) du JViewport
    Les trois zones de vues avec défilement (principal, entête de colonne, entête de ligne) sont en fait encapsulées dans un JViewport qui gère l'affichage partiel. Il est possible de spécifier un autre type de JViewport en utilisant la méthode setViewport.

### Entête de colonne

    Un entête toujours affiché en haut, mais pouvant être défilé horizontalement pour suivre le contenu principal.
    Il peut s'agir d'un composant quelconque. Cependant, pour les tables, il s'agit souvent de l'entête des colonnes de la table que l'on obtient par appel de la méthode getTableHeader :
       sp_pages.setColumnHeaderView(table_pages.getTableHeader); // configurer l'entête de colonne
    Les trois zones de vues avec défilement (principal, entête de colonne, entête de ligne) sont en fait encapsulées dans un JViewport qui gère l'affichage partiel. Il est possible de spécifier un autre type de JViewport en utilisant la méthode setColumnHeader.

### Entête de ligne

    Un entête toujours affiché à gauche, mais pouvant être défilé verticalement pour suivre le contenu principal.
    Il s'agit d'un composant quelconque :
       sp_pages.setRowHeaderView(p_row); // configurer l'entête de ligne
    Les trois zones de vues avec défilement (principal, entête de colonne, entête de ligne) sont en fait encapsulées dans un JViewport qui gère l'affichage partiel. Il est possible de spécifier un autre type de JViewport en utilisant la méthode setRowHeader.

### Barres de défilement

    Les barres de défilement sont affichées quand nécessaire par défaut. Ce comportement peut être modifié soit en spécifiant le mode pour les barres verticales et horizontales au constructeur, ou en appelant les méthodes setVerticalScrollBarPolicy( ... ) et setHorizontalScrollBarPolicy( ... ) :
    setVerticalScrollBarPolicy( ... ) setHorizontalScrollBarPolicy( ... ) Quand afficher la barre de défilement vertical / horizontal : ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED Quand nécessaire (contenu plus large que la zone de vue). ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER Jamais (vue tronquée et non défilable). ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS Toujours.

### Coins

    Il peut y avoir jusqu'à quatre coins affichés, qui sont par défaut vides. Il est possible d'y placer des composants de petite taille, en général décoratifs seulement, afin de combler le vide.
    setCorner(String key, Component component)

   ### key

       Nom du coin où placer le composant. Ce nom doit être l'un de ceux listés ci-dessous :
      * Coin absolu :
      ** ScrollPaneConstants.UPPER_LEFT_CORNER // supérieur gauche
      ** ScrollPaneConstants.UPPER_RIGHT_CORNER // supérieur droit
      ** ScrollPaneConstants.LOWER_LEFT_CORNER // inférieur gauche
      ** ScrollPaneConstants.LOWER_RIGHT_CORNER // inférieur
      * Coin relatif au sens de lecture :
      ** ScrollPaneConstants.UPPER_LEADING_CORNER // supérieur début de ligne (gauche/droit)
      ** ScrollPaneConstants.UPPER_TRAILING_CORNER // supérieur fin de ligne (droit/gauche)
      ** ScrollPaneConstants.LOWER_LEADING_CORNER // inférieur début de ligne (gauche/droit)
      ** ScrollPaneConstants.LOWER_TRAILING_CORNER // inférieur fin de ligne (droit/gauche)

   ### component

       Composant à placer.

# Arbre

Swing possède une classe de composant graphique permettant l'affichage d'une arborescence de données, de manière similaire à un explorateur de répertoires, nommée JTree.

## Modèle

Le modèle de l'arbre est géré par une classe implémentant l'interface javax.swing.tree.TreeModel. Cette interface permet d'obtenir l'arborescence des objets à afficher, la notification de modification des objets, et d'enregistrer un écouteur d'évènement de changement d'arborescence. Elle est implémentée par la classe javax.swing.tree.DefaultTreeModel gérant des nœuds implémentant l'interface javax.swing.tree.TreeNode.
Cette interface est implémentée par la classe javax.swing.tree.DefaultMutableTreeNode.

## Liens

* (anglais) How to Use Trees : http://docs.oracle.com/javase/tutorial/uiswing/components/tree.html

# Fenêtres

Les fenêtres Swing dérivent de celles disponibles avec AWT :
* JFrame (sous-classe de Frame) représente une fenêtre d'application.
* JDialog (sous-classe de Dialog) représente une fenêtre de dialogue.
* JWindow (sous-classe de Window) représente une fenêtre générique.
* JInternalFrame (pas d'équivalent AWT) représente une fenêtre interne à l'intérieur d'une zone multi-documents gérée par la classe JDesktopPane.
* JApplet (sous-classe de Applet) représente une fenêtre intégrée dans une page HTML d'un navigateur web. Ce type de fenêtre ne se rencontre plus beaucoup car pour des raisons de sécurité, Java est désactivé par défaut sur tous les navigateurs récents. De plus les applets sont déclarées obsolètes depuis Java 9 en 2017, et supprimées de Java depuis la version 11.

## Points communs

Les différents types de fenêtres Swing cités précédemment ont une structure commune et des méthodes communes.
L'exemple ci-dessous crée une fenêtre d'application avec un titre, contenant un label affichant du texte.

### Titre et icône

Une fenêtre possède un titre (méthode setTitle(String)) et une icône (méthode setIconImage(Image)). Par défaut, l'icône est la même que celle de la fenêtre propriétaire.

### Composition

Chaque fenêtre comporte un panel racine (classe JRootPane) qui gère la composition des éléments suivants :
* Le panneau frontal (Glass pane en anglais) est affiché au premier plan. Il est généralement utilisé pour afficher une animation de chargement de la fenêtre, une progression d'une longue opération en cours.
* Le panneau de contenu (Content pane) contient les composants ajoutés à la fenêtre (bouton, label, ...).
* La barre de menu (JMenuBar) définit les listes d'actions utilisables. Elle est affichée en haut de la fenêtre.
La barre de menu et le panneau de contenu sont gérés par un panneau de classe JLayeredPane permettant de gérer la superposition de composants.

### Taille et position

La taille et la position d'une fenêtre sont gérées avec les mêmes méthodes que celles des composants. Il n'y a pas de gestionnaire de disposition pour les fenêtres, car aucun conteneur ne les contient (hormis JInternalFrame et JApplet).
La taille et la position de la fenêtre définies par l'application peuvent ensuite être modifiées par l'utilisateur en déplaçant et en redimensionnant la fenêtre. Les méthodes setMinimumSize(Dimension) et setMaximumSize(Dimension) définissent les limites du redimensionnement. La méthode setPreferredSize(Dimension) héritée de la classe Component n'a pas de sens pour les fenêtres.

### Menu

Les fenêtres de type javax.swing.JFrame et javax.swing.JDialog peuvent avoir une barre de menus.
Voir le chapitre suivant sur les menus pour plus de détails.

## Fenêtre d'application

La fenêtre d'application est celle qui définie la vue principale d'une application. Une application peut en avoir plusieurs (Par exemple, une fenêtre par fichier ouvert). Les applications les plus simples n'ont qu'une seule fenêtre principale.
La classe javax.swing.JFrame définit une fenêtre d'application.

### Action de fermeture

La méthode setDefaultCloseOperation(int operation) définit l'action effectuée quand l'utilisateur clique le bouton de fermeture de la fenêtre. L'argument peut avoir l'une des valeurs suivantes définies dans l'interface WindowConstants implémentée par la classe JFrame :

#### DO_NOTHING_ON_CLOSE

    Ne rien faire, et appeler les gestionnaires d'évènements des fenêtres (WindowListener) enregistrés.

#### HIDE_ON_CLOSE

    Cacher la fenêtre puis appeler les gestionnaires d'évènements des fenêtres (WindowListener) enregistrés.

#### DISPOSE_ON_CLOSE

    Cacher et libérer la fenêtre puis appeler les gestionnaires d'évènements des fenêtres (WindowListener) enregistrés.

#### EXIT_ON_CLOSE

    Terminer l'application en appelant System.exit(0). Les gestionnaires d'évènements des fenêtres ne sont pas appelés.
Par défaut, la valeur est HIDE_ON_CLOSE. Tout changement provoque un évènement de changement de propriété dont le nom est "defaultCloseOperation".
Quand la dernière fenêtre est libérée (dispose), l'application peut se terminer.
Quand l'application doit faire confirmer la fermeture par l'utilisateur (par exemple en cas de données non sauvegardées), il est nécessaire de définir l'action de fermeture à rien (DO_NOTHING_ON_CLOSE) et d'ajouter un écouteur d'évènements des fenêtres (interface WindowListener implémentée par la classe vide WindowAdapter).
Dans le constructeur de la classe de fenêtre principale :
Dans la classe de fenêtre principale :
La méthode confirmerAction ci-dessus est réutilisable pour les actions provoquant une perte des données non sauvegardées :
* La fermeture de la fenêtre principale,
* L'action quitter (menu / bouton),
* Charger un fichier (si la même fenêtre est réutilisée),
* Créer un nouveau fichier (si la même fenêtre est réutilisée),

### États de la fenêtre

La méthode setExtendedState(int state) permet de modifier l'état de la fenêtre, et la méthode int getExtendedState permet de récupérer l'état actuel :

#### NORMAL

    Affichage normal,

#### ICONIFIED

    Fenêtre icônifiée sur le bureau ou réduite dans la barre des tâches,

#### MAXIMIZED_HORIZ

    Fenêtre affichée sur toute la largeur de l'écran,

#### MAXIMIZED_VERT

    Fenêtre affichée sur toute la hauteur de l'écran,

#### MAXIMIZED_BOTH

    Fenêtre affichée sur tout l'écran (combine les deux états précédents).

### Icône de fenêtre

L'icône de la fenêtre d'application est visible, selon le système d'exploitation, dans le coin du bord de la fenêtre (sous Windows, en haut à gauche en 16x16 pixels), dans la barre des tâches (taille variable : 16x16, 48x48, 64x64, ...), dans le sélecteur de fenêtres (sous Windows avec AltTab ↹ ou ⊞Tab ↹). L'icône de la fenêtre d'application peut aussi être utilisée par les boîtes de dialogue ayant la fenêtre comme propriétaire (owner) passé au constructeur.
Deux méthodes existent :
* La méthode setIconImage(java.awt.Image image) définit une image comme icône. Cette image est redimensionnée selon la taille nécessaire. Il est donc recommandé d'utiliser une image de grande résolution (64x64 pixels minimum) afin d'éviter un agrandissement produisant des blocs de pixels. Celle-ci doit donc aussi pouvoir être réduite sans perte de visibilité.
* La méthode setIconImages(java.util.List<java.awt.Image> images) permet de définir une liste d'images. Selon la taille d'icône du contexte (exemple : 16x16 pour l'icône du bord de fenêtre, 64x64 pour la barre des tâches, ...), une image suffisamment grande est sélectionnée et redimensionnée si nécessaire. Cette méthode est recommandée si l'icône change en fonction de la taille, pour par exemple y mettre moins d'éléments pour les plus petites tailles d'icône.
La méthode setIconImage(java.awt.Image image) est en fait un appel à la méthode setIconImages(java.util.List<java.awt.Image> images) en lui passant une liste contenant une seule image.
En général, l'icône est une image stockée dans le même répertoire que les classes de l'application, dans un format supporté par ImageIO (PNG, GIF). Exemple pour une image stockée dans le même répertoire que la classe de la fenêtre :
Pour une liste d'icônes :

## Fenêtre de dialogue

Une fenêtre de dialogue est une fenêtre temporaire ouverte sur une action spécifique permettant de dialoguer avec l'utilisateur pour lui fournir et lui demander une ou plusieurs informations : entrer les champs d'une nouvelle entité à ajouter dans une table, sélectionner un fichier, sélectionner une couleur, afficher un message d'erreur, demander une confirmation de suppression...

### Fenêtres de dialogue prédéfinies

Swing possède des classes ou méthodes permettant d'utiliser des fenêtres de dialogue prédéfinies.

#### JOptionPane

    La classe JOptionPane définit une fenêtre de dialogue simple permettant d'afficher un message avec une icône prédéfinie associée (information, question, avertissement, erreur...), avec éventuellement un champ de saisie, et de 1 à 3 boutons pour divers choix possible de réponse.
    Cette classe possède différentes méthodes statiques pour créer et afficher ces fenêtres.

#### JColorChooser

    La classe JColorChooser définit une fenêtre de dialogue pour sélectionner une couleur.

#### JFileChooser

    La classe JFileChooser définit une fenêtre de dialogue pour sélectionner un fichier ou un répertoire.

### Fenêtre de dialogue de type message : JOptionPane

La classe JOptionPane définit une fenêtre de dialogue simple permettant d'afficher un message avec une icône prédéfinie associée, pouvant avoir un champ de saisie selon le type, et proposant plusieurs choix de bouton.

#### Simple notification de message

Ce type de fenêtre affiche simplement le message et possède un bouton OK pour fermer la fenêtre, et utilise la méthode showMessageDialog.
Exemple :
* La méthode showMessageDialog ne retourne aucune valeur.
* frame est la fenêtre (Frame ou JFrame) par dessus laquelle le message est affiché. Si la référence est null, une fenêtre est créée.
* Le message affiché dans la fenêtre est spécifié avant le titre.
* Le dernier argument spécifie le type de message, définissant l'icône affichée en face du message :
Il existe deux variantes de la méthode showMessageDialog :
* Sans les deux derniers arguments de titre ("Message" par défaut) et type (JOptionPane.INFORMATION_MESSAGE par défaut).
* Avec un cinquième argument permettant d'afficher une icône personnalisée de type xjava.swing.Icon.

#### Message en HTML

Il est possible d'utiliser le format HTML pour formater le message affiché. Pour cela, il faut encadrer le message dans une balise <html> et ne pas utiliser de caractères de contrôle dans le message ; par exemple "\n" qui doit être remplacé par <br/>.
Exemple :
Pour plus de détails sur le formatage en HTML et les limitations, voir le chapitre sur le contenu en HTML.

#### Fenêtre de confirmation

Ce type de fenêtre affiche le message et propose plusieurs boutons de confirmation pour fermer la fenêtre, et utilise la méthode showConfirmDialog.
Exemple :
Les variantes de la méthode sont :

##### int showConfirmDialog(Component parent, String message)

    Titre "Select an option", boutons Oui, Non et Annuler ;

##### int showConfirmDialog(Component parent, String message, String title, int optionType)

    Titre et boutons spécifiés ;

##### int showConfirmDialog(Component parent, String message, String title, int optionType, int messageType)

    Type de message spécifié également ;

##### int showConfirmDialog(Component parent, String message, String title, int optionType, int messageType, Icon icon)

    Avec icône personnalisée.
* optionType définit les boutons disponibles :
** JOptionPane.YES_NO_OPTION : Boutons Oui et Non ;
** JOptionPane.YES_NO_CANCEL_OPTION : Boutons Oui, Non et Annuler ;
** JOptionPane.OK_CANCEL_OPTION : : Boutons OK et Annuler.
* La méthode retourne le choix de l'utilisateur :
** JOptionPane.OK_OPTION : Bouton OK cliqué ;
** JOptionPane.YES_OPTION : Bouton Oui cliqué ;
** JOptionPane.NO_OPTION : Bouton Non cliqué ;
** JOptionPane.CANCEL_OPTION : Bouton Annuler cliqué ;
** JOptionPane.CLOSED_OPTION : Fenêtre fermée sans choix (à considérer comme Annuler ou Non).

#### Fenêtre de saisie

Ce type de fenêtre utilise la méthode showInputDialog et affiche le message et un champ de saisie ou une liste de choix, et les boutons OK et Cancel pour fermer la fenêtre.
Exemple :
La méthode retourne la valeur sélectionnée ou entrée par l'utilisateur, ou null si l'utilisateur a choisi d'annuler.

#### Panel de dialogue interne

Les méthodes vu précédemment ont toutes une variante pour afficher un panel de dialogue interne, dont le nom est celui de la méthode où show est remplacé par showInternal :
* showInternalConfirmDialog,
* showInternalMessageDialog,
* showInternalInputDialog,
* showInternalOptionDialog.

### Fenêtre de sélection de couleur : JColorChooser

La fenêtre de sélection de couleur permet de choisir une couleur parmi une palette prédéfinie ou en spécifiant les composantes RVB ou TSL.
Les arguments de la méthode showDialog de la classe JColorChooser sont :
* la fenêtre principale sur laquelle la fenêtre de dialogue s'affiche,
* le titre de la fenêtre de dialogue,
* la couleur initiale.
La méthode retourne la couleur sélectionnée de classe java.awt.Color ou null si l'utilisateur a annulé.
Exemple : Sélection de la couleur de fond :

### Fenêtre de sélection de fichier ou répertoire : JFileChooser

La classe JFileChooser définit une fenêtre permettant de sélectionner un ou plusieurs fichiers, ou un répertoire.
Exemple : Sélection pour ouvrir un fichier.
Exemple : Sélection pour enregistrer un fichier.
Pour conserver le répertoire courant à chaque réouverture, il vaut mieux que l'instance de la classe JFileChooser soit déclarée dans la classe et construit par le constructeur.

#### Fichier présélectionné

Avant l'affichage, on peut changer le répertoire courant et/ou le fichier sélectionné :

#### Sélectionner plusieurs fichiers

Il est possible d'autoriser la sélection de plusieurs fichiers :

#### Sélectionner un fichier ou un répertoire

Le mode de sélection permet de choisir si l'utilisateur peut sélectionner un fichier, un répertoire ou les deux :

#### Filtres de fichier

La fenêtre de sélection possède deux types de filtre de classe javax.swing.filechooser.FileFilter :
* Un filtre général pour ne pas afficher certains fichiers à l'utilisateur,
* Une liste de filtre que l'utilisateur peut choisir pour afficher différents types de fichiers.
Le filtre général de la vue est configuré en appelant la méthode setFileFilter(FileFilter filter). Exemple :
Les filtres sélectionnables sont ajoutés un par un en appelant la méthode addChoosableFileFilter(FileFilter filter). Exemple :
Vous pouvez utiliser la sous-classe javax.swing.filechooser.FileNameExtensionFilter pour les filtres basés sur l'extension de fichier. Le constructeur prend en paramètre la description du filtre suivie de la liste des extensions de nom des fichiers acceptés :

#### Ajouter une prévisualisation

La fenêtre de sélection de fichiers peut afficher un composant accessoire pour, par exemple, afficher un aperçu du fichier sélectionné (image, ...). Pour ajouter un tel composant, il faut utiliser la méthode setAccessory(Component newAccessory).
Exemple : Une vue de prévisualisation d'image :
Une instance de cette classe s'ajoute en appelant les deux méthodes ci-dessous :

# Menus
## Types de menu

Swing supporte deux types de menu :
* les menus de type javax.swing.JMenu attaché à une fenêtre via sa barre de menu javax.swing.JMenuBar généralement située en haut de la fenêtre.
* les menus de type javax.swing.JPopupMenu attaché à tout composant, dont l'affichage est, par exemple, déclenché par un clic droit de la souris.

## Création d'un menu

Ces deux types de menu (javax.swing.JMenu et javax.swing.JPopupMenu) s'utilisent de la même façon. Un menu est initialement vide, et l'ajout d'un item se fait en appelant l'une des méthodes suivantes :

### add(JMenuItem item)

    Ajouter un item de menu.

### add(Action action)

    Ajouter un item de menu créé à partir d'une action.
La seconde méthode est préférable car une même action peut être attachée à différents composants (menu, bouton, ...) ce qui simplifie la gestion de son état (état activé ou désactivé, texte, icône, raccourci clavier, ...) dont la modification de répercute sur tous les composants liés.
Il est possible d'ajouter d'autres éléments à un menu :
* La méthode addSeparator permet d'ajouter un séparateur entre deux items de menu.
* La méthode add(JMenuItem item) accepte aussi un autre menu afin d'ajouter un sous-menu, car la classe javax.swing.JMenu dérive de javax.swing.JMenuItem.
Exemple :

## Gestion d'un menu contextuel

Un menu contextuel (classe javax.swing.JPopupMenu) a la particularité de s'afficher à l'endroit où l'utilisateur a déclenché son affichage (Par exemple, clic droit avec la souris sous Windows ou Linux, ou la touche Menu).

### Le déclenchement

Le déclenchement de l'ouverture du menu dépend de la plateforme : il peut se faire lors de l'appui du bouton de souris, ou lors de son relâchement (comme sous Windows par exemple). La classe MouseEvent possède une méthode isPopupTrigger permettant de tester si l'évènement est celui qui déclenche l'ouverture du menu pour la plateforme.
L'affichage du menu contextuel doit se faire avec la méthode show(JComponent, int, int) pour que le comportement du menu soit correct : mise en évidence de l'item survolé, gestion de la disparition du menu en cliquant ailleurs, ... L'utilisation des méthodes setLocation et setVisible n'est pas recommandée car le menu peut ne pas fonctionner correctement.

# Actions

Une action permet de préconfigurer plusieurs propriétés. Quand un composant (JRadiobutton, JButton, JMenuItem, ...) est lié à une action, ses propriétés suivent l'évolution de celles de l'action. Notamment, cela permet de désactiver/activer une action, de configurer un listener d'action (ActionListener) une seule fois. Cela est pratique quand une même action est disponible à la fois dans un menu et par un bouton, par exemple.
Exemple pour une action de menu, ou de bouton :

# Types de données et constantes

Les interfaces graphiques en Swing utilisent les mêmes types de données qu'en AWT. Ces types sont utilisés abondamment dans l'API pour définir des informations graphiques :
* Couleur
* Dimensions
* Police de caractères
* Position
* Orientation
* Curseur de souris

## Couleurs

Un objet de classe java.awt.Color représente une couleur au format RVBA (Rouge, Vert, Bleu et Alpha) à quatre composantes dont la valeur est entre 0 et 255. La composante alpha définit l'opacité de la couleur : 0 = transparente, 255 = opaque (valeur par défaut).
Le constructeur de la classe java.awt.Color permet de spécifier la couleur avec les trois composantes et éventuellement la valeur alpha pour la transparence, de type int entre 0 et 255 ou de type float entre 0.0 et 1.0.
Exemples :
La classe java.awt.Color définit quelques couleurs utilisées fréquemment :
| BLUE(0,0,255) | GREEN(0,255,0) | RED(255,0,0) | ORANGE(255,200,0) | |
| YELLOW(255,255,0) | MAGENTA(255,0,255) | CYAN(0,255,255) | PINK(255,175,175) | |
| BLACK(0,0,0) | DARK_GRAY(64,64,64) | GRAY(128,128,128) | LIGHT_GRAY(192,192,192) | WHITE(255,255,255) |

## Police de caractères

Un objet de classe java.awt.Font représente la police de caractère utilisée pour dessiner le texte. La classe possède deux constructeurs :

### Font(String name, int style, int size)

    Les attributs sont définis par les arguments suivants :
   * name : Nom de la police de caractères. Il peut aussi s'agir d'un nom symbolique défini par Java disponible sur toutes les plateformes : Dialog, DialogInput, Monospaced, ...
   * style : Style de police, parmi les constantes définies par la classe : PLAIN, BOLD, ITALIC, BOLD_ITALIC.
   * size : Taille de la police de caractères en points.

### Font(Map<? extends Attributes, ?> attributes)

    Les attributs sont définis par un dictionnaire (Map) dont les clés doivent être celles définies par la classe java.awt.font.TextAttribute. Ce constructeur permet de définir davantage d'attributs.
La taille des polices de caractères en Java est en points pour 72 DPI (Dot Per Inch), quel que soit le DPI du système.
S'il est configuré convenablement au niveau système, le nombre de pixels par pouce (DPI) permet d'avoir un affichage de taille physique constant quel que soit le moyen d'affichage utilisé. Sachant qu'un point (pt) vaut 1/72 pouce (1/72 in), une police de caractère de 16pt devrait donc être affichée sur une hauteur de 21.333 pixels environ sur un écran à 96 DPI :
$taille\_en\_pixels={\frac {taille\_en\_points}{72}}\times dpi={\frac {16}{72}}\times 96\approx 21.333$
Cependant, Java utilise 72 DPI, ce qui signifie que la taille donnée en point sera la même en pixels : 16pt affiché sur 16 pixels. Les polices de caractères apparaissent donc plus petites dans les applications Java par rapport aux autres applications (traitement de texte, logiciel de dessin, navigateur...) sur les écrans ayant un DPI supérieur et plus grandes sur les écrans avec un DPI inférieur à 72.
Pour obtenir un affichage identique aux autres applications, en ajoutant un paramètre dans l'application pour configurer le nombre de points par pouce, il faut ajuster la taille de la police. Pour l'exemple d'une police de caractères de 16 points sur un écran de 96 DPI, il faut utiliser une taille de 21,333. Cependant, le constructeur acceptant un nombre à virgule flottante pour la taille au lieu d'un entier est privé, mais la méthode deriveFont autorise un nombre à virgule flottante pour la taille.
Cependant, au lieu d'utiliser une valeur fixe, il est préférable d'utiliser la valeur retournée par la méthode getScreenResolution de la classe java.awt.Toolkit :
Le nombre de points par pouce n'est toutefois pas toujours bien configuré. La solution est de prévoir un paramètre de configuration pour changer la valeur de densité, en utilisant la valeur retournée par la méthode getScreenResolution comme valeur par défaut.

## Dimensions

Un objet de classe java.awt.Dimension définit la taille d'un composant avec deux attributs publics, qui peuvent être initialisés en passant leur valeur en argument du constructeur :

### int width

    Largeur en pixels (0 par défaut).

### int height

    Hauteur en pixels (0 par défaut).

## Position

Un objet de classe java.awt.Point définit la position d'un composant avec deux attributs publics, qui peuvent être initialisés en passant leur valeur en argument du constructeur :

### int x

    Position horizontale en pixels (0 par défaut), relative au bord gauche du conteneur.

### int y

    Position vertical en pixels (0 par défaut), relative au bord supérieur du conteneur.

## Rectangle

Un objet de classe java.awt.Rectangle définit à la fois la la position d'un composant et sa taille avec quatre attributs publics, qui peuvent être initialisés en passant leur valeur en argument du constructeur :

### int x

    Position horizontale en pixels (0 par défaut), relative au bord gauche du conteneur.

### int y

    Position vertical en pixels (0 par défaut), relative au bord supérieur du conteneur.

### int width

    Largeur en pixels (0 par défaut).

### int height

    Hauteur en pixels (0 par défaut).
La classe Rectangle a aussi des constructeurs acceptant une position (java.awt.Point) ou une taille (java.awt.Dimension) ou les deux.
Cette classe permet aussi quelques opérations :

## Constantes de position et orientation

L'interface javax.swing.SwingConstants est une collection de constantes relatives à la position et l'orientation. Les tableaux ci-dessous montrent leur valeur et ce qu'elle représente.
Le centre a la valeur nulle, et les autres constantes de positionnement sont distribuées ensuite dans le sens trigonométrique (anti-horaire) en partant du haut.
| | TOP = 1 | |
| LEFT = 2 | CENTER = 0 | RIGHT = 4 |
| | BOTTOM = 3 | |
Le centre a la valeur nulle, et les autres constantes de direction sont distribuées ensuite dans le sens horaire (anti-trigonométrique) en partant du haut.
| NORTH_WEST = 8 | NORTH = 1 | NORTH_EAST = 2 |
| WEST = 7 | CENTER = 0 | EAST = 3 |
| SOUTH_WEST = 6 | SOUTH = 5 | SOUTH_EAST = 4 |
Les autres constantes définies sont les constantes d'orientation :
* HORIZONTAL = 0
* VERTICAL = 1
les constantes de position selon l'orientation de lecture de la langue courante :
* LEADING = 10 avant dans le sens de lecture.
    Cela correspond à gauche (respectivement droite) si la langue courante se lit de gauche à droite (respectivement de droite à gauche),
* TRAILING = 11 après dans le sens de lecture.
    Cela correspond à droite (respectivement gauche) si la langue courante se lit de gauche à droite (respectivement de droite à gauche).
et les constantes de direction dans une séquence :
* NEXT = 12
* PREVIOUS = 13

## Curseur de souris

Un objet de classe java.awt.Cursor définit le curseur utilisé quand la souris survole un composant. Le curseur par défaut est défini en fonction du composant : une flèche par défaut, une ligne verticale pour un champ textuel, ... La méthode setCursor(Cursor c) est définie pour toutes les classes de composants et fenêtres.
Afin de conserver une cohérence avec les autres applications, le changement de curseur n'est conseillé que pour certains cas exceptionnels et la création de nouveaux composants.
Plusieurs méthodes statiques permettent d'utiliser un curseur prédéfini :

### Cursor.getPredefinedCursor(int type)

    Retourne un curseur typique (disponible sur toutes les plateformes Java) parmi ceux-ci :
   * Cursor.DEFAULT_CURSOR Curseur par défaut (flèche),
   * Cursor.CROSSHAIR_CURSOR Curseur en croix (ligne verticale + ligne horizontale),
   * Cursor.TEXT_CURSOR Curseur pour les champs de texte,
   * Cursor.WAIT_CURSOR Curseur d'attente durant une action en cours (sablier, ...), peut être animé selon la plateforme,
   * Cursor.direction_RESIZE_CURSOR Groupe de curseurs de la souris de type flèche directionnelle ou bidirectionnelle utilisé généralement pour le redimensionnement des fenêtres, quand la souris approche du bord : (N=North (nord), S=South (sud), E=East (est), W=West (ouest)) SW_RESIZE_CURSOR, SE_RESIZE_CURSOR, NW_RESIZE_CURSOR, NE_RESIZE_CURSOR, S_RESIZE_CURSOR, N_RESIZE_CURSOR, W_RESIZE_CURSOR, E_RESIZE_CURSOR,
   * Cursor.MOVE_CURSOR Curseur de type flèche dans les 4 directions cardinales, généralement utilisé lors du déplacement d'un élément,
   * Cursor.HAND_CURSOR Curseur de type main, généralement utilisé pour les actions ouvrant une fenêtre ou une page (lien) ;

### Cursor.getDefaultCursor

    Retourne le curseur par défaut (flèche) ;
    Équivaut à : Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)

### Cursor.getSystemCustomCursor(String name)

    Retourne un curseur spécifique au système, à partir d'un nom (par exemple "Invalid.32x32").
    Ces curseurs sont définis dans le fichier de configuration cursors.properties du JRE (ou le sous-répertoire "jre" du JDK) localisé dans le répertoire lib/images/cursors. Les fichiers images référencés sont situés dans le même répertoire.
    Exemple pour JDK 1.8 sous Windows, extrait du fichier C:\Program Files\Java\jdk1.8.0_05\jre\lib\images\cursors\cursors.properties :
Il est également possible de créer un curseur personnalisé à partir d'une image et d'un point dans l'image. Ce point détermine quel pixel de l'image correspond à la position de la souris.

## Notes de bas de page

* ↑ Exemple d'un écran de 23 pouces en résolution 1980x1080 (diagonale approximative de 2229.12 pixels) : $2229.12/23\approx 96$ pixels par pouce.

# Gestion de la disposition des composants

La disposition des composants n'est pas définie directement par le conteneur mais par le gestionnaire de disposition des composants (Layout manager en anglais).

## Informations utilisées et contrainte associée

Le gestionnaire peut utiliser différentes informations fournies par le composant :

### Dimension getPreferredSize

    La taille préférée du composant, qui dépend en général de son contenu et du moteur de rendu utilisé.

### Dimension getMinimumSize

    La taille minimale du composant. C'est la taille en dessous de laquelle le composant est jugé non utilisable (pas assez de place pour tout afficher, ...).

### Dimension getMaximumSize

    La taille maximale du composant.
Les gestionnaires de disposition ne respectent pas tous les trois contraintes précédentes définies par le composant.
Le code typique de création d'un conteneur avec gestionnaire de disposition est le suivant :
// Créer le conteneur et lui associer le gestionnaire de disposition Conteneur c = new Conteneur c.setLayout(new GestionnaireDeDisposition(arguments...));
// Ajouter les composants avec une contrainte de disposition c.add(composant, contrainte);
Exemple :

## Gestionnaires de disposition prédéfinis

Les interfaces java.awt.LayoutManager et java.awt.LayoutManager2 sont définies par une variété de classes définissant différentes façon de disposer les composants :

### FlowLayout

    Les composants sont dimensionnés à leur taille préférée, et disposés en ligne, avec passage à la ligne suivante dès que la largeur restante ne suffit plus. Les lignes peuvent être justifiées à gauche, à droite ou centrées dans le contenant.
       new FlowLayout // composants alignés à gauche par défaut
       new FlowLayout(FlowLayout.LEFT) // composants alignés à gauche
       new FlowLayout(FlowLayout.RIGHT) // composants alignés à droite
       new FlowLayout(FlowLayout.CENTER) // composants centrés
    L'alignement peut aussi prendre en compte le sens de lecture de gauche à droite ou de droite à gauche, leading pour le début (gauche / droite) et trailing pour la fin (droite / gauche) :
       new FlowLayout(FlowLayout.LEADING) // composants alignés en début de ligne
       new FlowLayout(FlowLayout.TRAILING) // composants alignés en fin de ligne
    Le constructeur peut prendre deux paramètres supplémentaires, espacement horizontal et vertical, pour spécifier l'espacement entre les composants et entre le conteneur et les composants :
       new FlowLayout(FlowLayout.RIGHT, 10, 5) // composants alignés à droite espacés de 10 pixels en horizontal et 5 en vertical.

### BorderLayout

    Le conteneur est divisé verticalement en trois zones, de haut en bas : nord (BorderLayout.NORTH), centrale et sud (BorderLayout.SOUTH). La partie centrale est elle-même divisée horizontalement en trois zones, de gauche à droite : ouest (BorderLayout.WEST), centre (BorderLayout.CENTER) et est (BorderLayout.EAST).
    Le constructeur peut prendre deux paramètres, espacement horizontal et vertical, pour spécifier l'espacement entre les composants :
       new BorderLayout // composants non espacés
       new BorderLayout(10, 5) // composants espacés de 10 pixels en horizontal et 5 en vertical.

### GridLayout

    Les composants sont dimensionnés selon la taille du conteneur divisée en colonnes et lignes dont le nombre et l'espacement est indiqué à l'appel du constructeur. Les composants sont positionnés dans l'ordre de leur ajout au conteneur, de gauche à droite en partant de la ligne supérieure.

### GridBagLayout

    Les composants sont dimensionnés à leur taille préférée si la contrainte associée le permet. La contrainte associée est de classe GridBagConstraint et définit la position dans la grille, le nombre de cellules occupées, et les contraintes de taille et d'alignement du composant dans la cellule.

### CardLayout

    Les composants sont dimensionnés à la taille du conteneur, et un seul des composants n'est visible à la fois. Cette disposition est utilisée pour implémenter les onglets.

### GroupLayout

    Les composants sont dimensionnés à leur taille préférée si possible, et groupés ensemble soit parallèlement, soit séquentiellement, en vertical et en horizontal. Cela signifie que les composants sont ajoutés deux fois par le gestionnaire. Ils ne sont pas ajoutés directement au conteneur.

### BoxLayout

    Les composants sont dimensionnés à leur taille préférée si possible, et alignés verticalement ou horizontalement. L'alignement selon l'autre axe est définie par l'alignement préférée du composant. Ce gestionnaire tient compte également de la taille minimale et maximale de chaque composant.
Le gestionnaire de disposition doit être assigné au conteneur soit en le passant au constructeur, soit en appelant la méthode setLayout après construction du conteneur. Exemple:
Pour BoxLayout, l'utilisation de la méthode setLayout est obligatoire car le constructeur a besoin de l'instance du conteneur :

## Disposition par défaut des conteneurs

Un conteneur possède un gestionnaire de disposition par défaut.

### JPanel

    FlowLayout est le gestionnaire de disposition par défaut.

### JTabbedPane

    CardLayout est le gestionnaire de disposition utilisé car ce conteneur est une vue à onglets. Les onglets sont visibles en haut du conteneur par défaut, et cliquable par l'utilisateur pour changer l'onglet visible.

# Gestion des évènements

Un composant peut recevoir divers évènements issus de l'interaction avec l'utilisateur :
* Avec la souris : enfoncement et relâchement d'un bouton, déplacement de la souris, défilement de la molette.
* Avec le clavier : enfoncement et relâchement d'une touche, génération d'un caractère.
* Avec d'autres périphériques d'entrée.
Il peut aussi en générer :
* Un bouton peut notifier le déclenchement d'une action.
* La sélection d'un item dans une liste peut déclencher un évènement.

## Principes de gestion des évènements

De manière générale, la gestion des évènements en Java se fait de la manière suivante :
* Les classes voulant recevoir une notification d'un évènement enregistrent un écouteur auprès de la source d'évènements. L'écouteur est un objet dont la classe implémente une interface particulière pour traiter un évènement comportant en général une seule méthode appelée pour notifier l'évènement qui s'est produit.
* La source d'évènement (un composant, un périphérique d'entrée : souris ou clavier, ...) notifie les écouteurs enregistrés en appelant la méthode de l'interface particulière. Cette méthode ne comporte en général qu'un seul argument dont la classe dérive de la classe java.util.EventObject.
L'exemple ci-dessous illustre comment recevoir un évènement d'action sur un bouton, qui se déclenche au clic avec la souris, ou au clavier quand le bouton a le focus.
La convention de gestion des évènements est basée sur celle des JavaBeans. Pour un évènement de type type (MouseWheel, Key, Mouse, ...) :
* Une classe d'évènement typeEvent dérivant de la classe java.util.EventObject contient toutes les informations sur l'évènement notifié.
* Un écouteur doit implémenter l'interface typeListener dont la ou les méthode(s) ont comme seul argument un objet de classe typeEvent.
* La source d'évènement possède deux méthodes publiques :
** addtypeListener pour ajouter un écouteur de l'évènement.
** removetypeListener pour retirer un écouteur.
* En général, en interne, la source d'évènement notifie les écouteurs enregistrés avec une méthode privée ou protégée nommée firetypeEvent.

## Les différents types d'évènements

Il existe un grand nombre de types d'évènements représentés par des classes dérivant de la classe java.util.EventObject. Cette section ne citera que les principaux types parmi ceux concernant les interfaces graphiques avec Swing.
La hiérarchie des types d'évènements contient un certain nombre de classes, chacune pouvant ajouter une ou plusieurs informations sur l'évènement.
| Classe | Classe | Classe | Classe | Classe | Classe | Information(s) sur l'évènement définie(s)/ajoutée(s) par la classe |
| java.util.EventObject | java.util.EventObject | java.util.EventObject | java.util.EventObject | java.util.EventObject | java.util.EventObject | Source de l'évènement : public Object getSource; |
| └─ | java.awt.AWTEvent | java.awt.AWTEvent | java.awt.AWTEvent | java.awt.AWTEvent | java.awt.AWTEvent | Identification de l'évènement (ID), consommation de l'évènement |
| | ├─ | java.awt.event.ActionEvent | java.awt.event.ActionEvent | java.awt.event.ActionEvent | java.awt.event.ActionEvent | Heure de l'évènement et modificateurs (boutons de souris, touches enfoncées : Shift, Alt, Ctrl...) |
| | │ | | | | | (ID: ACTION_PERFORMED) |
| | └─ | java.awt.event.ComponentEvent | java.awt.event.ComponentEvent | java.awt.event.ComponentEvent | java.awt.event.ComponentEvent | Composant source de l'évènement (conversion de type) |
| | | └─ | java.awt.event.InputEvent | java.awt.event.InputEvent | java.awt.event.InputEvent | Heure de l'évènement et modificateurs (boutons de souris, touches enfoncées : Shift, Alt, Ctrl...) |
| | | | ├─ | java.awt.event.KeyEvent | java.awt.event.KeyEvent | Code de la touche, caractère généré |
| | | | │ | | | (ID: KEY_TYPED, KEY_PRESSED, KEY_RELEASED) |
| | | | └─ | java.awt.event.MouseEvent | java.awt.event.MouseEvent | Position de la souris, nombre de clics, indicateur de menu popup |
| | | | | │ | | (ID: MOUSE_CLICKED, MOUSE_PRESSED, MOUSE_RELEASED, MOUSE_MOVED, MOUSE_ENTERED, MOUSE_EXITED, MOUSE_DRAGGED, MOUSE_WHEEL) |
| | | | | └─ | java.awt.event.MouseWheelEvent | Rotation de la molette de la souris (nombre, pixels de défilement) |
Les sous-classes de la classe java.awt.AWTEvent ont un attribut (ID) qui identifie une notification particulière d'un évènement, chacune correspondant à une méthode de l'interface d'écoute de l'évènement. Par exemple, pour les évènements provenant de la souris, les méthodes de l'interface java.awt.event.MouseListener sont appelées avec un objet évènement de classe java.awt.event.MouseEvent dont la valeur de l'identificateur (ID) correspond à celles-ci :
Même principe pour les méthodes de l'interface java.awt.event.MouseMotionListener pour intercepter les évènements de mouvement de la souris :
Cela s'applique aussi aux interfaces qui n'ont qu'une seule méthode, comme l'interface java.awt.event.MouseWheelListener pour la notification de rotation de molette de la souris :
Cette redondance apparente d'identification du type d'évènement (par la méthode appelée et par la valeur de l'attribut ID) facilite la réutilisation d'une même méthode pour traiter plusieurs évènements de façon similaire.

## Implémentation partielle

Les interfaces d'écoute définissant au moins deux méthodes possèdent une classe dont le nom est celui de l'interface où l'on remplace le suffixe Listener par Adapter. Cette classe définit des méthodes vides et permet aux applications de la surcharger pour ne définir que certaines méthodes de l'interface.
Exemple :

# Gestion des actions asynchrones

La création d'une interface graphique pour une application en Java entraîne la création de threads pour gérer les tâches de fond comme la gestion des évènements qui créer une boucle de réception d'évènements et leur distribution aux composants visibles.
Les actions déclenchées par l'utilisateur peuvent prendre beaucoup de temps : recherche de fichiers, calculs complexes, ... Lorsque de telles actions sont appelées directement depuis les écouteurs d'évènements, l'interface graphique est bloquée durant les secondes ou minutes d'exécution. Pour éviter ce problème, il faut gérer correctement les appels à ces actions de manière asynchrone.

## Action asynchrone

Pour éviter qu'une action dont l'exécution est longue empêche l'utilisateur d'utiliser l'interface graphique, il faut créer un thread spécifique.
Exemple : Une action longue exécutée par clic sur un bouton :
La méthode demarrerAction crée et démarre un thread pour exécuter l'action et retourne sans attendre la fin d'exécution afin de ne pas bloquer l'interface graphique :

## Mise à jour de l'interface

Une action asynchrone peut avoir besoin de mettre à jour l'interface graphique : indiquer que l'action est terminée, afficher le fichier ou l'image chargée ou le résultat d'un calcul, ... Pour cela, les méthodes des composants doivent être appelées par le thread de gestion des évènements afin d'éviter les conflits. Il suffit pour cela d'appeler la méthode statique invokeLater de la classe javax.swing.SwingUtilities.

# Apparence de l'interface

L'apparence et le comportement (look and feel en anglais) des composants Swing d'une application sont modifiables et configurables par le gestionnaire d'apparence et de comportement. L'apparence et le comportement varient plus largement pour les boîtes de dialogue standards telle celle de l'ouverture de fichiers où les composants utilisés diffèrent :

## Apparence de l'application

Les composants Swing ont une apparence et un comportement (look and feel en anglais) modifiables définis par le gestionnaire d'apparence et de comportement. L'apparence peut ensuite être modifiée pour chaque composant en appelant les méthodes communes :
* setBackground(Color) : Changer la couleur de fond.
* setForeground(Color) : Changer la couleur de premier plan (texte, ligne en général).
* setFont(Font) : Changer la police de caractères, si le composant affiche du texte.
* setBorder(Border) : Changer la bordure autour du composant (aucune par défaut en général).
Les méthodes get correspondantes existent également permettant d'obtenir la valeur courante.
Le gestionnaire d'apparence et de comportement par défaut est Metal, disponible quel que soit la plateforme. La classe UIManager permet de changer l'apparence et le comportement en appelant la méthode setLookAndFeel et en lui passant le nom de la classe implémentant l'apparence et le comportement voulus.
Si l'application effectue un changement, il faut le faire avant la création des fenêtres concernées. Par exemple dans la méthode main :
Si l'application effectue le changement après création des fenêtres, la mise à jour doit se faire explicitement en appelant la méthode statique SwingUtilities.updateComponentTreeUI pour chaque fenêtre à mettre à jour en argument.

## Aperçu

Les copies d'écran ci-dessous montrent l'apparence de divers composants Swing selon l'apparence sélectionnée. Les apparences disponibles dépendent du système d'exploitation sous lequel tourne l'application Java. L'apparence peut modifier les couleurs, la police de caractère par défaut, la taille, les marges des différents composants. Par contre, l'apparence du bord de la fenêtre ne change pas.
Sous windows 7 :
* Metal
* Nimbus
* CDE/Motif
* Windows
* Windows Classic
L'aspect de tous les composants changent, mais celui du bord de la fenêtre ne change pas par défaut. Le changement de l'aspect du bord de fenêtre n'est pas supporté par toutes les apparences. La seule apparence à le supporter est le look&feel Metal. Pour activer le changement d'apparence de décoration des fenêtres et boîtes de dialogue, il faut appeler ces méthodes (les deux en général) :
Ces deux méthodes doivent être appelées avant la création des fenêtres et boîtes de dialogue. On peut obtenir le même effet sur une fenêtre individuelle :
Pour une boîte de dialogue :
La personnalisation de l'apparence des bords de fenêtres est effectivement géré par JRootPane, dans une fenêtre sans décoration. Si le changement d'apparence de décoration des fenêtres et boîtes de dialogue est activé avec une apparence qui ne le supporte pas, la fenêtre n'a plus de bord.
L'apparence peut être configurée avant la création de fenêtre.
Par exemple, il est possible de désactiver les polices en gras de l'apparence Metal :

## Code source

Le code source ci-dessous est celui de l'application ayant permis les captures d'écrans de ce chapitre.

# Contenu en HTML

La plupart des composants Swing supporte le texte en HTML. Cela permet d'ajouter facilement du texte formaté pour améliorer la présentation de l'interface graphique.

## Affichage en HTML
### JLabel, JButton

    Pour un rendu HTML, le texte doit être encadré par <html> et </html>.
    Exemple :
    JLabel l_title = new JLabel("<html>Le titre avec <b>une partie en gras</b>,"+ " et <span style=\"color:blue;\">une autre partie en bleu</span>.</html>");

### JTextPane

    En plus d'encadrer le texte par <html> et </html>, il faut spécifier le type de contenu, car ce composant supporte plusieurs types de document.
    Exemple :
    JTextPane tp_exemple = new JTextPane; tp_exemple.setContentType("text/html"); tp_exemple.setText("<html><head><style>p { margin: 20px 0; }</style></head>"+ "<body><h1>Titre</h1><p>Le texte avec <b>une partie en gras</b>,"+ " et <span style=\"color:blue;\">une autre partie en bleu</span>.</p></body></html>");
    Ce composant mémorise la feuille de style définie par l'entête (head) du source HTML.
    Il faut forcer le changement de type pour la supprimer comme montré ci-dessous :
    // Force la suppression de la feuille de style mémorisée : tp_exemple.setContentType("text/plain"); // Utiliser les propriétés du composant comme style par défaut (couleurs, police de caractères) : tp_exemple.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, true); tp_exemple.setContentType("text/html"); tp_exemple.setText("<html><h1>Autre titre</h1><p>Autre texte.</p></html>");

## Limitations

Swing supporte HTML 4 et CSS 1.0 de manière limitée.

### Limitations HTML

Éléments HTML supportés par Swing :
| Élément(s) | Supporté(s) |
| <p> <span> <div> | Oui |
| <b> <i> <u> <sup> <sub> | Oui |
| <strong> <em> | Oui |
| <br> <hr> | Oui |
| <ins> <del> | Oui, mais rendu spécifique |
| <pre> | Non |
Attributs supportés par Swing :
| Attribut(s) | Supporté(s) |
| href src | Oui |
| class style | Oui |

### Limitations CSS

Attributs supportés :
| Attribut(s) | Supporté(s) |
| color background | Oui |
| font font-family | Oui |
| border border-spacing | Oui |
| border-top border-bottom border-left border-right border-collapse | Non |
| margin padding | Oui |
| margin-top margin-bottom margin-left margin-right | Non |
| padding-top padding-bottom padding-left padding-right | Non |

## Tester l'affichage en HTML

L'application dont le code source est ci-dessous permet de tester l'affichage en HTML avec un composant JTextPane.

### Code source
## Images

Le contenu HTML peut faire référence à des images. Par défaut, les chemins relatifs ne sont pas supportés, seul un chemin absolu permet l'affichage de l'image. Les URLs distantes sont également supportées pour les protocoles supportés par Java (HTTP, HTTPS).
Les chemins et URL relatifs sont supportés quand une URL de base est fournie à la méthode setPage(url) (url de type java.lang.String ou java.net.URL) qui charge la page correspondante. Cette méthode permet le chargement de pages HTML simples (HTML 4 et CSS 1), sans Javascript. Elle ne peut donc pas servir à afficher un site web.

# Créer un composant

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.

# Créer une case à cocher à trois états

Ce chapitre complète le sujet du précédent en montrant comment créer un composant à partir d'un composant existant. Ce chapitre décrira la création d'une case à cocher à trois états :
* Coché : L'option est sélectionnée,
* Non coché : L'option n'est pas sélectionnée,
* Inconnu ou partiel : Il s'agit d'un état utilisé soit quand l'option n'a pas d'état défini par l'utilisateur, soit parce qu'une partie des éléments qu'implique la sélection de l'option est sélectionnée (sélection partielle).

## Base du composant

La case à cocher sera basée sur un label (classe JLabel) pour afficher le texte, associé à une icône pour afficher l'état. L'état du composant dépendra de l'état de la case, mais aussi du fait que la souris soit au-dessus du composant ou non. Le tableau ci-dessous présente les différentes images utilisées, avec le nom du fichier utilisé par le code du composant pour charger l'image. Téléchargez les images sous le nom indiqué, dans le répertoire du package de la classe.
| État | Non coché | Inconnu / partiel | Coché |
| Normal | checkbox_0.png | checkbox_1.png | checkbox_2.png |
| Survol de la souris | checkbox_0_over.png | checkbox_1_over.png | checkbox_2_over.png |

## État du composant

Le composant a trois états possibles. Contrairement à une case à cocher normale, le type booléen ne suffit plus à représenter l'état. Il faut donc créer une énumération :
On ajoute deux méthodes retournant l'état suivant, selon deux modes de transition différents :
* Mode trois états : Non coché → Coché → Inconnu → Non coché.
* Mode deux états (case à cocher classique) : Inconnu → Non coché → Coché → Noncoché.

## Classe de la case à cocher

Le code ci-dessous présente la première version de la classe du composant, complétée au fil des sections de ce chapitre. La classe implémente l'interface java.awt.ItemSelectable des objets contenant des éléments sélectionnables (la case à cocher elle-même).

## Gestion de l'état du composant

La méthode suivante est appelée par plusieurs autres méthodes pour mettre à jour l'icône affichée en fonction de l'état et des indicateurs de la case à cocher.
Elle est utilisée par exemple par la méthode modifiant l'indicateur de survol de la souris :
Et également par les méthodes modifiant l'état de la case à cocher, en mode trois états ou en mode deux états.
La méthode nextState met la case à cocher dans l'état suivant, selon le mode de cycle courant.

## Déclencher une action

Beaucoup de composants Swing permettent de déclencher une action sur certains évènements. Les méthodes ci-dessous fournissent une implémentation pour définir un nom d'action et enregistrer des écouteurs de déclenchement d'action.

## Constructeurs

Le constructeur de la case à cocher définit l'écouteur d'évènements de la souris qui appelle les méthodes vues précédemment.

## Test du composant

Le code source pour tester la case à cocher est présenté ci-dessous :

# Dessin d'un composant

La création d'un composant directement à partir de la classe javax.swing.JComponent nécessite de définir une méthode pour redessiner le composant quand il est visible. Ce chapitre décrit comment le faire en détails.
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).

## Contexte graphique

Un objet de classe java.awt.Graphics est passé aux méthodes d'affichage du composant. Il s'agit du contexte graphique, gérant les attributs courants de dessin (couleur, style des lignes, police de caractères, transformations...) et possédant des méthodes de tracé de formes, de texte, d'images...
La classe réelle du contexte graphique passé aux méthode d'affichage est java.awt.Graphics2D possédant des méthodes supplémentaires améliorant l'affichage en 2D. Il est donc nécessaire de convertir l'objet fourni pour pouvoir appeler ces méthodes avancées.

### Attributs courants de dessin

Le contexte graphique possède des accesseurs de lecture et écriture pour les attributs de dessin à utiliser par les prochaines méthodes appelées :
* Couleur courante pour dessiner : setColor(Color)
* Police de caractère courante : setFont(Font)
Attributs supplémentaires ajoutés par la classe java.awt.Graphics2D :
* Couleur courante pour effacer : setBackgroundColor(Color)
* Composition avec les pixels existant : setComposite(Composite)
* Style des lignes (épaisseur, continu/tirets/..., extrémités, jointure de lignes d'un polygone) : setStroke(Stroke)
* Motif de remplissage : setPaint(Paint)
* Déformation des coordonnées (translation, rotation...) : setTransform(AffineTransform)
Exemple :

### Dessin de formes

Dans les exemples suivants, g représente le contexte graphique de classe java.awt.Graphics.

#### Méthode de tracé de contour de formes

    Les méthodes dont le nom commence par draw dessine le contour de formes et utilise la couleur et le style de tracé courants du contexte graphique.

#### Méthode de tracé de formes remplies

    Les méthodes dont le nom commence par fill dessine la surface remplie de formes et utilise la couleur et le motif courants du contexte graphique.
Les formes fermées possèdent à la fois une méthode de tracé du contour et une méthode de tracé de la forme remplie.

#### Tracer une ligne entre deux points

    g.drawLine(x1,y1, x2,y2)
    La méthode drawLine trace une ligne entre les deux points dont les coordonnées sont spécifiées en arguments, en utilisant le style courant de la ligne qui par défaut est une ligne continue d'un pixel d'épaisseur.

#### Tracer un rectangle

    g.drawRect(x,y, width,height)
    La méthode drawRect trace un rectangle de la taille spécifiée à partir des coordonnées du point supérieur gauche spécifiées. Cette méthode trace un rectangle qui s'étend sur la largeur et la hauteur spécifiées additionnées à l'épaisseur courante de la ligne (1 pixel par défaut). Les largeurs ou hauteurs négatives sont acceptées.

#### Remplir un rectangle

    g.fillRect(x,y, width,height)
    La méthode fillRect trace un rectangle rempli de la taille spécifiée à partir des coordonnées du point supérieur gauche spécifiées. Les largeurs ou hauteurs négatives ne sont pas supportées.

#### Tracer un rectangle 3D

    g.drawRect3D(x,y, width,height, boolean raised)
    La méthode drawRect trace un rectangle de la taille spécifiée à partir des coordonnées du point supérieur gauche spécifiées. Cette méthode trace un rectangle avec un bord donnant un effet 3D d'élévation quand raised=true ou d'abaissement quand raised=false. Ce rectangle s'étend sur la largeur et la hauteur spécifiées additionnées à l'épaisseur courante de la ligne (1 pixel par défaut). Les largeurs ou hauteurs négatives sont acceptées.

#### Remplir un rectangle 3D

    g.fillRect3D(x,y, width,height, boolean raised)
    La méthode fillRect3D remplit un rectangle de la taille spécifiée à partir des coordonnées du point supérieur gauche spécifiées. Cette méthode remplit un rectangle avec un bord donnant un effet 3D d'élévation quand raised=true ou d'abaissement quand raised=false. Ce rectangle s'étend sur la largeur et la hauteur spécifiées additionnées à l'épaisseur courante de la ligne (1 pixel par défaut). Les largeurs ou hauteurs négatives ne sont pas supportées.

#### Tracer un rectangle arrondi

    g.drawRect(x,y, width,height, arc_width, arc_height)
    La méthode drawRect trace un rectangle arrondi de la taille spécifiée à partir des coordonnées du point supérieur gauche spécifiées. Les coins du rectangle sont arrondis avec des quarts d'ellipse dont les diamètres horizontaux et verticaux sont spécifiés par les arguments arc_width et arc_height respectivement. Cette méthode trace un rectangle qui s'étend sur la largeur et la hauteur spécifiées additionnées à l'épaisseur courante de la ligne (1 pixel par défaut). Les largeurs ou hauteurs négatives sont acceptées.

#### Remplir un rectangle arrondi

    g.fillRect(x,y, width,height, arc_width, arc_height)
    La méthode fillRect trace un rectangle arrondi rempli de la taille spécifiée à partir des coordonnées du point supérieur gauche spécifiées. Les coins du rectangle sont arrondis avec des quarts d'ellipse dont les diamètres horizontal et vertical sont spécifiés par les arguments arc_width et arc_height respectivement. Les largeurs ou hauteurs négatives ne sont pas supportées.

#### Tracer un ovale

    g.drawOval(x,y, width,height)
    La méthode drawOval trace un ovale contenu dans un rectangle de la taille spécifiée à partir des coordonnées du point supérieur gauche spécifiées. Cette méthode trace un ovale qui s'étend sur la largeur et la hauteur spécifiées additionnées à l'épaisseur courante de la ligne (1 pixel par défaut). Les largeurs ou hauteurs négatives sont acceptées.

#### Remplir un ovale

    g.fillOval(x,y, width,height)
    La méthode fillOval trace un ovale rempli contenu dans un rectangle de la taille spécifiée à partir des coordonnées du point supérieur gauche spécifiées. Les largeurs ou hauteurs négatives ne sont pas supportées.

#### Tracer un arc

    g.drawArc(x,y, width,height, start_angle, ext_angle)
    La méthode drawArc trace un arc comme une partie d'un ovale contenu dans un rectangle de la taille spécifiée à partir des coordonnées du point supérieur gauche spécifiées. Cette méthode trace l'arc en démarrant à l'angle spécifié en radians, et qui s'étend sur le nombre de radians spécifié. Les largeurs ou hauteurs négatives sont acceptées.

#### Tracer un polygone

    g.drawPolygon(int x, int y, int point_count)
    g.drawPolygon(Polygon p)
    La méthode drawPolygon trace un polygone à partir des deux tableaux de coordonnées spécifiés soit directement, soit indirectement en utilisant un objet de classe java.awt.Polygon. Le dernier point est relié au premier pour fermer le polygone.

#### Remplir un polygone

    g.fillPolygon(int x, int y, int point_count)
    g.fillPolygon(Polygon p)
    La méthode fillPolygon remplit un polygone à partir des deux tableaux de coordonnées spécifiés soit directement, soit indirectement en utilisant un objet de classe java.awt.Polygon. Le dernier point est relié au premier pour fermer le polygone.

#### Tracer une ligne multipoints

    g.drawPolyline(int x, int y, int point_count)
    La méthode drawPolyline trace une ligne entre les points à partir des deux tableaux de coordonnées spécifiés directement. Le dernier point n'est pas relié au premier.

### Dessin de forme quelconque

L'interface java.awt.Shape définit les méthodes communes des formes pour tester l'appartenance d'un point ou d'un rectangle, tester si la forme intersecte un rectangle, obtenir le rectangle englobant, et obtenir un itérateur de chemin parcourant le contour de la forme. Ce dernier groupe de méthodes est utilisé pour le dessin de la forme.

#### Tracer une forme quelconque

    g.draw(Shape)
    La méthode draw trace le contour de la forme spécifiée.

#### Remplir une forme quelconque

    g.fill(Shape)
    La méthode fill remplit la surface de la forme spécifiée.
Différentes classes du package java.awt.geom implémentent cette interface. Celles dont le nom se termine par 2D sont des classes abstraites possédant deux sous-classes concrètes statiques internes appelées Double et Float permettant d'utiliser des coordonnées de type double et float respectivement.
* Line2D, Line2D.Double, Line2D.Float
* Rectangle2D, Rectangle2D.Double, Rectangle2D.Float
* Rectangle dérive de la classe Rectangle2D pour des coordonnées de type int.
* RoundRectangle2D, RoundRectangle2D.Double, RoundRectangle2D.Float
* Polygon
* Path2D, Path2D.Double, Path2D.Float
* Arc2D, Arc2D.Double, Arc2D.Float
* Ellipse2D, Ellipse2D.Double, Ellipse2D.Float
* CubicCurve2D, CubicCurve2D.Double, CubicCurve2D.Float
* QuadCurve2D, QuadCurve2D.Double, QuadCurve2D.Float
* Area englobe une autre forme et permet des opérations ensemblistes pour composer une aire complexe : union (méthode add), intersection (méthode intersect), soustraction (méthode subtract), soustraction symétrique (méthode exclusiveOr).

### Dessin de texte

Les méthodes de tracé de texte utilisent la couleur et la police de caractères courantes du contexte graphique. Les coordonnées spécifiées sont celles du point du début de ligne (à gauche pour le sens d'écriture de gauche à droite, ou à droite pour l'autre sens) situé au niveau de la ligne de base des caractères.

#### Tracer une chaîne de caractères

    g.drawString(String, int x, int y)
    g.drawString(String, float x, float y)
    g.drawString(AttributedCharacterIterator, int x, int y)
    g.drawString(AttributedCharacterIterator, float x, float y)
    La méthode drawString dessine la chaîne de caractère qu'elle soit spécifiée sous la forme d'une chaîne java.lang.String, d'un séquence de caractères avec attributs java.text.AttributedCharacterIterator. La classe java.awt.Graphics2D surcharge les méthodes pour autoriser des coordonnées sous forme de nombres à virgule flottante.

#### Tracer un tableau de caractères

    g.drawChars(char, int offset, int length, int x, int y)
    La méthode drawChars dessine la chaîne de caractère représenté par le tableau de caractères spécifié.

#### Tracer un tableau d'octets

    g.drawBytes(byte, int offset, int length, int x, int y)
    La méthode drawChars dessine la chaîne de caractère représenté par le tableau de caractères spécifié.

#### Police de caractères

La police de caractères utilisée est celle définie par la méthode void setFont(Font f) du contexte graphique. Elle est initialisée avec celle assignée au composant.
* Voir police de caractères

#### Gras, italique, soulignement et barré

Les styles gras et italiques sont gérés par la classe java.awt.Font (constantes PLAIN, BOLD, ITALIC, BOLD_ITALIC).
Le soulignement doit être géré par le code de dessin après ou avant avoir tracé le texte. La classe java.awt.Font fournit des informations pour le soulignement (distance et épaisseur de trait) via une instance de la classe java.awt.font.LineMetrics. Le code ci-dessous affiche un texte souligné, en exploitant ces informations et en utilisant l'objet java.awt.FontMetrics pour mesurer le texte et obtenir la longueur de la ligne de soulignement du texte.
Le style barré utilise le même principe en exploitant les informations retournées par les méthodes getStrikethroughOffset et getStrikethroughThickness. Dans le code précédent, il suffit de remplacer les valeurs affectées aux variables line_offset et line_thickness :
Les styles souligné et barré ne sont pas gérés directement par la classe java.awt.Font car il s'agit d'éléments graphiques (lignes) séparés du texte, pouvant avoir une couleur différente du texte, ou une épaisseur différente de celle indiquée par la classe java.awt.font.LineMetrics.

### Afficher une image

Le contexte graphique possède plusieurs variantes de la méthode drawImage permettant d'afficher une image (instance de java.awt.Image) :

#### drawImage(image, x, y, observateur)

    Afficher l'image à sa taille normal aux coordonnées indiquées pour le coin supérieur gauche.

#### drawImage(image, x, y, width, height, observateur)

    Afficher l'image à la taille spécifiée par width (largeur) et height (hauteur) aux coordonnées indiquées pour le coin supérieur gauche.

#### drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observateur)

    Afficher une portion de l'image définie par les coordonnées source des coins de la zone rectangulaire sx1, sy1, sx2, sy2 dans la zone destination définie par les coordonnées source des coins de la zone rectangulaire dx1, dy1, dx2, dy2. Cette méthode permet donc à la fois d'afficher une partie de l'image et de la redimensionner si nécessaire.
Avec ces méthodes, les pixels transparents laissent voir ce qui était dessiné avant.
Paramètres communs :

#### image

    Image à afficher (instance de java.awt.Image).

#### observateur

    Implémentation de l'interface java.awt.image.ImageObserver à notifier de la progression du chargement de l'image. Si l'image est déjà chargée auparavant, ce paramètre peut être null.
Exemple :
Ces méthodes possèdent une variante acceptant une couleur à afficher comme couleur de fond pour les images transparentes.

### Composant transparent

Par défaut, un composant est opaque, ce qui signifie qu'il doit dessiner toute la zone rectangulaire correspondant à sa taille.
Si la méthode paint ou la méthode paintComponent ne dessine pas toute la zone, pour laisser paraître le fond du contenant (un composant non rectangulaire, comme un bouton arrondi par exemple), des artifacts peuvent apparaître de manière sporadique ou non car Swing réutilise le même contexte graphique pour peindre plusieurs composants. Dans ce cas, il ne faut pas oublier de déclarer le composant comme transparent pour indiquer qu'il faut un contexte graphique propre pour peindre le composant, dans le constructeur de la classe du composant :

## Qualité de l'affichage

Par défaut, les algorithmes de dessin des méthodes du contexte graphique privilégient la vitesse de rendu à la qualité. La méthode addRenderingHints(Map<?, ?> hints) de la classe java.awt.Graphics2D permet de changer différentes options pour améliorer la qualité.
Le paramètre hints est un dictionnaire associant des clés définies par les constantes KEY_ de la classe java.awt.RenderingHints à des valeurs définies par les constantes VALUE_.
Quand les paramètres de qualité ne changent pas, il est recommandé de créer le dictionnaire une seule fois, comme dans l'exemple suivant :
Ce dictionnaire est ensuite utilisable dans la méthode d'affichage du composant :

### Propriétés générales de qualité

Les propriétés générales de qualités s'appliquent quand aucune autre propriété équivalente plus spécifique n'est applicable.
* RenderingHints.KEY_RENDERING : Qualité générale d'affichage.
| Valeur | Description |
| RenderingHints.VALUE_RENDER_DEFAULT | Option par défaut. |
| RenderingHints.VALUE_RENDER_SPEED | Privilégier la vitesse d'affichage. |
| RenderingHints.VALUE_RENDER_QUALITY | Privilégier la qualité d'affichage. |
* Dithering alternant des pixels noirs et blancs pour afficher les échelles de gris de l'image.RenderingHints.KEY_DITHERING : Gestion des couleurs sur un écran où leur nombre est limité.
| Valeur | Description |
| RenderingHints.VALUE_DITHER_DEFAULT | Option par défaut. |
| RenderingHints.VALUE_DITHER_DISABLE | Pas de dithering, les couleurs sont arrondies à la plus proche sans propagation d'erreur. |
| RenderingHints.VALUE_DITHER_ENABLE | Activer le dithering permettant la propagation d'erreur d'arrondi des couleurs sur les pixels voisins. |
* RenderingHints.KEY_ALPHA_INTERPOLATION : Qualité de la transparence des couleurs (canal alpha)
| Valeur | Description |
| RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT | Option par défaut. |
| RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED | Privilégier la vitesse de calcul de la transparence. |
| RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY | Privilégier la qualité de calcul de la transparence. |

### Qualité des lignes

Une partie des propriétés de qualité concerne le tracé des lignes par les méthodes dessinant un contour : drawLine, drawRect, drawOval, drawShape... Cela concerne les méthodes dont le nom commence par draw sauf drawImage, drawRenderedImage, drawRenderableImage et drawString, drawBytes, drawChars.
* RenderingHints.KEY_ANTIALIASING : option de réduction des effets pixelisés des lignes et des bords des formes géométriques.
| Valeur | Description |
| RenderingHints.VALUE_ANTIALIAS_DEFAULT | Utiliser l'option par défaut |
| RenderingHints.VALUE_ANTIALIAS_OFF | Sans antialiasing |
| RenderingHints.VALUE_ANTIALIAS_ON | Avec antialiasing |
* RenderingHints.KEY_STROKE_CONTROL : Autoriser une correction de la géométrie pour améliorer la qualité du tracé.
| Valeur | Description |
| RenderingHints.VALUE_STROKE_DEFAULT | Utiliser l'option par défaut |
| RenderingHints.VALUE_STROKE_NORMALIZE | Normaliser pour améliorer l'uniformité ou l'espacement des lignes et l'esthétique globale. |
| RenderingHints.VALUE_STROKE_PURE | Géométrie non modifiée, affiché tel quel avec une précision en dessous du pixel. |

#### Aperçu pour une épaisseur de 1.0

| | RenderingHints.VALUE_ANTIALIAS_OFF | RenderingHints.VALUE_ANTIALIAS_ON |
| RenderingHints. VALUE_STROKE_NORMALIZE | | |
| RenderingHints. VALUE_STROKE_PURE | | |

#### Aperçu pour une épaisseur de 1.5

| | RenderingHints.VALUE_ANTIALIAS_OFF | RenderingHints.VALUE_ANTIALIAS_ON |
| RenderingHints. VALUE_STROKE_NORMALIZE | | |
| RenderingHints. VALUE_STROKE_PURE | | |

### Qualité du texte

Trois autres propriétés de qualité concerne le tracé de texte, qu'il soit sous forme de chaîne de caractère (drawString), d'un tableau d'octets (drawBytes) ou d'un tableau de caractères (drawChars).
* RenderingHints.KEY_FRACTIONALMETRICS : utiliser une précision en dessous du pixel pour positionner les caractères selon le vecteur d'avance mis à l'échelle de la taille de la police de caractères ou bien arrondir à l'entier le plus proche.
| Valeur | Description |
| RenderingHints.VALUE_FRACTIONALMETRICS_DEFAULT | Utiliser l'option par défaut |
| RenderingHints.VALUE_FRACTIONALMETRICS_OFF | Utiliser des mesures fractionnaires. |
| RenderingHints.VALUE_FRACTIONALMETRICS_ON | Arrondir à l'entier le plus proche. |
* RenderingHints.KEY_TEXT_ANTIALIASING : utiliser de l'antialiasing pour le texte ou bien arrondir au pixel le plus proche.
| Valeur | Description |
| RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT | Utiliser l'option par défaut |
| RenderingHints.VALUE_TEXT_ANTIALIAS_OFF | Utiliser l'antialias. |
| RenderingHints.VALUE_TEXT_ANTIALIAS_ON | Sans antialias. |
| RenderingHints.VALUE_TEXT_ANTIALIAS_GASP | Utiliser l'antialias (VALUE_TEXT_ANTIALIAS_ON) ou non (VALUE_TEXT_ANTIALIAS_OFF) selon l'information de la table GASP présente dans la police de caractères pour la taille utilisée. Cette information est en général présente dans les polices TrueType, cependant, si l'information n'est pas présente, la valeur par défaut de l'algorithme est utilisée (VALUE_TEXT_ANTIALIAS_DEFAULT). Cette option peut ne pas avoir un comportement consistant quand une police de caractères logique est utilisée vu qu'elle est composée de plusieurs polices physiques. |
| RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB | Utiliser l'antialias sur écran LCD, dans le sens horizontal, avec l'ordre de couleur des sous-pixels RGB (rouge, vert, bleu). |
| RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HBGR | Utiliser l'antialias sur écran LCD, dans le sens horizontal, avec l'ordre de couleur des sous-pixels BGR (bleu, vert, rouge). |
| RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VRGB | Utiliser l'antialias sur écran LCD, dans le sens vertical, avec l'ordre de couleur des sous-pixels RGB (rouge, vert, bleu). |
| RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VBGR | Utiliser l'antialias sur écran LCD, dans le sens vertical, avec l'ordre de couleur des sous-pixels BGR (bleu, vert, rouge). |
* RenderingHints.KEY_TEXT_LCD_CONTRAST : ajustement du contraste pour l'antialiasing sur écran LCD.
| Valeur | Description |
| int | Valeur entre 100 et 250 pour ajuster le contraste du texte. Typiquement en noir sur fond blanc, la valeur 100 donne un contraste fort, et 250 un contraste faible. L'intervalle typique est entre 140 et 180. |
Le réglage de l'antialiasing du texte dont le nom de la valeur contient LCD est réservé aux écrans LCD ou assimilés où les pixels voisins sont tellement proches qu'il est possible que la composante bleue du pixel se mélange à la rouge du pixel voisin. Ce mélange permettant d'augmenter artificiellement la densité de pixels est la technique utilisée pour l'antialiasing LCD du texte, avec différentes variantes selon la disposition des composantes de couleur composant les pixels de l'écran. La valeur à utiliser dépend donc du modèle de l'écran.
Dans l'image ci-dessus, l'antialiasing de texte est RenderingHints.VALUE_TEXT_ANTIALIAS_HRGB : H pour Horizontal, RGB représentant l'ordre des composants des pixels de gauche à droite : rouge, vert, bleu. La couleur rouge borde le côté gauche des lettres en noir pour se mélanger au fond blanc, tandis que du bleu borde le côté opposé des lettres. Les côtés des couleurs rouges et bleues serait inversés pour un texte blanc sur fond noir.
Antialiasing LCD ordre des composants rouge, vert, bleu dans le sens horizontal (RenderingHints.VALUE_TEXT_ANTIALIAS_HRGB) :
| Zoom sur le bord gauche des lettres en noir sur fond blanc | Zoom sur le bord gauche des lettres en noir sur fond blanc | Zoom sur le bord gauche des lettres en noir sur fond blanc | Zoom sur le bord droit des lettres en noir sur fond blanc | Zoom sur le bord droit des lettres en noir sur fond blanc | Zoom sur le bord droit des lettres en noir sur fond blanc |
| blanc | rouge | noir | noir | bleu | blanc |
Sur un écran LCD dont l'ordre des composants correspond, la composante bleue se mélange au rouge et vert à proximité, idem pour le rouge à droite avec les composantes bleue et verte.
Sur un écran LCD dont l'ordre des composants serait l'ordre inverse cela donnerait un effet indésirable de tracé parallèle rendant le texte flou :
Antialiasing LCD ordre des composants rouge, vert, bleu dans le sens horizontal (RenderingHints.VALUE_TEXT_ANTIALIAS_HRGB) sur un écran dont l'ordre des composants est bleu, vert, rouge :
| Zoom sur le bord gauche des lettres en noir sur fond blanc | Zoom sur le bord gauche des lettres en noir sur fond blanc | Zoom sur le bord gauche des lettres en noir sur fond blanc | Zoom sur le bord droit des lettres en noir sur fond blanc | Zoom sur le bord droit des lettres en noir sur fond blanc | Zoom sur le bord droit des lettres en noir sur fond blanc |
| blanc | rouge | noir | noir | bleu | blanc |
Il faut dans ce cas utiliser l'ordre inverse :
Antialiasing LCD ordre des composants bleu, vert, rouge dans le sens horizontal (RenderingHints.VALUE_TEXT_ANTIALIAS_HBGR) sur un écran dont l'ordre des composants est bleu, vert, rouge :
| Zoom sur le bord gauche des lettres en noir sur fond blanc | Zoom sur le bord gauche des lettres en noir sur fond blanc | Zoom sur le bord gauche des lettres en noir sur fond blanc | Zoom sur le bord droit des lettres en noir sur fond blanc | Zoom sur le bord droit des lettres en noir sur fond blanc | Zoom sur le bord droit des lettres en noir sur fond blanc |
| blanc | bleu | noir | noir | rouge | blanc |
Sur un écran non LCD, où les pixels seraient trop éloignés pour que la composante d'un pixel puissent se mélanger au pixel voisin, ou dont les pixels n'ont pas une haute densité, l'effet ne fonctionne pas correctement et un bord rouge ou bleu est visible. Pour ce cas, il est recommandé d'utiliser RenderingHints.VALUE_TEXT_ANTIALIAS_ON.

#### Aperçu de l'antialiasing et des mesures fractionnaires

Un aperçu de l'effet des différentes valeurs (lignes) pour RenderingHints.KEY_TEXT_ANTIALIASING (sans le préfixe commun RenderingHints.VALUE_TEXT_ANTIALIAS_, et pour le contraste par défaut) est présenté ci-dessous, combiné aux effets de RenderingHints.KEY_FRACTIONALMETRICS (colonnes).
* OFF
* ON
* LCD_HRGB
* LCD_HBGR
* LCD_VRGB
* LCD_VBGR

#### Aperçu de l'ajustement du contraste

Différentes valeurs d'ajustement du contraste pour VALUE_TEXT_ANTIALIAS_LCD_HRGB
* 100
* 140
* 180
* 250

### Qualité des images

Les autres propriétés de qualité concerne l'affichage des images par les méthodes drawImage, drawRenderedImage et drawRenderableImage.
* RenderingHints.KEY_INTERPOLATION : option d'affichage d'images redimensionnées.
| Valeur | Description |
| RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR | Pixel voisin le plus proche. |
| RenderingHints.VALUE_INTERPOLATION_BILINEAR | La couleur d'un pixel est déterminée linéairement par les 4 pixels voisins les plus proches. |
| RenderingHints.VALUE_INTERPOLATION_BICUBIC | La couleur d'un pixel est déterminée par une fonction cubique sur les 9 pixels voisins les plus proches. |
* RenderingHints.KEY_COLOR_RENDERING : option de qualité d'affichage des couleurs.
| Valeur | Description |
| RenderingHints.VALUE_COLOR_RENDER_DEFAULT | Option par défaut. |
| RenderingHints.VALUE_COLOR_RENDER_SPEED | Conversion rapide des couleurs. |
| RenderingHints.VALUE_COLOR_RENDER_QUALITY | Conversion des couleurs privilégiant la qualité. |

## Références

* ↑ Grid-fitting and Scan-conversion Procedure Table https://docs.microsoft.com/en-us/typography/opentype/spec/gasp

# Dessin d'une étoile

Le dessin d'un composant utilise différente formes de base : lignes, rectangles, polygones. Le but de cette section est de dessiner des étoiles de cette forme :
Afin d'avoir un composant générique permettant d'afficher le texte sous une série d'étoiles correspondant à un niveau d'appréciation, un niveau de qualité, ...

## Composant

La classe DisplayLevel ci-dessous définit le composant : état courant et couleurs utilisées. Elle sera complétée progressivement au cours de ce chapitre.

## Dessin d'une étoile

Les lignes de la forme n'étant pas courbées, le dessin d'un polygone sera utilisé.
Le polygone sera composé de 10 points :
* 5 points pour les pointes, à la distance r du centre du cercle englobant (voir image ci-contre).
* 5 points entre les pointes, à la distance s du centre du cercle englobant (voir image ci-contre).
La distance r représente le rayon du cercle englobant qui déterminera la taille des étoiles dessinées. La distance s est calculable à partir du rayon r par la formule suivante :
$s={\frac {r}{\phi ^{2}}}={\frac {r}{\phi +1}}$
Avec $\phi ={\frac {{\sqrt {5}}+1}{2}}$
Les points étant radialement espacés régulièrement, l'angle entre deux points est de ${\frac {360}{10}}=36$ degrés.

### Coordonnées du polygone

Les coordonnées des points du polygone sont pré-calculées et stockées dans un tableau pour un rayon de 10000 pixels. Ces coordonnées de base servent au calcul des coordonnées selon la taille d'étoile voulue, et sont relatives au centre du cercle situé aux coordonnées (0,0).
Un objet de la classe java.awt.Polygon est utilisé pour représenter le polygone adapté à la taille de l'étoile selon celle du composant. Les points sont calculés en multipliant les coordonnées pré-calculées des 10 points par le rayon voulu et en divisant le résultat par 10000. Pour les points intermédiaires, on peut aussi ajouter un coefficient pour changer la forme de l'étoile :
* Dessin d'une étoile avec coefficient de 80%.
* Dessin d'une étoile avec coefficient de 100%.
* Dessin d'une étoile avec coefficient de 150%.

### Calcul du polygone

Le polygone est calculé, si besoin, au moment de dessiner le composant :
Les coordonnées dans le polygone shape sont fixes, mais le composant a besoin de le dessiner plusieurs fois à différents endroits. La méthode moveShapeTo ci-dessous déplace les points en mettant à jour le point de référence servant à calculer le vecteur de translation à appliquer :

### Dessiner le composant

Il ne reste plus que le dessin du composant, utilisant plusieurs fois le polygone pour afficher les étoiles et affiche également le texte.
La méthode applyHintsTo ajuste la configuration pour améliorer la qualité d'affichage :

## Code source complet et utilisation

Le code source complet de la classe DisplayLevel est ci-dessous.
Le programme ci-dessous illustre comment le composant peut être utilisé.

# Dessin de texte et effets

Le dessin d'un composant est réalisé par les méthodes paint de la classe du composant (en Swing, la méthode void paintComponent(Graphics g)) utilisant un contexte graphique.

## Dessin de texte

Les méthodes de tracé de texte utilisent la couleur et la police de caractères courantes du contexte graphique. Les coordonnées spécifiées sont celles du point du début de ligne (à gauche pour le sens d'écriture de gauche à droite, ou à droite pour l'autre sens) situé au niveau de la ligne de base des caractères.

### Tracer une chaîne de caractères

    g.drawString(String, int x, int y)
    g.drawString(String, float x, float y)
    g.drawString(AttributedCharacterIterator, int x, int y)
    g.drawString(AttributedCharacterIterator, float x, float y)
    La méthode drawString dessine la chaîne de caractère qu'elle soit spécifiée sous la forme d'une chaîne java.lang.String, d'un séquence de caractères avec attributs java.text.AttributedCharacterIterator. La classe java.awt.Graphics2D surcharge les méthodes pour autoriser des coordonnées sous forme de nombres à virgule flottante.

### Tracer un tableau de caractères

    g.drawChars(char, int offset, int length, int x, int y)
    La méthode drawChars dessine la chaîne de caractère représenté par le tableau de caractères spécifié.

### Tracer un tableau d'octets

    g.drawBytes(byte, int offset, int length, int x, int y)
    La méthode drawChars dessine la chaîne de caractère représenté par le tableau de caractères spécifié.

#### Police de caractères

La police de caractères utilisée est celle définie par la méthode void setFont(Font f) du contexte graphique. Elle est initialisée avec celle assignée au composant.
* Voir police de caractères

#### Gras, italique, soulignement et barré

Les styles gras et italiques sont gérés par la classe java.awt.Font (constantes PLAIN, BOLD, ITALIC, BOLD_ITALIC).
Le soulignement doit être géré par le code de dessin après ou avant avoir tracé le texte. La classe java.awt.Font fournit des informations pour le soulignement (distance et épaisseur de trait) via une instance de la classe java.awt.font.LineMetrics. Le code ci-dessous affiche un texte souligné, en exploitant ces informations et en utilisant l'objet java.awt.FontMetrics pour mesurer le texte et obtenir la longueur de la ligne de soulignement du texte.
Le style barré utilise le même principe en exploitant les informations retournées par les méthodes getStrikethroughOffset et getStrikethroughThickness. Dans le code précédent, il suffit de remplacer les valeurs affectées aux variables line_offset et line_thickness :
Les styles souligné et barré ne sont pas gérés directement par la classe java.awt.Font car il s'agit d'éléments graphiques (lignes) séparés du texte, pouvant avoir une couleur différente du texte, ou une épaisseur différente de celle indiquée par la classe java.awt.font.LineMetrics.

### Exemple : Texte en dégradé de couleurs

La méthode de remplissage est représentée par une instance de la classe java.awt.Paint. L'instance par défaut remplit les formes avec une couleur de manière unie. Il existe d'autres classes de remplissage dont celles permettant de peindre une forme en dégradé de couleurs.
Le tracé de texte est en fait un remplissage et il est dont possible d'utiliser un dégradé de couleurs.
Le code ci-dessous affiche une ligne de texte en dégradé de couleurs (voir image ci-dessus).

## Utiliser la forme des caractères

Pour un affichage de texte plus évolué, il est possible d'exploiter la forme des caractères. La forme des caractères est représentée par une instance de la classe java.awt.font.GlyphVector. Cette classe possède des méthodes retournant des instances de la classe java.awt.Shape qui peuvent donc être manipulées, et notamment utilisées avec les méthodes draw et fill du contexte graphique.
Le texte tel que montré par l'image ci-dessus est dessiné par le code ci-dessous et reprend la première partie du code de la section précédente, en remplaçant l'affichage du texte par la récupération de la forme des caractères. Celle-ci est obtenue en appelant la méthode createGlyphVector de la police de caractère, en lui passant le contexte de rendu et le texte.
Cette forme des caractères est ensuite utilisée :
* pour obtenir les rectangles autour du caractère w (en position 3),
* pour tracer le contour des glyphes du texte en pointillé.

# Créer une image

Une application peut avoir besoin de créer une image, soit pour réutiliser cette image et l'afficher plusieurs fois en évitant de tout redessiner à nouveau, soit pour créer un fichier externe utilisable dans une autre application.
La création d'une image peut se faire aussi simplement que le dessin d'un composant, en utilisant un contexte graphique.

## Créer une image

La classe java.awt.image.BufferedImage permet de créer une nouvelle image. Le type d'image et sa taille en pixels doit être spécifié au constructeur.
Exemple :

## Dessiner le contenu

Le contenu est ensuite dessiné en obtenant un contexte graphique, comme celui utilisé pour dessiner un composant :
Comme le contexte graphique est de la même classe que celui utilisé pour le dessin d'un composant, les mêmes méthodes s'appliquent, donc notamment la méthode addRenderingHints pour améliorer la qualité de l'image.

## Enregistrer l'image

Enregistrer l'image se fait en utilisant la classe javax.imageio.ImageIO, en appelant la méthode statique write et en lui passant l'image, le nom du format d'image, et le chemin du fichier (de type java.io.File):

## Capture d'écran

La classe java.awt.Robot permet d'automatiser certaines actions de l'utilisateur (déplacer le curseur de souris, générer des touches, ...) et de réaliser des captures d'écrans. Son but principal est de pouvoir tester des applications automatiquement.
La classe possède deux constructeurs :

### Robot

    Constructeur d'instance pour l'écran principal.

### Robot(GraphicsDevice device)

    Constructeur d'instance pour l'écran spécifié.
Ensuite, il suffit d'appeler la méthode createScreenCapture(Rectangle bounds) pour créer une image de capture de la zone spécifiée de l'écran. L'image retournée est de type java.awt.image.BufferedImage.
Le code suivant réalise une capture de l'écran principal :
La capture est réalisée de manière asynchrone, ce qui explique l'utilisation de la classe java.awt.MediaTracker dans l'exemple précédent pour attendre que l'image soit prête à être utilisée.