IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Penser en Java 2nde édition - Sommaire |  Préface |  Avant-propos | Chapitre : 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15 |  Annexe : A B C D  | Tables des matières - Thinking in Java

  Chapitre 13 - Création de fenêtres & d'Applets

pages : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 

Dans init(), addActionListener() est utilisée pour enregistrer l'objet BL pour chacun des boutons.

Il est souvent plus pratique de coder l'ActionListener comme une classe anonyme interne [anonymous inner class], particulièrement lorsqu'on a tendance à n'utiliser qu'une seule instance de chaque classe listener. Button2.java peut être modifié de la façon suivante pour utiliser une classe interne anonyme :

//: c13:Button2b.java
// Utilisation de classes anonymes internes.
// <applet code=Button2b width=200 height=75>
// </applet>
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import com.bruceeckel.swing.*;

public class Button2b extends JApplet {
JButton
  b1 = new JButton("Button 1"),
  b2 = new JButton("Button 2");
JTextField txt = new JTextField(10);
ActionListener al = new ActionListener() {
   public void actionPerformed(ActionEvent e){
    String name =
      ((JButton)e.getSource()).getText();
    txt.setText(name);
  }
};
  public void init() {
  b1.addActionListener(al);
  b2.addActionListener(al);
  Container cp = getContentPane();
  cp.setLayout(new FlowLayout());
  cp.add(b1);
  cp.add(b2);
  cp.add(txt);
}
  public static void main(String[] args) {
  Console.run(new Button2b(), 200, 75);
}
} ///:~

L'utilisation d'une classe anonyme interne sera préférée (si possible) pour les exemples de ce livre.

Zones de texte

Un JTextArea est comme un JTextField, sauf qu'il peut avoir plusieurs lignes et possède plus de fonctionnalités. Une méthode particulièrement utile est append(); avec cette méthode on peut facilement transférer une sortie dans un JTextArea, faisant de ce fait d'un programme Swing une amélioration (du fait qu'on peut scroller en arrière) par rapport à ce qui a été fait jusqu'ici en utilisant des programmes de ligne de commande qui impriment sur la sortie standard. Comme exemple, le programme suivant remplit un JTextArea avec la sortie du générateur geography du chapitre 9 :

//: c13:TextArea.java
// Utilisation du contrôle JTextArea.
// <applet code=TextArea width=475 height=425>
// </applet>
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.util.*;
import com.bruceeckel.swing.*;
import com.bruceeckel.util.*;

public class TextArea extends JApplet {
JButton
  b = new JButton("Add Data"),
  c = new JButton("Clear Data");
JTextArea t = new JTextArea(20, 40);
Map m = new HashMap();
  public void init() {
   // Utilisation de toutes les données :
  Collections2.fill(m,
    Collections2.geography,
    CountryCapitals.pairs.length);
  b.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e){
      for(Iterator it= m.entrySet().iterator();
          it.hasNext();){
        Map.Entry me = (Map.Entry)(it.next());
        t.append(me.getKey() + ": "
          + me.getValue() + "\n");
      }
    }
  });
  c.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e){
      t.setText("");
    }
  });
  Container cp = getContentPane();
  cp.setLayonew FlowLayout());
  cp.add(new JScrollPane(t));
  cp.add(b);
  cp.add(c);
}
  public static void main(String[] args) {
  Console.run(new TextArea(), 475, 425);
}
} ///:~

Dans init(), le Map est rempli avec tous les pays et leurs capitales. Remarquons que pour chaque bouton l'ActionListener est créé et ajouté sans définir de variable intermédiaire, puisqu'on n'aura plus jamais besoin de s'y référer dans la suite du programme. Le bouton "Add Data" formate et ajoute à la fin toutes les données, tandis que le bouton "Clear Data" utilise setText() pour supprimer tout le texte du JTextArea.

