Le GridBagLayout expliqué


précédentsommairesuivant

2. Le GridBagLayout pas à pas.

2.1. Définir la taille des composants.

Comme nous l'avons dit précédemment, le GridBagLayout est un gestionnaire de placement qui permet d'agencer des composants sur une grille virtuelle en se basant sur les désidératas des composants spécifiés au travers de leurs méthodes getMinimumSize() et getPreferedSize(). Si la taille du container le permet, tous les composants auront pour taille leur taille préférée, sinon, c'est la taille minimum qui leur sera attribuée, ce qui peut poser des résultats bizarres sur les champs de saisie. Ces désidératas vont également permettre de calculer la taille de chacune des cellules de la grille. Dans cette grille virtuelle, toutes les cellules d'une même ligne auront toujours la même hauteur. De même, chacune des cellules d'une même colonne auront toute la même largeur.

La hauteur d'une cellule de cette grille sera égale à la hauteur maximale parmi les composants de cette ligne. La largeur d'une cellule de la grille sera égale à la largeur maximale parmi les composants de cette colonne.

Il est inutile de définir une taille maximum par composant car le GridBagLayout ne fait jamais appel à leurs méthodes getMaximumSize(). Pour les tailles minimales et préférées, elles possèdent généralement des valeurs calculées en interne lors de l'initialisation du composant. C'est ainsi que lors du développement d'une interface graphique, il est souvent inutile de préciser les tailles minimums et préférées pour tous les composants. Certaines valeurs suffisent généralement. Prenons l'exemple d'un formulaire classique.

Image non disponible
Un formulaire classique.

Excepté pour les boutons Ok et Annuler, sur aucun autre composant nous n'avons défini de tailles minimales ni préférées. Pour les étiquettes, nous utilisons les valeurs par défaut initialisées par Swing en interne. Pour les champs de saisie et pour la liste déroulante, nous avons demandé au GridBagLayout d'étendre ces composants pour que ceux-ci s'étendent sur toute la place qui est disponible horizontalement. Au niveau des boutons, pour que ceux-ci aient la même largeur, nous avons dû leur spécifier une taille préférée identique.

L'ensemble des codes source accompagnant ce tutoriel peuvent être téléchargés iciCodes sources. Nous vous invitons à les télécharger, les compiler et les exécuter par vous-mêmes. Ceux-ci sont pleinement documentés et ont été écrit dans une optique pour faciliter leur compréhension.

2.2. Définir des contraintes.

Revenons maintenant sur la classe GridBagConstraints. Lorsque nous ajoutons un composant à un container utilisant un GridBagLayout, ce composant est généralement associé à une contrainte de type GridBagConstraints. Si aucune contrainte n'est spécifiée, une contrainte avec des valeurs par défaut sera utilisée. Nous vous recommandons de toujours spécifier la contrainte pour éviter d'avoir des surprises.

Voyons maintenant les différentes possibilités qu'offre un objet de type GridBagConstraints au travers de ses propriétés publiques. Nous allons supposer que l'orientation des containers est toujours sur LEFT_TO_RIGHT.

a. Les propriétés gridx et gridy.

Ces propriétés permettent de positionner un composant dans la grille. Prenons un petit exemple pour comprendre comment les cases de cette grille sont numérotées.

Image non disponible
Les propriétés gridx et gridy.

Dans cet exemple, nous pouvons voir que notre grille possède 4 lignes et 3 colonnes. La numérotation de la grille commence en gridx et gridy égales à zéro dans le coin supérieur gauche. Ces deux propriétés ne peuvent contenir que des valeurs entières supérieures ou égales à zéro. En se déplaçant de gauche à droite, gridx augmente de 1 par cellule. En se déplaçant vers le bas, gridy augmente de 1 par cellule.

Chacune des cellules ne doit pas forcément contenir un composant. Par exemple, la case (0,4) de cet exemple est vide.

Bien qu'il est fortement conseillé de toujours spécifié les positions x et y d'une contrainte, il est également possible d'utiliser la valeur GridBagConstraints.RELATIVE (qui est la valeur par défaut). Cette constante signifie que le composant sera placé dans une cellule à droite (gridx) ou dans une cellule en dessous (gridy) du composant précédent.

b. Les propriétés gridwidth et gridheight.

