Les stratégies de placement
Nous allons, dans ce chapitre étudier un ensemble de classes chargées de résoudre les problèmes de positionnement de vos composants graphiques aux sein de vos conteneurs. Ce groupe de classes, que sont les layout manager (gestionnaires de positionnement : on préférera parler de stratégies de placement), peut être utilisé soit dans une interface AWT, soit dans une interface Swing, sans distinction.
En fait, il existe très peu de layout manager : 5 classes, plus quelques unes introduitent avec Swing. Pour les cinq premières, sachez qu'elles sont contenues dans le package java.awt. Les dernières se trouvent dans le package java.swing. Nous allons donc tout au long de ce chapitre les étudier une par une.
Notez une chose importante : même si vous êtes en train de travailler avec Swing, vous vous devez d'importer le package java.awt pour pouvoir manipuler les stratégies de placement. Elles ne sont pas ré-écrite dans le package Swing.
Affecter une stratégie de placement à un conteneur
La première chose à savoir, est qu'une stratégie de placement se définie au niveau d'un conteneur (objet de type java.awt.Container). Vous pouvez ne pas en définir : dans ce cas, vous vous devrez de systématiquement positionner vos composants au pixels près. Cette solution peu utilisable rend malgré tout dans certains cas de grands services.
Bien entendu, une stratégie de placement se définie en créant un objet instance de l'une des classes de stratégie de placement. Une fois cet objet instancié, il ne reste plus qu'à l'affecter à un conteneur. Pour ce faire, on utilise la méthode setLayout. Elle prend un unique paramètre : le LayoutManager. Si vous passez la valeur nulle en paramètre, aucune stratégie de placement ne sera utilisée : il faut alors positionner au pixel près vos composants.
La stratégie FlowLayout
Cette première stratégie est certainement la plus utilisée et la plus simple à manipuler. Si vous l'affectez à un conteneur, celui-ci va tenter de mettre le plus de composants possible sur une première ligne. Dès que la taille du conteneur ne permet plus d'insérer un nouveau composant, le layout utilise alors un seconde ligne, et ainsi de suite jusqu'à arriver au dernier composant.
Vous pouvez de plus configurer votre layout en spécifiant si vous souhaitez que chaque ligne de composant soit alignée à gauche, à droite ou, si vous préférez, centrée. Cette configuration peut être mise en place soit à la construction de l'objet de positionnement, soit par la suite en utilisant la méthode setAlignment. Des attributs de la classe FlowLayout sont préfédinis : FlowLayout.LEFT, FlowLayout.CENTER, FlowLayout.RIGHT par exemple.
A titre d'exemple, considérons le programme suivant : il créer une dizaine de boutons est demande de les placer dans une fenêtre. Pour mettre en évidence le rôle de la stratégie de placement, il suffit de retailler la fenêtre. Notez de plus que le programme est transformé en applet, histoire que vous puissiez directement tester ce comportement. Cliquez sur le bouton de l'applet pour demander l'affichage de la fenêtre.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class Layout1 extends Applet {
Image startImage = null;
Frame f = null;
public void init() {
try {
MediaTracker mt = new MediaTracker(this);
startImage = this.getImage(this.getCodeBase(),"start.gif");
mt.addImage(startImage,0);
this.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
showWindow();
}
});
mt.waitForAll();
} catch(Exception e) {}
}
public void stop() {
if (f != null && f.isVisible())
f.setVisible(false);
}
public void paint(Graphics gc) {
gc.drawImage(startImage,0,0,null);
}
public void showWindow() {
f = new Frame("Ma fenetre");
f.setLayout(new FlowLayout(FlowLayout.CENTER));
for(int i=0;i
<10;i++) { f.add(new Button("Button n°"+ i));
}
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent event) {
f.setVisible(false);
}
});
}
} |
"Layout1.class": sorry, WinHelp cannot show this applet...
Quelques explications malgré tous sont nécessaires. Le programme est constitué de quatre méthodes : seules la dernière nous intéresse en terme de stratégie de placement. Cependant, regardons un peu les trois premières méthodes. La première permet d'initialiser l'applet en pré-chargeant un petite image : on ne sort de cette méthode qu'à la fin du chargement de l'image. La second (stop) sert à fermer la fenêtre au cas ou vous quitteriez le document en cours de visualisation dans votre navigateur. La troisième (paint) sert à redessiner le contenu de l'applet dès lors que c'est nécessaire. Enfin, la dernière créer le fenêtre de tests. Pour de plus ample informations sur la mise en place d'applet, reportez-vous au chapitre concerné.
La stratégie BorderLayout
Cette seconde stratégie permet d'opérer une division de l'espace utilisable au sein d'un conteneur. Par opposition au FlowLayout, le nombre de zones utilisable par cette stratégie est figé : un BorderLayout divise l'espace utilisable en cinq zones. Dans chacune de ces zones, vous pouvez qu'un unique composant (mais rien n'empêche que certains de ces composants soient des containers. Afin de mieux percevoir la topographie de cette stratégie de placement, étudions de plus près ce petit exemple.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class Layout2 extends Applet {
Image startImage = null;
Frame f = null;
public void init() {
try {
MediaTracker mt = new MediaTracker(this);
startImage = this.getImage(this.getCodeBase(),"start.gif");
mt.addImage(startImage,0);
this.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
showWindow();
}
});
mt.waitForAll();
} catch(Exception e) {}
}
public void stop() {
if (f != null && f.isVisible())
f.setVisible(false);
}
public void paint(Graphics gc) {
gc.drawImage(startImage,0,0,null);
}
public void showWindow() {
f = new Frame("Ma fenetre");
f.setLayout(new BorderLayout());
f.add(new Label("Un Label"),BorderLayout.NORTH);
f.add(new Label("Un Label"),BorderLayout.SOUTH);
f.add(new TextField("Un TextField"),BorderLayout.WEST);
f.add(new TextField("Un TextField"),BorderLayout.EAST);
f.add(new Button("Un boutton"),BorderLayout.CENTER);
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent event) {
f.setVisible(false);
}
});
}
} |
"Layout2.class": sorry, WinHelp cannot show this applet...
Comme vous l'avez remarqué dans l'exemple, on ajoute au conteneur (ici l'applet) cinq composants (deux labels, deux zones de texte et un bouton). Chacun de ces composants est inséré dans une zone définie par le BorderLayout : un label du la zone supérieur (BorderLayout.NORTH), un autre label au sud (BorderLayout.SOUTH), deux zones de saisie de texte à droite et à gauche (BorderLayout.EAST et BorderLayout.WEST) et enfin un bouton au centre (BorderLayout.CENTER). Notez que l'on spécifie la zone ou doit être inséré le composant en invoquant la méthode add sur un conteneur. Autre petite chose à savoir : vous n'êtes pas obligé d'utiliser toutes les zones : dans ce cas là, le gestionnaire de positionnement s'adaptera automatiquement.
Si vous avez été curieux, vous avez du retailler la fenêtre. Si cela n'a pas encore été le cas, faîtes le ! Que constatez-vous ? Et oui, en effet, chacun des composants se retaille aussi pour occuper 100% de la superficie de la zone qui lui est associée. Les zones de saisie de texte et le bouton deviennent difforme. A priori, vous pourriez penser qu'une telle stratégie n'est pas utilisable. Détrompez-vous ! Au contraire, elle peut être fort utile, mais à une condition : placez y des conteneurs. Ainsi, sur chaque conteneur, vous définissez à nouveau une stratégie de placement, et groupe de composants par groupe, vous définissez la structure de votre interface. A titre d'exemple, étudiez le code suivant.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class Layout3 extends Applet {
Image startImage = null;
Frame f = null;
public void init() {
try {
MediaTracker mt = new MediaTracker(this);
startImage = this.getImage(this.getCodeBase(),"start.gif");
mt.addImage(startImage,0);
this.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
showWindow();
}
});
mt.waitForAll();
} catch(Exception e) {}
}
public void stop() {
if (f != null && f.isVisible())
f.setVisible(false);
}
public void paint(Graphics gc) {
gc.drawImage(startImage,0,0,null);
}
public void showWindow() {
f = new Frame("Ma fenetre");
f.setLayout(new BorderLayout());
Panel panel1 = new Panel();
panel1.setLayout(new FlowLayout());
for(int i=0; i<5;i++) panel1.add(new Button("Button H"+i)); Panel panel2 = new Panel();
panel2.setLayout(new GridLayout(-1,1));
for(int i=0; i<5; i++) panel2.add(new Button("Button V"+i));
f.add(panel1,BorderLayout.NORTH);
f.add(panel2,BorderLayout.EAST);
f.add(new Button("Un boutton"),BorderLayout.CENTER);
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent event) {
f.setVisible(false);
}
});
}
} |
"Layout3.class": sorry, WinHelp cannot show this
applet...
La stratégie GridLayout
Nous allons maintenant parler d'une autre stratégie de placement. Celle-ci permet de disposer vos composants dans une grille constituée de lignes et de colonnes. Notez que nous avons déjà testé cette stratégie dans le dernier exemple de la stratégie précédente (la petit barre de boutons dans la zone de droite du
BorderLayout).
Donc, comme dit précédemment, le but de cette stratégie est de découper l'espace d'un conteneur un une grille. Pour ce faire, le constructeur de la classe GridLayout peut accepter deux paramètres : le nombre de lignes et le nombre de colonnes. En fait, cette classe accepte d'autre constructeurs, dont voici leurs prototypes.
GridLayout();
GridLayout(int rows,int cols);
GridLayout(int rows,int cols,int hgap, int vgap); |
Notez un détail fort utile : si vous passez une valeur inférieure ou égale à zéro, en terme de nombre de lignes (ou de colonnes), alors le gestionnaire de positionnement acceptera un nombre quelconque de lignes (ou de colonnes). Dans certains cas, cela simplifie le maintient et l'évolution de votre code.
Un premier exemple simple
Nous allons commencer par un exemple simple d'utilisation de ce gestionnaire de positionnement. Par la suite nous améliorerons le tout. Dans cet exemple, nous allons placer six boutons dans une grille de trois lignes par deux colonnes. Afin de bien comprendre le fonctionnement de ce
layout, il est conseiller de retailler la fenêtre.
Pour ajouter les composants dans le conteneur, nous utilisons toujours la méthode add à laquelle nous passons nos composants en paramètres. Notez qu'elle ne prend pas de second paramètre. En effet cette stratégie de positionnement cherche à remplir d'abord la première ligne de la gauche vers la droite, puis passe ensuite à la ligne suivante, et ainsi de suite.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class GridLayout1 extends Applet {
Image startImage = null;
Frame f = null;
public void init() {
try {
MediaTracker mt = new MediaTracker(this);
startImage = this.getImage(this.getCodeBase(),"start.gif");
mt.addImage(startImage,0);
this.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
showWindow();
}
});
mt.waitForAll();
} catch(Exception e) {}
}
public void stop() {
if (f != null && f.isVisible())
f.setVisible(false);
}
public void paint(Graphics gc) {
gc.drawImage(startImage,0,0,null);
}
public void showWindow() {
f = new Frame("Ma fenetre");
f.setLayout(new GridLayout(3,2));
for(int i=0; i<6; i++) f.add(new Button("Bouton" + i));
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent event) {
f.setVisible(false);
}
});
}
} |
"GridLayout1.class": sorry, WinHelp cannot show this
applet...
Qu'avez-vous constaté ? Et oui, le composants placé dans les cellules de la grille cherche a en occuper tout l'espace, ce qui n'est pas toujours du plus bel effet.
Embellissons un peu les choses
Pour rendre l'exemple précédent un peu plus agréable à visionner, il y a plusieurs solutions envisageables. Une seule l'est si l'on souhaite garder le gestionnaire
GridLayout. Elle consiste à mettre espace de séparation entre chaque composant. Pour ce faire, il vous suffit de donner des valeurs à deux attributs de votre gestionnaire (soit à la construction, soit par les méthodes d'accès). Ces attributs fixent les espaces pour les séparation horizontales et celles verticales.
L'exemple suivant utilise cette technique. Le résultat est initialement plus agréable, mais si vous cherchez à retailler la fenêtre, vous observerez toujours un comportement d'adaptation de la superficie de composant à celle de la cellule. Les séparations sont malgré tout conservées.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class GridLayout2 extends Applet {
Image startImage = null;
Frame f = null;
public void init() {
try {
MediaTracker mt = new MediaTracker(this);
startImage = this.getImage(this.getCodeBase(),"start.gif");
mt.addImage(startImage,0);
this.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
showWindow();
}
});
mt.waitForAll();
} catch(Exception e) {}
}
public void stop() {
if (f != null && f.isVisible())
f.setVisible(false);
}
public void paint(Graphics gc) {
gc.drawImage(startImage,0,0,null);
}
public void showWindow() {
f = new Frame("Ma fenetre");
GridLayout layout = new GridLayout(3,2);
layout.setHgap(10);
layout.setVgap(10);
f.setLayout(layout);
for(int i=0; i<6; i++) f.add(new Button("Bouton" + i));
f.pack();
f.setVisible(true);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent event) {
f.setVisible(false);
}
});
}
} |
"GridLayout2.class": sorry, WinHelp cannot show this
applet...
Remarquez un petit détail : les tailles précisées sont bien celles des séparations entre composants. Il n'y a pas d'espace entre les bords du conteneurs et les composants.
La stratégie GridBagLayout
La stratégie CardLayout
La stratégie BoxLayout
...
|