Lors de l'ajout du JTextArea à l'applet, il est enveloppé dans un JScrollPane, pour contrôler le scrolling quand trop de texte est placé à l'écran. C'est tout ce qu'il y a à faire pour fournir des fonctionnalités de scrolling complètes. Ayant essayé d'imaginer comment faire l'équivalent dans d'autres environnements de programmation de GUI, je suis très impressionné par la simplicité et la bonne conception de composants tels que le JScrollPane.

Contrôle de la disposition

La façon dont on place les composants sur un formulaire en Java est probablement différente de tout système de GUI que vous avez utilisé. Premièrement, tout est dans le code ; il n'y a pas de ressources qui contrôlent le placement des composants. Deuxièmement, la façon dont les composants sont placés dans un formulaire est contrôlée non pas par un positionnement absolu mais par un layout manager qui décide comment les composants sont placés, selon l'ordre dans lequel on les ajoute ( add() ). La taille, la forme et le placement des composants seront notablement différents d'un layout manager à l'autre. De plus, les gestionnaires de disposition s'adaptent aux dimensions de l'applet ou de la fenêtre de l'application, de sorte que si la dimension de la fenêtre est changée, la taille, la forme et le placement des composants sont modifiés en conséquence.

JApplet, JFrame, JWindow et JDialog peuvent chacun fournir un Container avec getContentPane() qui peut contenir et afficher des Components. Dans Container, il y a une méthode appelée setLayout() qui permet de choisir le layout manager. D'autres classes telles que JPanel contiennent et affichent des composants directement, et donc il faut leur imposer directement le layout manager, sans utiliser le content pane.

Dans cette section nous allons explorer les divers gestionnaires de disposition en créant des boutons (puisque c'est ce qu'il y a de plus simple). Il n'y aura aucune capture d'événements de boutons puisque ces exemples ont pour seul but de montrer comment les boutons sont disposés.

BorderLayout

L'applet utilise un layout manager par défaut : le BorderLayout (certains des exemples précédents ont modifié le layout manager par défaut pour FlowLayout). Sans autre information, il prend tout ce qu'on lui ajoute ( add() ) et le place au centre, en étirant l'objet dans toutes les directions jusqu'aux bords.

Cependant le BorderLayout ne se résume pas qu'à cela. Ce layout manager possède le concept d'une zone centrale et de quatre régions périphériques. Quand on ajoute quelque chose à un panel qui utilise un BorderLayout, on peut utiliser la méthode add() surchargée qui prend une valeur constante comme premier argument. Cette valeur peut être une des suivantes :

BorderLayout.NORTH (en haut) BorderLayout.SOUTH (en bas) BorderLayout.EAST (à droite) BorderLayout.WEST (à gauche) BorderLayout.CENTER (remplir le milieu, jusqu'aux autres composants ou jusqu'aux bords)

Si aucune région n'est spécifiée pour placer l'objet, le défaut est CENTER.

Voici un exemple simple. Le layout par défaut est utilisé, puisque JApplet a BorderLayout par défaut :

//: c13:BorderLayout1.java
// Démonstration de BorderLayout.
// <applet code=BorderLayout1
// width=300 height=250> </applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;

public class BorderLayout1 extends JApplet {
  public void init() {
  Container cp = getContentPane();
  cp.add(BorderLayout.NORTH,
    new JButton("North"));
  cp.add(BorderLayout.SOUTH,
    new JButton("South"));
  cp.add(BorderLayout.EAST,
    new JButton("East"));
  cp.add(BorderLayout.WEST,
    new JButton("West"));
cp.add(BorderLayout.CENTER,
    new JButton("Center"));
}
  public static void main(String[] args) {
  Console.run(new BorderLayout1(), 300, 250);
}
} ///:~

Pour chaque placement autre que CENTER, l'élément qu'on ajoute est comprimé pour tenir dans le plus petit espace le long d'une dimension et étiré au maximum le long de l'autre dimension. CENTER, par contre, s'étend dans chaque dimension pour occuper le milieu.

FlowLayout