Ces propriétés permettent de préciser le nombre de cases qu'occupera un composant horizontalement (gridwidth) et verticalement (gridheight). Ces propriétés doivent être une valeur entière supérieure ou égale à 1. Cependant, il est également possible de spécifier deux constantes particulières : GridBagConstraints.REMAINDER et GridBagConstraints.RELATIVE. REMAINDER permet de préciser que ce composant est le dernier de sa ligne ou de sa colonne. Nous vous recommandons de toujours l'utiliser. RELATIVE permet de préciser l'avant dernier composant. Il occupera toutes les cellules disponibles depuis sa position jusqu'à l'avant dernière cellule.

Reprenons un nouvel exemple pour illustrer ces deux propriétés en images.

Image non disponible
Les propriétés gridwidth et gridheight.

Nous avons dessiné la grille virtuelle en rouge sur cet exemple. Les composants encadrés en bleu sont les derniers de leurs lignes. Ils ont donc des gridwidth fixées à GridBagConstraints.REMAINDER. Le composant encadré en vert est le dernier de sa ligne et de sa colonne. Les propriétés gridwidht et gridheight de sa contrainte sont donc fixées toutes les deux à GridbagConstraints.REMAINDER.

Nous pouvons également remarquer que lorsque nous utilisons la constante REMAINDER, la grille est légèrement déformée. Par exemple, sur la première et la dernière ligne de notre interface, il n'y a qu'une seule colonne qui sera aussi large que la largeur du container. De même, notre grille ne contient pas de case numérotée (4, 4). Le dernier composant sur la quatrième ligne est en (3, 4).

Nous avons utilisé la constante RELATIVE pour la liste déroulante, juste pour montrer tout les cas possibles. Mais il est clair que nous aurions pu préciser un gridwith de 2 pour la liste déroulante. Ce qui aurait donné le même résultat.

Pour le reste des composants, il suffit de compter le nombre de cases sur lesquelles le composant peut s'étendre horizontalement et verticalement et de l'attribuer respectivement aux propriétés gridwidth et gridheight de la contrainte lui étant associée. Regardons sur l'exemple l'espace réservé à la photo. Cet espace à un gridheight de 4. Il s'étend donc sur les cellules {(0,1), (0,2), (0,3), (0,4)} de notre grille.

c. La propriété insets.

La propriété insets permet de préciser des marges en pixels autour du composant à l'intérieur de la (ou des) case(s) qui lui sont allouées. Cette propriété doit être un objet de type java.awt.Insets. Par défaut, les marges sont nulles. Reprenons notre exemple précédent pour illustrer cette propriété. Nous l'avons présenté ainsi:

Image non disponible
Un formulaire avec marge.

En réalité, nous avons légèrement triché en utilisant des marges autour des composants. Si nous ne l'avions pas fait, nous aurions le résultat suivant :

Image non disponible
Un Formulaire sans marges.

Tous les composants sont collés au bord du container et ne sont pas espacés entre eux. Remarquons au passage qu'une marge peut être négative (pratique déconseillée). Ceci permet de "faire sortir" le composant de sa cellule et de déborder sur la (les) cellule(s) adjacente(s). Nous pouvons le voir sur l'image suivante ; nous avons définie une marge négative à gauche (de 15 pixels) sur le bouton radio "Homme". Nous pouvons voir qu'il déborde sur la cellule de l'étiquette "Sexe".

Image non disponible
Une marge peut être négative.

Jusqu'à présent, nous avons vu comment nous pouvons positionner un composant dans une cellule, comment lui allouer une ou plusieurs cellules et nous venons de voir comment mettre des marges autour des composants. Il est vraiment important que ces notions soient bien maîtrisées pour comprendre la suite de ce tutoriel, qui se corse légèrement. C'est pour cette raison que nous vous proposons trois petits exercices pour pouvoir vous exercer.

Exercice 1 Exercice 2 Exercice 3
Image non disponible Image non disponible Image non disponible

d. La propriété anchor.

Nous l'avons vu précédemment, un composant est positionné sur une cellule de la grille grâce à sa position x et y et qu'il peut s'étendre sur plusieurs cellules aussi bien horizontalement que verticalement. La propriété anchor permet de spécifier un point d'ancrage à un composant à l'intérieur de sa (ou ses) cellule(s). Par défaut, le composant sera centré horizontalement et verticalement dans les cellules qui lui sont allouées. En effet, si la taille de la case de la grille est supérieure aux désidératas du composant, nous pourrons choisir où ancrer ce composant au sein de cet espace. Prenons un exemple pour illustrer la chose.

