|
Traduction non autorisée de |
Cette note explore en détail comment travaillent les gestionnaires de disposition1 de la bibliothèque JFC/Swing et comment on peut les imbriquer efficacement pour créer des interfaces-utilisateur graphiques (IUG) esthétiques et utiles.
Pour expliquer pourquoi les gestionnaires de disposition sont nécessaires il suffit dexaminer quelques uns des problèmes quils résolvent. Regardez les copies décran suivantes. Elles décrivent plusieurs égarements trop fréquents des IUG :
Un premier défaut de certaines interfaces graphiques est dignorer les changements de taille provoqués par lutilisateur. 640 × 480 pixels est une dimension décran très commune et cest une bonne idée de sassurer que votre application peut se caser dans une telle taille. Cependant, beaucoup dutilisateurs ont des écrans bien plus grands et souhaitent en profiter. Des IUG dont on ne peut pas changer la taille seront alors extrêmement frustrantes. Par exemple, dans laffichage initial de lécran suivant, le nom saisi est un peu long, si bien quil nentre pas entièrement dans le champ de texte et nest pas visible dun seul coup :
Dans lespoir de voir le nom tout entier vous allez agrandir horizontalement la boîte de dialogue. Malheureusement, le programmeur qui a écrit cette application a utilisé une taille et un positionnement absolus, si bien que les composants de la boîte de dialogue ne sagrandissent pas :
Après avoir reçu des plaintes, le développeur réalisera son erreur et modifiera la IUG afin quelle change de taille proprement :
Avant lagrandissement
Après lagrandissement
Une autre faute courante consiste à penser que toutes les plate-formes ou bibliothèques de look-and-feel (si vous utilisez JFC/Swing) ont les mêmes caractéristiques relativement aux tailles des éléments. Limage suivante montre linterface utilisateur précédente sous le look-and-feel « Motif » de JFC/Swing, quand on nutilise pas un gestionnaire de disposition :
Vous constatez que le look-and-feel Motif applique une bordure vide autour des boutons, ce qui a des effets indésirables lors du dessin. Changer la taille des polices de caractères a des effets similaires.
Certains mots prennent la même place dans différentes langues. « Non » est un bon exemple, car il sépelle à peu près de la même manière dans plusieurs langues. Cependant, de nombreux mots ont une longueur variable selon la langue utilisée. Par exemple :
Un bouton simple pour finir une application. Le programmeur na pas pensé à la traduction dans dautres langues... |
|
...telles que lallemand : « Auf Wiedersehen » apparaît coupé dans le mot beaucoup plus court « Wieder ». |
Lutilisateur ne voit pas la phrase entière. Ce nest pas quinesthétique, cest dangereux (« wieder » signifie encore !).
Un gestionnaire de disposition est un objet qui encapsule un algorithme pour calculer la position et la taille des composants dune IUG. Au lieu décrire cet algorithme à lintérieur de votre code et de guetter les changements de taille et autres événements modifiant la disposition, lalgorithme est gardé séparément. Cela permet quil soit réutilisé dans plusieurs applications, tout en simplifiant votre propre code.
Le comportement dun gestionnaire de disposition est défini par linterface
java.awt.LayoutManager
,
qui spécifie comment communiquent un conteneur et un gestionnaire de
disposition. Elle introduit des méthodes pour :
Une interface additionnelle, java.awt.LayoutManager2
,
a été ajoutée à partir de la version 1.1 du JDK.
Elle ajoute un petit nombre de méthodes de positionnement et de validation.
Tout composant graphique (classe java.awt.Component
)
possède plusieurs méthodes de consultation pour aider le processus
de disposition. Chacune de ces méthodes renvoie un objet java.awt.Dimension
décrivant la taille demandée. Ces méthodes sont les suivantes :
public Dimension getPreferredSize()
: renvoie la
taille souhaitée pour un composant, public Dimension getMinimumSize()
: renvoie la
plus petite taille souhaitée pour un composant, public Dimension getMaximumSize()
: renvoie la
plus grande taille souhaitée pour un composant.Les gestionnaires de disposition utilisent ces méthodes pour calculer la place et la taille des composants. Selon leur nature, ils peuvent satisfaire ou ignorer tout ou partie des souhaits que ces méthodes expriment. Chaque gestionnaire de disposition a son propre algorithme et peut utiliser ou non cette information pour décider du placement dun composant. Bien entendu, lorsquon écrit son propre gestionnaire de disposition on doit indiquer soigneusement lesquelles de ces méthodes sont respectées ou ignorées.
Lorsque vous définissez vos propres composants vous avez deux manières dindiquer ce que les méthodes précédentes doivent renvoyer :
Sil sagit dun composant Swing, alors vous héritez de trois
méthodes setPreferredSize(Dimension)
, setMinimumSize(Dimension)
et setMaximumSize(Dimension)
. Vous pouvez appeler ces méthodes
directement pour expliciter linformation correspondante. Par exemple :
JButton boutonOk = new JButton("Ok"); boutonOk.setPreferredSize(new Dimension(100,10)); |
Najustez les tailles que si vous êtes sûrs de ce que vous faites (probablement, lexemple ci-dessus nest pas une bonne idée...).
Sil ne sagit pas dun composant Swing (par exemple, cest seulement un composant AWT) alors vous devez le sous-classer4 en redéfinissant les méthodes qui ajustent les tailles. Par exemple :
Button boutonOk = new Button("Ok") { public Dimension getPreferredSize() { return new Dimension(100,10); } |
Les gestionnaires de disposition doivent être associés aux conteneurs
(classe java.awt.Container
)
pour faire leur travail. Si un conteneur na pas de gestionnaire de disposition
associé, alors il place les composants comme indiqué par le programmeur
à l'aide des méthodes setLocation()
et setSize()
ou setBounds()
.
Si un conteneur est associé à un gestionnaire de disposition
alors le premier demande au second la position et la taille des composants avant
que ces derniers soient dessinés. Le gestionnaire de disposition lui-même
ne se charge pas du dessin ; il décide simplement quelle taille
et position chaque composant doit occuper et appelle setLocation()
et setSize()
ou setBounds()
sur chacun de ces composants.
On associe un gestionnaire de disposition et un conteneur en appelant la méthode
setLayout(LayoutManager)
du conteneur. Par exemple :
Panel p = new Panel(); p.setLayout(new BorderLayout()); |
Notez que certains conteneurs, comme java.awt.Panel
et javax.swing.JPanel
,
possèdent un constructeur bien pratique qui prend un gestionnaire de
disposition comme argument :
Panel p = new Panel(new BorderLayout()); |
Les conteneurs ont plusieurs méthodes qui peuvent être utilisées pour leur ajouter des composants. Ce sont :
public Component add(Component comp) public Component add(String name, Component comp) public Component add(Component comp, int index) public void add(Component comp, Object constraints) public void add( Component comp, Object constraints, int index) |
Chacune de ces méthodes ajoute un composant au conteneur et passe des
informations au gestionnaire de disposition correspondant. Toutes ces méthodes
prennent un paramètre Component
, qui spécifie quel
composant doit être ajouté. Certaines prennent un indice. Il est
utilisé pour indiquer un ordre dans le conteneur ; certains gestionnaires
de disposition (comme les CardLayout
) respectent lordre des
composants ajoutés.
Les autres paramètres, name et constraints sont des
informations qui peuvent être utilisées par un gestionnaire de
disposition particulier pour aider à diriger le placement. Par exemple,
lorsquon ajoute un composant à un conteneur qui est géré
par un BorderLayout
, vous devez spécifier un point cardinal
comme contrainte.
Chacune des méthodes add()
précédentes délègue
son travail à une même méthode addImpl()
:
protected void addImpl(Component comp, Object constraints, int index) |
(addImpl signifie « implémentation de la méthode
add ») Cette méthode est celle qui fait tout le travail.
Elle ajoute le Component
au Container
et, si un gestionnaire
de disposition est en train de gérer le conteneur, elle appelle la méthode
addLayoutComponent()
de ce gestionnaire de disposition. Cest
à travers addLayoutComponent()
que le gestionnaire de disposition
reçoit les contraintes (depuis lappel de add()
).
Si vous créez une sous-classe de Container
et vous voulez
surcharger une méthode add()
il vous suffit de surcharger
addImpl()
. Toutes les autres méthodes add()
passent par cette dernière.
En plus du gestionnaire de disposition, chaque conteneur a une méthode
getInsets()
qui renvoie un objet java.awt.Insets
.
Un tel objet a quatre champs publics (de type int
) : top
,
bottom
, left
and right
.
Lencadrement définit laire quun conteneur se réserve pour son propre usage (par exemple pour dessiner une bordure décorative). Les gestionnaires de disposition doivent respecter cette aire lorsquils positionnent et donnent une taille aux composants contenus.
A titre de démonstration, créez une simple sous-classe de Panel
munie dune bordure 3D surélevée entourant tout le panneau.
Vous devez définir une bordure distante de 5 pixels du bord du conteneur,
et réservant un espace comparable entre elle et les composants contenus.
La classe ressemblera à quelque chose comme ceci :
public class BorderPanel extends Panel { private static final Insets insets = new Insets(10, 10, 10, 10); public Insets getInsets() { return insets; } public void paint(Graphics g) { Dimension size = getSize(); g.setColor(getBackground()); g.draw3DRect( 5, 5, size.width - 11, size.height - 11, true); } } |
Pour créer le panneau, vous avez défini un objet Insets
statique qui représente lespace à réserver. Puisque
cet espace ne doit pas changer, vous avez utilisé une simple instance
finale. Vous devez renvoyer cette instance chaque fois quun gestionnaire
de disposition (ou nimporte qui dautre) appelle getInsets()
.
Ensuite vous définissez une méthode paint()
qui
prend la taille du conteneur dans lequel elle dessine et alors trace une bordure
surélevée à lintérieur de cet espace. Si vous
utilisez la classe précédente comme ceci :
Frame f = new Frame("Test"); f.setLayout(new GridLayout(1, 0)); f.setBackground(Color.lightGray); BorderPanel p = new BorderPanel(); p.setLayout(new GridLayout(1,0)); p.add(new Button("Hello")); f.add(p); f.setVisible(true); f.pack(); |
vous obtiendrez lIUG suivante :
Si vous nêtes pas familier avec les GridLayout
, pas
de problème, nous en discuterons plus tard.
Vous pouvez obtenir un autre effet intéressant en ajoutant ce qui suit
à la méthode paint()
:
g.draw3DRect(6, 6, size.width-13, size.height-13, false); |
après le premier draw3DRect()
:
ou, si vous échangez true
et false
dans les
deux appels de drawRect()
:
Si au lieu des composants dAWT vous utilisez plutôt ceux de Swing
alors vous préférerez explorer les diverses classes Border
fournies pour produire ce genre deffets, au lieu dutiliser des Insets
.
La bibliothèque AWT inclut cinq gestionnaires de disposition standard ; en les combinant, vous pourrez créer toutes les IUG que vos pouvez imaginer. Ce sont :
FlowLayout
BorderLayout
GridLayout
CardLayout
GridBagLayout
Chacun va être expliqué en détail ci-dessous, ainsi que les stratégies et pièges de son utilisation. Par eux-mêmes, ces gestionnaires de disposition ne semblent pas très intéressants. Cependant, lorsquils sont combinés, ils deviennent incroyablement flexibles.
Cest le plus simple des gestionnaires de disposition de AWT. Sa stratégie de placement est :
Pour ajouter des composants à un
conteneur géré par un FlowLayout
vous pouvez utiliser
une des méthodes add()
suivantes :
public Component add(Component comp) public Component add(Component comp, int index) |
Vous ne devez spécifier aucune contrainte pour les composants car FlowLayout
dépend uniquement de lordre des appels de add()
(ou
de lindice indiqué dans un appel de add(Component comp, int
index)
.)
Examinons un simple FlowLayout
en action. Supposons que nous ayons
la définition de classe suivante :
public class FlowTest { public static void main(String[] args) { Frame f = new Frame("FlowTest"); f.setLayout(new FlowLayout()); f.add(new Button("A")); f.add(new Button("B")); f.add(new Button("C")); f.add(new Button("D")); f.add(new Button("E")); f.setVisible(true); } } |
Cette classe crée un cadre (objet Frame
) et lui ajoute
cinq boutons. Elle dispose les boutons autant quelle peut par ligne, puis elle
se déplace à la ligne suivante pour en afficher davantage. Les
images suivantes montrent le cadre lorsquil est agrandi horizontalement :
Un peu dagrandissement horizontal
Davantage dagrandissement horizontal
Notez que le gestionnaire de disposition met autant de composants que possible. Sil ny a pas de place pour tous, certains ne sont simplement pas montrés
:
Les objets FlowLayout
peuvent être personnalisés
au moment de leur construction en passant des indications dalignement au constructeur :
FlowLayout.CENTER
(valeur par défaut)FlowLayout.RIGHT
FlowLayout.LEFT
Ces réglages déterminent comment sont positionnés les
composants dune ligne donnée. Par défaut, tous les composants
dans une ligne sont séparés par un écart horizontal, ensuite
tout le bazar est centré dans lespace disponible. LEFT
et RIGHT
introduisent un bourrage tel que la ligne est alignée
à gauche ou à droite. Lalignement peut également être
changé, après la construction, en appelant la méthode setAlignment()
de FLowLayout
avec une des indications dalignement ci-dessus.
FlowLayout
peut aussi être personnalisé avec différents
écartements horizontaux et verticaux. Cela spécifie combien despace
est laissé entre les composants, horizontalement (hgap
)
et verticalement (vgap
).
Il faut savoir que lorsquon demande à un Container
quelle
est sa taille préférée, il obtient sa réponse auprès
de son gestionnaire de disposition. Cela amène la question de savoir
ce quun FlowLayout
fait de préférence avec ses composants.
Malheureusement, il ny a pas de moyen pour un conteneur de connaître quoi que ce soit à propos de la taille de son parent (c.-à-d. le conteneur qui le contient) ou de la manière dont le gestionnaire de disposition de son parent prévoit de le disposer, lui. Ainsi, la seule information disponible pour déterminer la taille préférée dun conteneur est lensemble des composants quil contient.
La préférence dun FlowLayout
est de disposer tous
ses composants sur une seule ligne. Cela implique que sa hauteur préférée
sera la hauteur du plus haut de ses composants, plus un certain débordement
appelé son vgap
(vgap
et hgap
sont des propriétés communes de la plupart des gestionnaires de
disposition, et spécifient les écartements entre les composants).
La largeur préférée sera la somme de toutes les largeurs
des composants contenus, plus des hgap
entre les composants ainsi
quaux extrémités de la ligne.
Réfléchissez aux conséquences de cela. Quarrive-t-il
si un conteneur géré par un FlowLayout
est imbriqué
dans un autre conteneur également géré par un FlowLayout
?
Par exemple :
Panel p1 = new Panel(new FlowLayout()); Panel p2 = new Panel(new FlowLayout()); p2.add(new Button("A")); p2.add(new Button("B")); p2.add(new Button("C")); p2.add(new Button("D")); p2.add(new Button("E")); p1.add(p2); |
Le résultat est déconcertant. La démarche du processus de placement est la suivante :
p1
demande à ce dernier de sagencer,p1
constate quil est associé à un gestionnaire
de disposition et lui délègue la tache dagencement,FlowLayout
de p1
demande la taille préférée
de tous ses composants
p1
est p2
FlowLayout
de p2
déclare quil
préfère disposer tous ses composants sur une seule ligne,FlowLayout
de p2
demande à ses
composants (les boutons) leur taille préférée
et calcule la taille de cette ligne,FlowLayout
de p2
renvoie cette taille
préférée,p2
renvoie la taille préféréeFlowLayout
de p1
essaie de respecter autant
que possible la taille préférée de p2
p2
à sa taille préféréep2
2 à la hauteur de la ligne de boutons et à
la largeur disponiblep2
les valeurs disponibles.Quest-ce que nous voulons dire ? Si un conteneur géré par
un FlowLAyout
est placé à lintérieur dun
autre conteneur dont le gestionnaire de disposition respecte sa largeur préférée,
le conteneur géré par un FlowLayout
imbriqué
aura toujours une simple ligne.
Quand on utilise un FlowLayout
il faut garder présents
à lesprit les points suivants :
FlowLayout
à lintérieur dun conteneur dont le gestionnaire de disposition
respecte la largeur préférée
FlowLayout
cache les composants qui ne rentrent
pas dans le conteneur
FlowLayout
nest vraiment utile que si vous avez un petit nombre de composant,FlowLayout
est orienté horizontalement.
Si vous souhaitez un arrangement vertical vous devez écrire votre propre
gestionnaire de disposition (ou bien utiliser le gestionnaire BoxLayout
qui est livré avec JFC/Swing. Le gestionnaire BoxLayout
est expliqué dans le tutoriel Fundamentals
of Swing: Part I)BorderLayout
est probablement le plus utilisé des gestionnaires
de disposition standard. Il définit un schéma de placement qui
fait correspondre son conteneur avec cinq sections logiques :
La première chose qui vous vient à lesprit est certainement
« mais je nai jamais eu une IUG ressemblant à
ceci ! » De plus, vous avez probablement raison. Mais le secret
de BorderLayout
se trouve dans lemploi de ses possibilités
dimbrication, et dans lutilisation de deux ou trois des sections
logiques (il est vraiment rare quon utilise plus de trois sections à
la fois).
Pour commencer, regardez lextrait de code correspondant à la IUG ci-dessus :
Frame f = new Frame("orderTest"); f.setLayout(new BorderLayout()); f.add(new Button("North"), BorderLayout.NORTH); f.add(new Button("South"), BorderLayout.SOUTH); f.add(new Button("East"), BorderLayout.EAST); f.add(new Button("West"), BorderLayout.WEST); f.add(new Button("Center"), BorderLayout.CENTER); |
Le gestionnaire BorderLayout
requiert une contrainte lors de lajout
dun composant. La contrainte doit être une des suivantes :
BorderLayout.NORTH
BorderLayout.SOUTH
BorderLayout.EAST
BorderLayout.WEST
BorderLayout.CENTER
Ces contraintes sont spécifiées dans les deux méthodes
add()
suivantes :
public void add(String constraint, Component component)
Component
à un Container
.
public void add(Component component, Object constraint)
Vous verrez dautres variantes de la méthode add()
lorsque
vous examinez le gestionnaire CardLayout
, plus
tard. Pour le moment, tenez-vous en aux formes ci-dessus.
Pour BorderLayout
, largument contrainte décrit
la position que le composant doit occuper. Notez que lexemple donné
plus haut aurait pu être écrit comme ceci :
Frame f = new Frame("BorderTest"); f.setLayout(new BorderLayout()); f.add("North", new Button("North")); f.add("South", new Button("South")); f.add("East", new Button("East")); f.add("West", new Button("West")); f.add("Center", new Button("Center")); |
La principale différence est que la nouvelle forme (utilisant les contraintes
du type BorderLayout.NORTH
) peut être vérifiée
au moment de la compilation : si vous écrivez BorderLayout.NORFH
le compilateur peut le détecter. Si vous écrivez "Norfh"
,
cela ne sera pas détecté à la compilation, mais provoquera
lenvoi dune exception IllegalArgumentException
durant lexécution.
La plate-forme Java 2 (préalablement connue comme JDK 1.2) ajoute les
constantes additionnelles BEFORE_FIRST_LINE
, AFTER_LAST_LINE
,
BEFORE_LINE_BEGINS
, et AFTER_LINE_ENDS
. En réalité
elles sont équivalentes à NORTH
, SOUTH
,
WEST
, et EAST
, respectivement. Cependant, elles peuvent
avoir une autre orientation lorsque le texte nest pas orienté de gauche
à droite et du haut vers le bas. Examinez la classe java.awt.ComponentOrientation
pour obtenir de linformation supplémentaire sur ces questions dépendantes
de la langue.
BorderLayout
respecte certaines des tailles préférées
des composants contenus, mais non toutes. Sa stratégie de placement
est :
NORTH
),
obtenir sa taille préférée.SOUTH
), obtenir
sa taille préférée.EAST
),
obtenir sa taille préférée.WEST
),
obtenir sa taille préférée.CENTER
) lui
donner tout lespace qui reste, sil en reste.Considérez maintenant limbrication dun conteneur géré
par un FlowLayout
à lintérieur dun conteneur géré
par un BorderLayout
. Dabord, que va-t-il se passer si vous ajoutez
le conteneur géré par un FlowLayout
comme composant
NORTH
ou SOUTH
du BorderLayout
?
Panel flow = new Panel(new FlowLayout()); Panel border = new Panel(new BorderLayout()); flow.add(new Button("A")); flow.add(new Button("B")); flow.add(new Button("C")); flow.add(new Button("D")); flow.add(new Button("E")); border.add(flow, BorderLayout.NORTH); |
Vous rappelez-vous ce qui arrive lorsquun conteneur géré par
un FlowLayout
est ajouté à un gestionnaire qui respecte
sa taille préférée ? Le conteneur géré
par le FlowLayout a une ligne unique ! Il narrangera jamais ses composants
sur plus dune ligne.
Cétait facile (maintenant que vous connaissez le secret). Maintenant,
que se passe-t-il si vous ajoutez le conteneur géré par un FlowLayout
comme composant WEST
ou EAST
? Pour la démonstration,
placez le conteneur dans la section EAST
:
Cet affichage initial est aussi beau que vous lattendiez : chaque composant a pris sa taille préférée.
Maintenant, réduisez la largeur et augmentez la hauteur. Le conteneur
FlowLayout
insiste toujours pour maintenir la largeur préférée
de la ligne et mange la place que le composant central devrait utiliser. Ce
résultat est très inattendu, car on aurait pensé que le
conteneur FlowLayout
aurait rempli la zone Est et aurait laissé
un peu de place au composant du centre.
A retenir : un BorderLayout
demande la taille préférée
de chaque section et la respecte autant que possible.
Pour montrer cela encore plus, réduisez la largeur jusquà ce que seul le composant à lEst soit visible :
Enfin, si vous compressez encore, le conteneur géré par le FlowLayout
est découpé à la taille de lespace disponible :
Ainsi, la seule place dans laquelle vous devez mettre un conteneur géré
par un FlowLayot
dans un conteneur géré par un BorderLayout
est la section CENTER
(à moins que vous ayez seulement un
petit nombre de composants dans le FlowLayout
).
Considérons maintenant quelques jolies généralisations
à propos de BorderLayout
:
NORTH
et SOUTH
dans un BorderLayout
peuvent être utiles si vous voulez lier la hauteur dune partie dune
IUG à la hauteur préférée de cette partie,EAST
et WEST
dans un BorderLayout
peuvent être utiles si vous voulez lier la largeur dune partie dune
IUG à la largeur préférée de cette partie,CENTER
est la partie qui développe autant que possible.Ce sont des propriétés importantes dun BorderLayout
,
et rendent ce dernier très puissant lorsquil est utilisé
pour créer des IUG imbriquées plus complexes.
Prenons un exemple vraiment simple. Supposez que vous voulez un simple champ de texte étiqueté. Vous voulez écrire ce nouveau composant, exhibant les propriétés suivantes :
Vous pouvez démarrer avec le code suivant :
Panel p = new Panel(new BorderLayout()); Label nameLabel = new Label("Name:"); TextField entry = new TextField(); p.add(nameLabel, BorderLayout.WEST); p.add(entry, BorderLayout.CENTER); |
Ici vous liez la largeur de létiquette sa largeur préférée. Elle prendre lespace nécessaire et ne sagrandira pas. Le champ de saisie nest pas borné et peut sagrandir. Ainsi, vous obtenez le résultat souhaité :
Taille initiale
Après agrandissement horizontal
En fait, il semble que vous obtenez le résultat souhaité. Regardez ce qui arrive quand vous agrandissez verticalement :
Le TextField
est agrandi. Ainsi, vous avez besoin de lier la hauteur
des deux composants à leur hauteur préférée. Cela
peut être obtenu en plaçant la paire Label
/TextField
à lintérieur dun autre conteneur géré
par un BorderLayout
, à titre de composants NORTH
ou SOUTH
. Si vous voulez que les champs soient en haut de la IUG,
vous devez les placer au Nord :
Panel p = new Panel(new BorderLayout()); Label nameLabel = new Label("Name:"); TextField entry = new TextField(); p.add(nameLabel, BorderLayout.WEST); p.add(entry, BorderLayout.CENTER); Panel p2 = new Panel(new BorderLayout()); p2.add(p, BorderLayout.NORTH); |
Maintenant, quand vous agrandissez verticalement, vous obtenez leffet souhaité :
Les BorderLayout
peuvent être personnalisés avec
des valeurs hgap
et vgap
au moment de leur construction.
Ces valeurs spécifient combien despace est laissé entre composants,
horizontalement (hgap) et verticalement (vgap).
Si on demande à un conteneur contrôlé par un BorderLayout
quelle est sa taille préférée, que renvoie-t-il ?
Lidée sous-jacente derrière sa taille préférée
est dassurer que tous les composants contenus reçoivent leurs tailles
préférées. Dabord, regardons la largeur préférée :
Dans limage ci-dessus apparaissent trois rangées de composants :
NORTH
,WEST
, CENTER
et EAST
(plus autant
de hgaps que nécessaire),SOUTH
La largeur préférée du gestionnaire doit prendre en compte
les largeurs de ces lignes. Si on écrit LP comme abréviation
pour « largeur préférée », alors
on peut écrire une équation simple donnant la largeur préférée
dun BorderLayout
:
pw = max(north.LP, south.LP, (west.LP + center.LP + east.LP + hgaps)) |
La quantité de hgaps dépend du nombre de composants présents dans la ligne centrale.
La hauteur perforée (HP dans léquation suivante) dépend
des tailles des composants NORTH
et SOUTH
, plus la
plus grande taille dun composant de la ligne centrale :
ph = vgaps + north.HP + south.HP + max(west.HP, center.HP, east.HP) |
La quantité de vgaps dépend du nombre de lignes présentes
dans le BorderLayout
.
Une application utile de cette manière de déterminer la taille
préférée est la création de deux ou trois lignes
de composants dont chacune garde sa taille préférée. Supposez
que vous avez une étiquette (Label) une zone de texte (TextArea
)
et un champ de texte (TextField
) dont vous voulez quils apparaissent
comme ceci :
Et quand vous agrandissez verticalement, vous ne voulez pas que les composants grandissent verticalement :
Dabord, rappelez-vous que vous pouvez lier la hauteur dun composant
à sa hauteur préférée en le plaçant dans
la parte NORTH
ou SOUTH
dun BorderLayout
.
Sil arrive que ce composant lié soit lui-même un autre BorderLayout
(avec des composants NORTH
, CENTER
et SOUTH
)
chaque composant à lintérieur de ce gestionnaire obtient sa hauteur
préférée. La résultat est celui de la figure ci-dessus,
et voici le code qui le produit :
Frame f = new Frame("BorderTest"); Panel p = new Panel(new BorderLayout()); f.setLayout(new BorderLayout()); p.add(new Label("Hello", Label.CENTER), BorderLayout.NORTH); p.add(new TextArea(), BorderLayout.CENTER); p.add(new TextField(), BorderLayout.SOUTH); f.add(p, BorderLayout.NORTH); |
Une combinaison efficace dun FlowLayout
et dun BorderLayout
sert à placer les boutons « Ok » et « Cancel »
des dialogues. Par exemple :
On obtient cela avec le code suivant :
Frame f = new Frame("BorderTest"); Panel p = new Panel(new FlowLayout(FlowLayout.RIGHT)); f.setLayout(new BorderLayout()); p.add(new Button("Ok")); p.add(new Button("Cancel")); f.add(p, BorderLayout.SOUTH); |
Il y a un problème dans cette approche : les tailles des boutons sont différents. Nous verrons une meilleure manière de créer ce type de IUG dans un moment.
GridLayout
dispose ses composants sur une grille. Chaque composant
reçoit la même taille et est positionné de la gauche vers
la droite, du haut vers le bas :
Le code qui produit la IUG précédente ressemble à ceci :
Frame f = new Frame("Grid Test"); f.setLayout(new GridLayout(3,4)); for (int x = 1; x < 13; x++) f.add(new Button(""+x)); |
Dans la spécification dun GridLayout
il y a deux paramètres
principaux : nrows (nombre de lignes) et ncols (nombre de
colonnes). Vous pouvez spécifier les deux paramètres, mais un
seul sera utilisé. Regardez le fragment de code suivant, extrait
de GridLayout.java
:
if (nrows > 0) ncols = (ncomponents + nrows - 1) / nrows; else nrows = (ncomponents + ncols - 1) / ncols; |
Notez que si le nombre de lignes nest pas nul alors le gestionnaire calcule le nombre de colonnes ; si le nombre de lignes est nul, il le calcule daprès le nombre de colonnes.
Pour un observateur peu attentif, linstruction
f.setLayout(new GridLayout(3,4)); |
semble devoir toujours diviser le composant en douze (3 × 4) sections, mais ce nest pas le cas. Dans linstruction ci-dessus, vous pouvez mettre ce que vous voulez comme nombre de colonnes, leffet obtenu sera exactement le même.
Une meilleure manière de spécifier les lignes et les colonnes
dun GridLayout
est de toujours mettre lun des deux à zéro.
Une valeur nulle pour ligne ou colonnes signifie « nimporte quel
nombre de ».
N.B. Attention, vous ne pouvez pas spécifier zéro pour les deux,
une IllegalArgumentException
serait lancée.
Donner zéro pour une valeur rendent évidentes les intentions de conception. Si vous voulez nécessairement quatre ligne, dites cela ; si vous tenez absolument à avoir trois colonnes, dites cela. Lexemple précédent devrait être écrit soit comme ceci :
f.setLayout(new GridLayout(3,0)); |
soit comme cela :
f.setLayout(new GridLayout(0,4)); |
Comment déterminer la taille préférée dun GridLayout
?
Ce gestionnaire veut saccommoder à la taille préférée
de tous les composants contenus, si possible. Pour cela il examine toutes
les tailles préférées et détermine la plus grande
largeur préférée et la plus grande hauteur préférée.
Vous devez garder à lesprit que la largeur préférée
maximale et la hauteur préférée maximale ne proviennent
pas nécessairement du même composant !
Le GridLayout
cherche à donner pour taille à chaque
composant cette largeur préférée maximum et cette hauteur
préférée maximum ( rappelez-vous : tous les composants
dans un GridLayout
doivent avoir la même taille). Cela produit
la taille préférée dun GridLayout
:
pw = (maxPrefWidth * cols) + (hgap * (cols + 1)) ph = (maxPrefHeight * rows) + (vgap * (rows + 1))
Nous devions faire en sorte que les boutons « Ok » et
« Cancel » de la boîte de dialogue vue précédemment
soient de même taille. Cela peut être obtenu en déposant
les boutons dans un GridLayout
à une ligne. Vous pouvez
alors ajouter ce conteneur GridLayout
à un FlowLayout
,
ou bien utiliser un BorderLayout
imbriqué au lieu du FlowLayout
.
Voici ces deux approches :
Frame f = new Frame("Ok/Cancel"); f.setLayout(new BorderLayout()); Panel p = new Panel(); p.setLayout(new FlowLayout(FlowLayout.RIGHT)); Panel p2 = new Panel() p2.setLayout(new GridLayout(1,0,5,5)); p2.add(new Button("Ok")); p2.add(new Button("Cancel")); p.add(p2, BorderLayout.EAST); f.add(p, BorderLayout.SOUTH); |
Frame f = new Frame("Ok/Cancel"); f.setLayout(new BorderLayout()); Panel p = new Panel(); p.setLayout(new BorderLayout()); Panel p2 = new Panel() p2.setLayout(new GridLayout(1,0,5,5)); p2.add(new Button("Ok")); p2.add(new Button("Cancel")); p.add(p2, BorderLayout.EAST); f.add(p, BorderLayout.SOUTH); |
Notez la différence daspect. Le FlowLayout
met un espacement
défini par hgap
et vgap
autour des composants,
tandis que BorderLayout
ne met de lespace quentre les composants
(si bien que les composants touchent les bord du conteneur).
Dans les deux cas, les boutons apparaissent avec la même taille. Limbrication de ces gestionnaires peut être vue plus facilement sur la figure suivante :
CardLayout
utilise une stratégie différente de celles
des autres gestionnaires de disposition. Au lieu daffecter des emplacements
dans le conteneur pour tous les composants inclus, il naffiche quun composant
à la fois. Les composants sont ajoutés à un CardLayout
en utilisant les méthodes add
suivantes :
public void add(Component component, String key); public void add(String key, Component component); public void add(String key, Component component, int index); |
Les deux premières formes de la méthode add()
ajoutent
le composant à la fin de la liste des composants du conteneur. La dernière
forme lajoute à la position indiquée. La position du composant
dans le conteneur détermine lordre dans lequel les composants sont affichés
vie les méthodes de manipulation de CardLayout
.
Une chaîne unique doit être affectée à chaque composant ajouté à un conteneur. Par exemple :
Panel p = new Panel(new CardLayout()); p.add("one", new Button ("the first component")); p.add(new Button ("the second component"), "two"); p.add("three", new Button ("the third component")); p.add("between two and three", new Button ("the fourth component"), 2); |
Lorsque des composants sont ajoutés à un conteneur contrôlé
par un CardLayout
, une clé de type String
est
associée à chaque composant. Différents composants peuvent
être affichés en utilisant les méthodes next()
,
previous()
et show()
de CardLayout
. Lordre
dans lequel les composants ont été ajoutés au conteneur
détermine leur ordre daffichage lorsquon utilise les méthodes
next
et previous
. Par exemple :
CardLayout l = (CardLayout) p.getLayout(); l.previous(p); l.next(p); l.show(p, "two"); |
Notez que les méthodes previous()
, next()
et show()
requièrent quune référence au conteneur
leur soit passée comme argument. Ces gestionnaires de disposition ne
gardent pas une référence au conteneur quils gèrent. Lorsquils
effectuent des actions comme appeler previous()
, next()
ou show()
et, comme nous le verrons plus tard, larrangement des
composants, ces gestionnaires de disposition doivent être informés
du conteneur sur lesquels ils opèrent afin davoir accès aux composants
quils agencent.
Les CardLayout
sont couramment employés dans les IUG qui
veulent organiser leur données dans plusieurs écrans plus petits,
au lieu dun unique écran plus grand. Cela sutilise typiquement en combinaison
avec plusieurs boutons pour permuter les composants à lintérieur
du CardLayout
.
[ Dans JFC/Swing, un composant JTabbedPane
remplace
avantageusement un panneau munis dun CardLayout
N. du T.]
Comment déterminer la taille préférée dun CardLayout
?
Puisque la taille préférée doit prendre en compte la taille
préférée de tous les composants inclus dans le conteneur,
la taille préférée du CardLayout
sera définie
par la plus grande des largeurs préférées des composants
et la plus grande des hauteurs préférées des composants.
GridBagLayout
est connu comme le gestionnaires de disposition
le plus difficile à comprendre. Il y a plusieurs raisons à cette
renommée :
GridBagLayout
qui apparaissent après
que des composants aient été ajoutés ou enlevés
à un GridBagLayout
déjà affichéGridBagLayout
maintient un certain état interne qui
devient parfois confus lorsque des composants sont ajoutés ou enlevés.Traiter tous les détails de lutilisation dun GridBagLayout
peut occuper un livre entier. Cela sera brièvement couvert dans le contexte
dun exemple dans une section
ultérieure.
Sans un outil de construction de IUG comme VisualAge for Java ou JBuilder
vous devriez éviter les GridBagLayout
autant que possible.
Pour la plupart des IUG vous obtiendrez les mêmes résultats en
imbriquant dautres gestionnaires de disposition plus simples.
Les sections suivantes décrivent une interface utilisateur réelle et comment faire pour la créer avec laide des gestionnaires de disposition de Java.
Un gros exemple dimbrication de gestionnaires de disposition standard est WS_FTP, un client FTP graphique pour Windows :
Cette IUG, qui semble destinée aux utilisateurs avertis, est passablement complexe. Les boutons du bas sont des items de menu et ceux à droite de chaque liste de fichiers des menus surgissants en relation avec les listes de fichiers. Cependant, comme elle est, cette IUG est un exemple intéressant dimbrication de gestionnaires de disposition basiques.
Avant décrire nimporte quelle IUG vous devez toujours faire deux choses :
Dans lexemple précédent, ignorez les lignes autour des composants.
On peut les ajouter une fois que la IUG est construite, soit en utilisant le
modèle de conception « Décorateur »
soit, si vous utilisez un conteneur JFC/Swing, en appelant setBorder()
.
Partez dun dessin décrivant ce à quoi vous voulez que la IUG ressemble. Simplifiez un peu en enlevant les bordures décoratives et supposez lexistence dun composant « Liste de fichiers » (probablement une table avec un en-tête).
Les flèches indiquent quelles parties de la IUG doivent sagrandir ou se rétrécir lorsque la IUG est modifiée en taille.
Le diagramme ci-dessus a été annoté avec le comportement des changements de taille de la IUG. Nous avons montré dessous quelles lignes représentent un comportement basé sur une taille fixe (Preferred size) ou une taille variable (Varying size). Lorsquune partie dun composant a une taille fixe (horizontale, verticale ou les deux) il ne sagrandit ni ne se contracte lorsque la fenêtre est modifiée. Le signe égal (=) indique lassemblage de composants ayant la même taille.
Quelques notes sur le comportement de la IUG :
Maintenant, lamusement commence. Vous devez examiner la IUG est essayer de visualiser les gestionnaires de disposition utilisés pour représenter ses diverses sections. Quand vous essayez de concevoir une IUG, travaillez vers lintérieur à partir des bords externes. Commencez toujours en cherchant des « frontaliers », des composants ou groupes de composants qui bordent une face et ont une largeur ou un hauteur fixe.
En regardant la IUG ci dessus vous pouvez la visualiser comme un BorderLayout
avec deux composants : un SOUTH
et un CENTER
:
La partie SOUTH
du BorderLayout
a une hauteur fixe,
basée sur la taille préférée de ses composants.
La partie CENTER
sagrandit pour remplir tout lespace restant
dans la IUG. En examinant la section SOUTH
de plus près,
on voit trois parties :
On note trois composants, empilés verticalement, chacun avec sa hauteur
préférée. Vous pouvez être tenté de les planter
dans un GridLayout
, mais cela les forcerait à prendre le
même espace verticalement. Ce nest pas ce que vous voulez.
Supposez que vous placez ces trois composants comme le NORTH
,
le CENTER
et le SOUTH
dun panneau muni dun BorderLayout
,
lui-même imbriqué comme composant SOUTH
dans le BorderLayout
principal. Pensons à ce qui se passe lorsque cette partie de la IUG est
agencée :
BorderLayout
extérieur demande sa taille préférée
à son composant SOUTH
SOUTH
étant lui-même un BorderLayout
,
demande leur taille préférée à ses propres
composantsBorderLayout
imbriqué renvoie une taille qui
est
BorderLayout
extérieur affecte à la taille
de son composant SOUTH
la largeur du cadre et la hauteur préférée
du composant SOUTH
. Le composant CENTER
obtient
tout lespace restant.SOUTH
peut maintenant agencer les composants quil
contient
SOUTH
demande leur taille préférée
à ses enfants,SOUTH
affecte à ses composants toute
la largeur quil a lui-même obtenueNORTH
sa hauteur préféréeSOUTH
sa hauteur préféréeCENTER
lespace restant.Notez le dernier alinéa. Le centre obtient tout lespace restant. Puisque
le BorderLayout
extérieur a accordé au composant
SOUTH
sa hauteur préférée et que la hauteur
préférée est égale à la somme des hauteurs
préférées des composants contenus, le composant CENTER
imbriqué (la fenêtre de message, un objet TextArea
)
obtient justement sa hauteur préférée.
Dabord, définissons la partie NORTH
de cette sous-IUG.
Elle a trois composantes, également espacées, centrées
sur la largeur du panneau. Cela ressemble à un FlowLayout
,
non ? Ben oui, cest un Panel
avec un FlowLayout
contenant trois composants CheckBox
. Notez que les deux premiers
CheckBox
sont associés à laide dun CheckBoxGroup
,
si bien quils deviennent des « boutons radio ».
Créons la partie SOUTH
de cette sous-IUG. Elle a sept composants
Button
, de même taille. Lexpression « de même
taille » doit immédiatement déclencher « GridLayout
»
dans votre esprit. Donc, la partie SOUTH
de la sous-IUG est un
Panel
avec un GridLayout
contenant sept composants
Button
. Ce GridLayout
consiste en une unique ligne,
il faut donc utilisez les paramètres (1, 0) dans lappel de son constructeur.
La partie CENTER
est un simple composant TextArea
.
Vous navez pas besoin de limbriquer dans un autre composant Panel
;
les composants peuvent être directement ajoutés aux conteneurs...
Définissez le nombre de lignes de cette zone de texte à 3 et le
nombre de colonnes à 30. Ces nombres pilotent la taille préférée
de la zone de texte ; ils sont sans effet sur cette dernière lorsque
sa taille est définie par le gestionnaire de disposition. Ici vous devez
les spécifier car on demandera à la zone sa taille préférée
et cette dernière donnera par défaut une réponse supérieure
à 3 lignes. Le 30 nest utile que si on appelle la méthode pack()
du cadre, car il contribuera alors au calcul de sa largeur. Une dernière
indication à propos de TextArea
: si vous la placez
dans un BorderPanel
comme discuté dans la section Insets
ci-dessus, vous obtiendrez un bord en relief, en creux ou en sillon.
Récapitulons :
Ftp extends Frame
, layout = BorderLayout
SOUTH
= Panel
, layout = BorderLayout
NORT
= Panel
, layout = FlowLayout
CheckBox("ASCII")
+ ChecboxGroup
CheckBox("Binary")
+ ChecboxGroup
CheckBox("Auto")
CENTER
= TextArea(3,30)
SOUTH
= Panel
, layout = GridLayout(1,
0)
Button("Close")
Button("Cancel")
Button("LogWnd")
Button("Help")
Button("Options")
Button("About")
Button("Exit")
Maintenant il va falloir sintéresser à la partie CENTER
du BorderLayout
extérieur. Dabord, examinez ce dessin de
la section centrale :
Vous ne voyez rien dintéressant ? Cela a un rapport avec les deux boîtes dessinées autour des composants de la partie gauche et ceux de la partie droite. Ils sont structurellement les mêmes. Exactement les mêmes. Lorsque deux choses sont les mêmes, et que les faire est une corvée, vous devez penser réutilisation.
Prenons un de ces machins et examinons-le séparément. Décidons
den faire une classe FileDisplay
. Pour commencer lexamen de cette
sous-IUG recherchons ses éléments frontaliers. On en voit immédiatement
trois :
La partie NORTH
consiste en un Label
et une « combo
box » (nous utiliserons un composant Choice
de AWT
pour le moment), empilés verticalement. Le CENTER
est un
composant List
. LEast
est un groupe de Button
empilés verticalement.
SI nous regardons dabord la partie NORTH
, il faut que le Label
et le Choice
prennent leur hauteur préférée
et toute la largeur. Puisque ceci est le composant NORTH
, tout
ce quon y mettra dedans recevra sa largeur préférée. En
plaçant le Label
et le Choice
comme NORTH
et SOUTH
(ou NORTH
et CENTER
, ou CENTER
et SOUTH
) dun BorderLayout
, ils auront leur hauteur
préférée. Encore une fois, un GridLayout
aurait
été un mauvais choix car cela aurait supposé que les deux
composants avaient la même hauteur. Vous pouvez vouloir changer la taille
de la police dans le Label
pour la rendre plus grosse, sans vouloir
que cela affecte la taille de lobjet Choice
.
La partie CENTER
est simple : uniquement un composant List
.
Non contenu dans un Panel
(sauf si vous avec une sous-classe de
Panel
qui fournit une bordure décorative, comme le BorderPanel
précédemment étudié).
Venons-en à la partie EAST
, qui est un peu plus tordue.
Vous voulez que tous les composants Button
aient la même
taille (vos pensez immédiatement GridLayout
) mais cette
taille doit être constamment leur taille préférée.
Cest lespace sous les boutons qui sagrandit et se rétrécit
si nécessaire.
Puisque vous avez placé toute la pile de Button
comme le
composant EAST
dun BorderLayout
, leur largeur est
fixée à la largeur préférée du conteneur
qui renferme ces boutons. Pour que leur largeur effective soit leur largeur
préférée vous devez placer toute la bande à lintérieur
dun autre BorderLayout
, en tant que composant NORTH
!
En définitive, le composant EAST
est un Panel
muni dun BorderLayout
contenant un autre Panel
à
titre de composant NORTH
, et ce second Panel
a lui-même
un GridLayout
qui contrôle les boutons. Le GridLayout
étant une simple colonne de boutons, les paramètres de sa construction
sont (0, 1).
Voici donc la structure dun FileDisplay
:
FileDisplay extends Panel
, layout = BorderLayout
NORTH
= Panel
, layout = BorderLayout
NORTH
= Label("")
SOUTH
= Choice
CENTER
= List
EAST
= Panel
, layout = BorderLayout
NORTH
= Panel
, layout = GridLayout(1,
0)
Button("ChgDir")
Button("MkDir")
TextField("*.*")
Button("View")
Button("Exec")
Button("Rename")
Button("Deete")
Button("Refresh")
Button("irInfo")
Pour que tout ceci fonctionne proprement vous devez faire que la propriété
« text » du composant Label
devienne la propriété
« text » du FileDisplay
tout entier. Pou
cela il vous faut définir dans la classe FileDisplay
des
méthodes String getText()
et void setText(String
text)
qui simplement obtiennent ou définissent la valeur du texte
du Label
. Vous verrez cela dans le code montré plus loin.
Il ne reste plus maintenant que la section CENTRE
de la IUG principale.
Examinons à quoi ressemble la section CENTER
quand on enlève
les détails encapsulés dans les FileDisplay
:
Les deux ovales représentent les deux instances de FileDisplay
.
Le comportement de cette partie de la IUG est déterminé par quatre
composants : deux composants FileDisplay
, qui doivent prendre
la même quantité despace, et deux composants Button
,
chacun avec sa taille préférée, flottant librement entre
les deux précédents.
Supposons que, pour des raisons esthétiques, les deux boutons doivent être centrés verticalement.
Cest une situation tordue. Parmi les gestionnaires de disposition standard,
il ny a quun choix possible : GridBagLayout
.
Voyez comment cela devient difficile, y compris pour un cas aussi simple que
celui-ci. Dabord, vous devez déterminer quelles sont les cellules
de la grille. Pour cela, une chose est toujours nécessaire, avec des
GridBagLayout
: dessiner des lignes traversant toute la
IUG entre chaque paire de composants adjacents :
Vous pouvez voir que ce GridBagLayout
a six cellules et quatre
composants (numérotés). Chaque cellule requiert une configuration,
passée à laide dun objet GridBagConstraints
, dans
la version adéquate de la méthode add : add(Component component,
Object constraints)
. Vous devez cheminer à travers des options
des GridBagConstraints
et déterminer lesquelles vous conviennent.
Dabord les plus faciles : gridx
, gridy
, gridwidth
,
gridheight
, ipadx
, et ipady
. Vous pouvez
utiliser des encadrements (insets) au lieu de bourrages (pad),
si bien que ipadx
et ipady
seront nuls pour tous les
composants :
1
|
2
|
3
|
4
|
|
gridx |
0
|
2
|
1
|
1
|
gridy |
0
|
0
|
0
|
1
|
gridwidth |
1
|
1
|
1
|
1
|
gridheight |
2
|
2
|
1
|
1
|
ipadx |
0
|
0
|
0
|
0
|
ipady |
0
|
0
|
0
|
0
|
Ensuite vous devez comprendre le paramètre remplissage (fill).
Rappelez-vous que cela signifie la manière dont les composants sétendent
à lintérieur des cellules quon leur a allouées. Pour
les composants 1 et 2, cela doit être BOTH
; si vous
voulez que les composants 3 et 4 aient leur taille préférée,
le paramètre fill doit être NONE
.
Ensuite, lancre. Puisque les composants 1 et 2 remplissent complètement
leur espace alloué, peu importe leur ancre. Dans ce cas on utilise habituellement
la valeur par défaut CENTER
. Le composant 3 se tient en
bas et au centre de sa cellule, son paramètre ancre doit être SOUTH
.
Le composant 4 est en haut et au centre de sa cellule, son paramètre
ancre est NORTH
.
Les encadrements définissent lespace laissé autour des composants. Il ny a pas despace à laisser autour des composants 1 et 2, ainsi leurs encadrements sont laissés à 0. Vous voulez sans doute un peu despace autour des composants 3 et 4, quelque chose comme 4 pixels entre eux et tout autre composant voisin. Il y a plusieurs manières dobtenir 4 pixels entre les composants 3 et 4, par exemple mettre 2 pour leurs encadrements supérieur et inférieur.
On arrive au pire moment de la construction de ce GridBagLayout
:
les poids. Réfléchissez aux règles que vous voulez :
Par la règle 1, les paramètres weightx
des composants
1 et 2 doivent être égaux. Par la règle 2, les paramètres
weightx
des composants 3 et 4 doivent être 0. Ce sont les
seules règles relatives à lespace horizontal, si bien que vous
pouvez prendre les poids que vous voudrez pour les composants 1 et 2, pourvu
quils soient égaux. Les poids sont des nombres flottants, et une convention
sympa consiste à faire que leur somme soit 1.0. Par conséquent,
il faut donner aux composants 1 et 2 une valeur de 0.5 pour le paramètre
weightx
.
Par la règle 3, les paramètres weighty
des composants
1 et 2 doivent être égaux, et égaux à la valeur weighty
totale du conteneur. Par la règle 4, le paramètre weighty
de chacun des composants 3 et 4 doit être la moitié du weighty
total. Tout cela donne les résultats suivants :
1
|
2
|
3
|
4
|
||
gridx |
0
|
2
|
1
|
1
|
|
gridy |
0
|
0
|
0
|
1
|
|
gridwidth |
1
|
1
|
1
|
1
|
|
gridheight |
2
|
2
|
1
|
1
|
|
ipadx |
0
|
0
|
0
|
0
|
|
ipady |
0
|
0
|
0
|
0
|
|
fill |
BOTH
|
BOTH
|
NONE
|
NONE
|
|
anchor |
CENTER
|
CENTER
|
SOUTH
|
NORTH
|
|
weightx |
0.5
|
0.5
|
0.0
|
0.0
|
|
weighty |
1.0
|
1.0
|
0.5
|
0.5
|
|
insets | top |
0
|
0
|
0
|
2
|
bottom |
0
|
0
|
2
|
0
|
|
left |
0
|
0
|
4
|
4
|
|
right |
0
|
0
|
4
|
4
|
La définition de ces GridBagConstraints
termine la conception
du IUG. Le code réalisé produit ceci :
Lequel, décorations mises à part, se présente exactement comme vous vouliez !
N. B. A propos du dernier GridBagLayout
: quelquefois, la
première idée qui vient en étudiant les composants 3 et
4 est de mettre weightx
et weightxy
égaux à
0 pour les deux composants. Cela produit le résultat suivant :
Notez où sont les boutons "<--" et "-->".
Les valeurs de weightx
et weighty
contrôlent
la taille des cellules de la gille que le composant occupe. Si la somme des
poids pour une ligne ou une colonne données négale pas celle
des autres lignes ou colonnes, alors le GridBagLayout
comble la
différence en définissant le poids du dernier composant dans la
ligne ou la colonne. Dans notre cas nous avons trois colonnes : deux sont
des composants FileDisplay
et le troisième est la colonne
avec les deux buttons dessus. Puisque la somme des valeurs weighty
des deux boutons ne correspond pas à la valeur weighty
des
autres colonnes, GridBagLayout
donne à la cellule du second
bouton le totalité de lespace restant.
Il y a deux approches courantes de lécriture du code dune IUG à la main. Lune consiste à construire les sous-IUG par des appels de méthodes, lautre consiste simplement à mettre tout le code dun seul trait.
Le code suivant illustre la première manière :
Ce code a été généré en utilisant VisualAge for Java, de IBM, et simplifié en lui enlevant la gestion des exceptions et les préfixes « ivj » de tous les noms de variables. Ce code suit une stratégie dinstantiation « paresseuse » : aucun composant de la IUG et aucune sous-IUG ne sont crées avant que e ne soit nécessaire. Ce qui a plusieurs avantages :
Bien entendu, il y a quelques désavantages :
Une autre alternative consiste à utiliser un constructeur de IUG pour créer linterface. Cela peut être une option très intéressante, surtout si votre IUG est très complexe.
En prenant le même exemple et en lécrivant dun seul jet on obtient le code suivant :
Lavantage de cette manière est davoir moins de code, mais il peut être difficile de voir la structure de la IUG dans le code. Il peut aussi devenir très difficile dordonner correctement la création/addition des objets de la IUG : soyez très prudents avant de créer quelque chose avant de lutiliser.
Un besoin fréquent dans la construction de IUG est celui dun masque de saisie3. Voici un exemple simple :
Notez que dans cette IUG toutes les étiquettes sont alignées horizontalement, ainsi que les champs de texte. Lorsque cette IUG est agrandie horizontalement, les champs de texte sagrandissent mais les étiquettes non.
Une des choses à garder dans la tête quand on conçoit cette IUG est ce qui doit arriver lorsque la fenêtre est agrandie verticalement. Les champs de texte ont tendance à devenir laids lorsquils sagrandissent verticalement. Vous voulez probablement conserver leur hauteur préférée.
Garder les étiquettes et les champs de texte à leur taille
préférée
Permettre aux étiquettes et champs de texte de grandir
Bon, comment concevoir cette IUG ?
Dabord vous devez réfléchir à la manière de lier
les étiquettes et les champs de texte à leur hauteur préférée.
Cela peut se faire en plaçant tous les composants comme parties NORTH
dun BorderLayout
.
Ensuite vous devez diviser la IUG en deux parties également étendues :
la partie gauche et la partie droite. Vous devez penser « GridLayout
»
dès que vous entendez « également étendues ».
Ce GridLayout
consiste en une unique ligne ; le paramètre
pour le constructeur sera donc (1, 0) (Dans lexemple ci-dessus on a aussi défini
hgap
à 5).
Maintenant, comment produire les paires étiquette + champ de texte ?
La première réponse est sans doute de placer chaque paire dans
leur propre BorderLayut
. Par exemple :
Panel panel1 = new Panel(new BorderLayout()); panel1.add(new Label("First name:"), BorderLayout.WEST); panel1.add(new TextField(), BorderLayout.CENTER); |
En utilisant cette stratégie pour construire chaque paire détiquettes et de champs de texte on obtient un effet légèrement indésirable :
Quest-ce qui sest passé ? Pensez à ce que BorderLayout
fait. Il dit « Je regarde mon composant WEST
;
je lui donne sa largeur préférée. Ensuite, je donne à
mon composant CENTER
tout le restant de place ». Notez
bien quil ne dit pas « Je regarde le composant au-dessous de moi ;
je massure que mon composant WEST
a la même largeur que
son composant WEST
» Dune manière ou dune
autre, vous avez besoin dassocier dune part les étiquettes les une
avec les autres, et dautre part les champs de texte les uns avec les autres.
La seule manière de faire cela est de mettre toutes les étiquettes qui vont ensemble dans le même conteneur. Et pareil pour les champs de texte. Cependant, vous devez aussi vous assurer que les étiquettes et les champs de texte sont alignés horizontalement.
Plus quune traduction, le texte en bleu est une adaptation assez libre des explications originales et de lexemple qui va avec. [N. du T.]
Lélément le plus extérieur de notre
IUG est un panneau muni dun BorderLayout
. Il a pour composant
NORTH
un panneau (nommé diptyque
dans notre
code), lequel dispose donc de toute la largeur disponible et de sa hauteur préférée.
Ce panneau est muni à son tour dun gestionnaire
GridLayout
à une ligne, ce qui assure que ses deux composants
(nommés voletDeGauche
et voletDeDroite
) ont
exactement la même taille.
Chacun de son côté, ces deux volets sont
munis dun gestionnaire BorderLayout
et ont deux panneaux (nommés
dans notre code panneauDeLabels
et panneauxDeTexte
)
comme composants WEST
et CENTER
, ce qui donne au premier
une largeur fixe (égale à sa largeur préférée)
et au second une largeur qui varie avec les changements de taille du cadre tout
entier.
Enfin, ces panneaux de labels et de textes sont munis
de GridLayout
à trois lignes, et reçoivent les composants
élémentaires (des Label
et des TextField
)
nécessaires.
Finalement, notre IUG sera donc structurée comme suit (on a fait apparaître les noms des variables qui représentent les composants dans notre code) :
Panel
(nommé this
,
lobjet en cours de construction), layout = BorderLayout
NORTH
= Panel
(nommé
diptyque
), layout = GridLayout(1, 0)
Panel
(nommé voletDeGauche
),
layout = BorderLayout
WEST
= Panel
(nommé panneauDeLabels
), layout = GridLayout(3,
0)
Label
Label
Label
CENTER
= Panel
(nommé panneauDeTextes
), layout = GridLayout(3,
0)
TextField(12)
TextField
TextField
Panel
(nommé voletDeDroite
),
layout = BorderLayout
WEST
= Panel
(nommé panneauDeLabels
), layout = GridLayout(3,
0)
Label
Label
CENTER
= Panel
(nommé panneauDeTextes
), layout = GridLayout(3,
0)
TextField
TextField
Le code correspondant, enrichi
dun peu de hgap
et vgap
pour écarter
les composants entre eux, se trouve dans le fichier InputForm.java.
Notez quun des TextField
reçoit
une indication de largeur (12 colonnes). Cela définit sa taille préférée
initiale. Il faut le faire car notre panneau est placé dans un cadre
dont la taille est initialisée par un appel de pack()
;
si tous les champs de texte étaient sans indication de largeur, ils seraient
tous ridiculement réduits. Dautre part, il suffit de fixer la largeur
dun champ de texte, puisque notre assemblage de gestionnaires de disposition
fait en sorte quils soient tous de même largeur.
Notez également que cette IUG est satisfaisante parce que le plus long texte dune étiquette du volet de gauche (« First name : ») est sensiblement aussi long, une fois affiché, que le plus long texte dune étiquette du volet de droite (« Last name : »). Cela rend toutes les étiquettes sensiblement égales. Si ce nétait pas le cas, il faudrait forcer légalité en définissant explicitement la longueur de la plus longue étiquette de chaque côté :
... Label etiquette = new Label("First name : "); etiquette.setPreferredSize(new Dimension(100, 0)); panneauDeLabels.add(etiquette); panneauDeLabels.add(new Label("Street : ")); panneauDeLabels.add(new Label("Phone : ")); ... |
Il faut noter les paramètres passés aux conteneurs GridLayout
les plus imbriqués. On aura pu les concevoir en partant du fait quils
ont une colonne. Cela aurait été une erreur : si au lieu
de « panneauDeTextes = new Panel(new GridLayout(3, 0,...));
»
on avait construit ces conteneurs par lappel « panneauDeTextes
= new Panel(new GridLayout(0, 1, ...));
» la IUG aurait ressemblé
à ceci :
Copyright © 1998-1999 MageLang Institute. All Rights Reserved.
[1] Gestionnaire de disposition est employée partout dans ce document comme traduction de layout manager.
[2] Nous traduisons insets par encadrement.
[3] Nous traduisons input form par masque de saisie.
[4] Rappel : on dit qu'on « sous-classe » un objet lorsqu'on définit une sous-classe de sa classe afin de pouvoir redéfinir certaines de ses méthodes.