Celui-ci aligne simplement les composants sur le formulaire, de gauche à droite jusqu'à ce que l'espace du haut soit rempli, puis descend d'une rangée et continue l'alignement.

Voici un exemple qui positionne le layout manager en FlowLayout et place ensuite des boutons sur le formulaire. On remarquera qu'avec FlowLayout les composants prennent leur taille naturelle. Un JButton, par exemple, aura la taille de sa chaîne.

//: c13:FlowLayout1.java
// Démonstration de FlowLayout.
// <applet code=FlowLayout1
// width=300 height=250> </applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;

public class FlowLayout1 extends JApplet {
  public void init() {
  Container cp = getContentPane();
  cp.setLayout(new FlowLayout());
   for(int i = 0; i     cp.add(new JButton("Button " + i));
}
  public static void main(String[] args) {
  Console.run(new FlowLayout1(), 300, 250);
}
} ///:~

Tous les composants sont compactés à leur taille minimum dans un FlowLayout, ce qui fait qu'on peut parfois obtenir un comportement surprenant. Par exemple, vu qu'un JLabel prend la taille de sa chaîne, une tentative de justifier à droite son texte ne donne pas de modification de l'affichage dans un FlowLayout.

GridLayout

Un GridLayout permet de construire un tableau de composants, et lorsqu'on les ajoute ils sont placés de gauche à droite et de haut en bas dans la grille. Dans le constructeur on spécifie le nombre de rangées et de colonnes nécessaires, et celles-ci sont disposées en proportions identiques.

//: c13:GridLayout1.java
// Démonstration de GridLayout.
// <applet code=GridLayout1
// width=300 height=250> </applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;

public class GridLayout1 extends JApplet {
  public void init() {
  Container cp = getContentPane();
  cp.setLayout(new GridLayout(7,3));
   for(int i = 0; i     cp.add(new JButton("Button " + i));
}
  public static void main(String[] args) {
  Console.run(new GridLayout1(), 300, 250);
}
} ///:~

Dans ce cas il y a 21 cases mais seulement 20 boutons. La dernière case est laissée vide car il n'y a pas d'équilibrage dans un GridLayout.

GridBagLayout

Le GridBagLayoutnous donne un contrôle fin pour décider exactement comment les régions d'une fenêtre vont se positionner et se replacer lorsque la fenêtre est redimensionnée. Cependant, c'est aussi le plus compliqué des layout managers, et il est assez difficile à comprendre. Il est destiné principalement à la génération de code automatique par un constructeur d'interfaces utilisateurs graphiques [GUI builder] (les bons GUI builders utilisent GridBagLayout plutôt que le placement absolu). Si votre modèle est compliqué au point que vous sentiez le besoin d'utiliser le GridBagLayout, vous devrez dans ce cas utiliser un outil GUI builder pour générer ce modèle. Si vous pensez devoir en connaître les détails internes, je vous renvoie à Core Java 2 par Horstmann & Cornell (Prentice-Hall, 1999), ou un livre dédié à Swing, comme point de départ.

Positionnement absolu

Il est également possible de forcer la position absolue des composants graphiques de la façon suivante :