Image non disponible
La propriété anchor.

Nous pouvons voir dans cet exemple une grille de 2 lignes et de 3 colonnes. La hauteur de la première ligne est trop grande (verticalement) par rapport aux désidératas des boutons 1 et 3. Grâce à la propriété anchor, nous allons pouvoir préciser comment positionner ces boutons dans l'espace qui leur est réservé. Pour le bouton 1, nous avons choisi de le placer au nord (GridBagConstraints.PAGE_START) et pour le bouton 3 au sud (GridBagConstraints.PAGE_END). Quant au bouton 4, lui a un espace horizontal beaucoup plus large que ce que lui demande. Nous avons choisi dans cet exemple de l'aligner au centre de sa case (GridBagConstraints.CENTER).

Nous pouvons, pour schématiser, imaginer une cellule (ou un groupe de cellules) subdivisée en une série de neuf zones, chacune pouvant servir de point d'ancrage:

Image non disponible

Les constantes que nous vous présentons ont été introduites avec Merlin (Java 1.4). Antérieurement, il était possible de spécifier des constantes comme NORTH, EAST, SOUTHEAST... Ces constantes, bien qu'étant toujours acceptées, sont fortement déconseillées car elles ne permettent pas d'être localisées.

Nous n'allons utiliser que les nouvelles constantes dans nos exemples. Reprenons un exemple plus parlant pour découvrir la propriété anchor en images.

GridBagConstraints.LINE_START GridBagConstraints.CENTER
Image non disponible Image non disponible
GridBagConstraints.LINE_END
Image non disponible

Dans cet exemple, notre grille ne contient qu'une seule colonne. Le bouton ayant besoin de peu de place par rapport à la liste de noms, nous pouvons choisir où ancrer ce bouton dans sa colonne.

Depuis Java SE 6, de nouvelles constantes pour les points d'ancrages ont été introduites pour supporter la ligne de base. Commençons par comprendre leur but pour ensuite revenir sur leur utilisation.

La ligne de base (ou baseline) est une ligne imaginaire sur laquelle reposent les lettres lorsque nous écrivons. Lorsque nous étions petits, et que nous apprenions à écrire, ces lignes étaient bel et bien réelles.

Image non disponible
Le GridBagLayout supporte la ligne de base.

Lors du développement d'une interface utilisateur, il est bien plus élégant (et reposant pour les yeux à la lecture) d'aligner les composants sur cette ligne de base imaginaire Reprenons un de nos exemples précédents pour comprendre cela en images.

Image non disponible
Alignement sur la ligne de base.

Nous avons dessiné en bleu les lignes de base. Sur chacune des lignes de notre grille, une ligne de base peut exister. En effet, tous les composants ne supportent pas la ligne de base (JEditorPane, JTextPane...), ou n'en n'ont pas d'utilité (JSeparator...). Aucune ligne de base ne sera donc définie sur une ligne de la grille ne contenant aucun composant ayant une ligne de base.

Comme nous l'avons fait pour les constantes de bases, nous pouvons diviser une cellule ou un groupe de cellules en neuf zones comme ceci (orientation du container sur LEFT_TO_RIGHT):

Image non disponible

Chacune de ces zones permet d'ancrer un composant dans sa (ou ses) cellules. Elles s'utilisent exactement comme les constances précédemment présentées mais l'alignement sera, dans ce cas-ci plus adéquat.

e. La propriété fill.

Cette propriété permet de définir la manière dont un composant sera (ou ne sera pas) redimensionné lorsque l'espace qui lui est alloué est supérieur à celui de ses désidératas. Un composant pourra être redimensionné pour occuper l'entièreté de l'espace qui lui est alloué horizontalement, verticalement ou dans les deux sens. Par défaut, aucun redimensionnement n'est appliqué. Prenons un exemple.

Image non disponible

Dans cet exemple, nous avons 3 boutons. Le Bouton 3 est seul sur sa ligne. Il a donc l'ensemble de sa l'espace de sa ligne pour se placer. Nous avons décidé de l'ancrer à la position LINE_START. Aligné au centre, cela aurait donné :

Image non disponible

Maintenant, si nous précisons sur la contrainte du bouton 3 de s'étendre horizontalement (propriété fill = GridBagConstraints.HORIZONTAL), le résultat, et peu importe où le bouton est ancré serait :

Image non disponible

Cette propriété est plutôt simple à comprendre. Par contre, elle est un peu plus subtile lorsqu'elle est combinée avec des poids (weightx et weighty). Nous allons voir ca dans le paragraphe suivant.

f. Les propriétés weightx et weighty.

Ces deux propriétés permettent de définir comment l'espace supplémentaire sera distribué parmi les composants horizontalement (weightx) et verticalement (weighty). Par défaut, aucun poids n'est défini. Ces deux propriétés sont très subtiles et, mal utilisées, peuvent provoquer des désastres au niveau de l'interface que nous développons. Les valeurs les plus communes sont les valeurs 0 et 1. Rare sont les cas où nous devrons préciser d'autres valeurs.

Prenons un exemple assez courant. Imaginons que nous avons une fenêtre avec trois boutons comme ceci :

Image non disponible

Nous avons définie une taille préférée identique sur chacun des boutons et l'espace du container est encore trop large. La question est : comment distribuer l'espace supplémentaire (en vert sur l'image) ?.

Nous avons deux possibilités :

1- Soit allouer tout l'espace à un des composants, ici nous distribuons l'espace supplémentaire au bouton aide.

Image non disponible

Pour obtenir un résultat pareil, il nous suffit de préciser un weightx de 1 sur le bouton Aide et de l'ancrer sur LINE_END. En fait, lorsque nous définissons un poids weightx sur une contrainte, c'est toute la colonne qui sera redimensionnée et nous aurons à choisir un point d'ancrage adéquat. Nous aurions très bien pu ancrer le bouton Aide au centre. Nous aurions eu le résultat suivant :

Image non disponible

L'espace supplémentaire est complètement distribué pour la première colonne mais nous avons décidé d'ancrer le composant au centre de celle-ci ce qui donne le résultat précédent. Si nous décidons de centrer le composant sur LINE_START, nous verrons un résultat très courant.

Image non disponible

Lorsque nous agrandissons le container, l'espace supplémentaire s'ajoutera entre les boutons Aide et OK.

2- Soit de partager l'espace supplémentaire parmi les composants. Nous allons par exemple choisir de donner 50% de l'espace supplémentaire au bouton Aide; les boutons OK et Cancel recevrons chacun 25% de l'espace supplémentaire (ce qui donne 100%).

Image non disponible

Associé à une politique de redimensionnement le résultat peut donner :

Image non disponible

Si nous prenons notre règle, nous pourrons nous assurer que le bouton Aide à une largeur deux fois plus grande que le bouton Ok ou Annuler.

Remarquons que toute valeur double est autorisée pour les poids. En fait, la somme des poids d'une rangée (ou d'une colonne) vaut 100%. C'est-à-dire que si nous précisons une seule contrainte de poids sur une rangée (weightx) de 0.5 ou de 50, celle-ci sera équivalente à 1. Si nous précisons 3 poids, disons de 10, 10, 20, ceux-ci seront équivalent à 0.25, 0.25 et 0.5.

Dans la plupart des cas, il y a souvent un élément central dans l'interface qui s'occupera de récupérer l'espace supplémentaire aussi bien horizontalement que verticalement et dans lequel il s'étendra. Nous le verrons dans notre exemple complet réalisé pas à pas.

g. Les propriétés ipadx et ipady.

Ces attributs permettent de définir des marges internes au composant. Nous l'avons vu précédemment, le GridBagLayout demande les tailles minimales ou préférées sur les composants ajoutés au container. Lorsque nous précisons une valeur ipadx différente de nulles, la largeur minimale (ou préférée) du composant à laquelle cette contrainte est augmentée de chaque coté du composant. L'utilité de ces marges est assez vague et nous ne les utiliserons pas dans nos exemples.

Voyons tout de même en image le comportement de ces propriétés.

Image non disponible

Ces deux zones de saisie ont exactement la même taille préférée, mais à la seconde nous avons ajouté un ipadx de 20. Nous pouvons directement voir que leurs tailles ne sont pas égales. La seconde zone de saisie est légèrement plus large que la première car nous lui avons spécifié un ipadx de 20 pixels à la contrainte lui étant associée. Si nous mesurons nous pouvons remarquer que les 20 pixels sont distribué de part et d'autre du composant de manière égale (10 pixels de chaque coté).


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2010 bbclone. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.