  1. Positionner un layout manager null pour le Container : setLayout(null).
  2. Appeler setBounds() ou reshape() (selon la version du langage) pour chaque composant, en passant un rectangle de limites avec ses coordonnées en pixels. Ceci peut se faire dans le constructeur, ou dans paint(), selon le but désiré.

Certains GUI builders utilisent cette approche de manière extensive, mais ce n'est en général pas la meilleure manière de générer du code. Les GUI builders les plus efficaces utilisent plutôt GridBagLayout.

BoxLayout

Les gens ayant tellement de problèmes pour comprendre et utiliser GridBagLayout, Swing contient également le BoxLayout, qui offre la plupart des avantages du GridBagLayout sans en avoir la complexité, de sorte qu'on peut souvent l'utiliser lorsqu'on doit coder à la main des layouts (encore une fois, si votre modèle devient trop compliqué, utilisez un GUI builder qui générera les GridBagLayouts à votre place). BoxLayout permet le contrôle du placement des composants soit verticalement soit horizontalement, et le contrôle de l'espace entre les composants en utilisant des choses appelées struts (entretoises) et glue (colle). D'abord, voyons comment utiliser BoxLayout directement, en faisant le même genre de démonstration que pour les autres layout managers :

//: c13:BoxLayout1.java
// BoxLayouts vertical et horizontal.
// <applet code=BoxLayout1
// width=450 height=200> </applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;

public class BoxLayout1 extends JApplet {
  public void init() {
  JPanel jpv = new JPanel();
  jpv.setLayout(
    new BoxLayout(jpv, BoxLayout.Y_AXIS));
   for(int i = 0; i     jpv.add(new JButton("" + i));
  JPanel jph = new JPanel();
  jph.setLayout(
    new BoxLayout(jph, BoxLayout.X_AXIS));
   for(int i = 0; i     jph.add(new JButton("" + i));
  Container cp = getContentPane();
  cp.add(BorderLayout.EAST, jpv);
  cp.add(BorderLayout.SOUTH, jph);
}
  public static void main(String[] args) {
  Console.run(new BoxLayout1(), 450, 200);
}
} ///:~

Le constructeur du BoxLayout est un peu différent des autres layout managers : on fournit le Container que le BoxLayout doit contrôler comme premier argument, et la direction du layout comme deuxième argument.

Pour simplifier les choses, il y a un container spécial appelé Box qui utilise BoxLayout comme manager d'origine. L'exemple suivant place les composants horizontalement et verticalement en utilisant Box, qui possède deux méthodes static pour créer des boxes avec des alignements verticaux et horizontaux :

//: c13:Box1.java
// BoxLayouts vertical et horizontal.
// <applet code=Box1
// width=450 height=200> </applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;

public class Box1 extends JApplet {
  public void init() {
  Box bv = Box.createVerticalBox();
   for(int i = 0; i     bv.add(new JButton("" + i));
  Box bh = Box.createHorizontalBox();
   for(int i = 0; i     bh.add(new JButton("" + i));
  Container cp = getContentPane();
  cp.add(BorderLayout.EAST, bv);
  cp.add(BorderLayout.SOUTH, bh);
}
  public static void main(String[] args) {
  Console.run(new Box1(), 450, 200);
}
} ///:~

Une fois qu'on a obtenu un Box, on le passe en second argument quand on ajoute des composants au content pane.

Les struts ajoutent de l'espace entre les composants, mesuré en pixels. Pour utiliser un strut, on l'ajoute simplement entre les ajouts de composants que l'on veut séparer :

//: c13:Box2.java
// Ajout de struts.
// <applet code=Box2
// width=450 height=300> </applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;

public class Box2 extends JApplet {
  public void init() {
  Box bv = Box.createVerticalBox();
   for(int i = 0; i     bv.add(new JButton("" + i));
    bv.add(Box.createVerticalStrut(i*10));
  }
  Box bh = Box.createHorizontalBox();
   for(int i = 0; i     bh.add(new JButton("" + i));
    bh.add(Box.createHorizontalStrut(i*10));
  }
  Container cp = getContentPane();
  cp.add(BorderLayout.EAST, bv);
  cp.add(BorderLayout.SOUTH, bh);
}
  public static void main(String[] args) {
  Console.run(new Box2(), 450, 300);
}
} ///:~

Ce livre a été écrit par Bruce Eckel ( télécharger la version anglaise : Thinking in java )
Ce chapitre a été traduit par P. Boite ( groupe de traduction )
télécharger la version francaise (PDF) | Commandez le livre en version anglaise (amazon) | télécharger la version anglaise
pages : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 
Penser en Java 2nde édition - Sommaire |  Préface |  Avant-propos | Chapitre : 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15 |  Annexe : A B C D  | Tables des matières - Thinking in Java