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 

Ce n'est pas une liste exhaustive, en partie du fait que le modèle d'événements nous permet de créer nos propres types d'événements et listeners associés. De ce fait, on rencontrera souvent des bibliothèques qui ont inventé leurs propres événements, et la connaissance acquise dans ce chapitre nous permettra de comprendre l'utilisation de ces événements.

Utilisation de listener adapters pour simplifier

Dans le tableau ci-dessus, on peut voir que certaines interfaces listener ne possèdent qu'une seule méthode. Celles-ci sont triviales à implémenter puisqu'on ne les implémentera que lorsqu'on désire écrire cette méthode particulière. Par contre, les interfaces listener qui ont plusieurs méthodes peuvent être moins agréables à utiliser. par exemple, quelque chose qu'il faut toujours faire en créant une application est de fournir un WindowListener au JFrame de manière à pouvoir appeler System.exit() pour sortir de l'application lorsqu'on reçoit l'événement windowClosing(). Mais comme WindowListener est une interface, il faut implémenter chacune de ses méthodes même si elles ne font rien. Cela peut être ennuyeux.

Pour résoudre le problème, certaines (mais pas toutes) des interfaces listener qui ont plus d'une méthode possèdent des adaptateurs [adapters], dont vous pouvez voir les noms dans le tableau ci-dessus. Chaque adaptateur fournit des méthodes vides par défaut pour chacune des méthodes de l'interface. Ensuite il suffit d'hériter de cet adaptateur et de redéfinir uniquement les méthodes qu'on doit modifier. Par exemple, le WindowListener qu'on utilisera normalement ressemble à ceci (souvenez-vous qu'il a été encapsulé dans la classe Console de com.bruceeckel.swing) :

class MyWindowListener extends WindowAdapter {
  public void windowClosing(WindowEvent e) {
System.exit(0);
}
}

Le seul but des adaptateurs est de faciliter la création des classes listener.

Il y a cependant un désavantage lié aux adaptateurs, sous la forme d'un piège. Supposons qu'on écrive un WindowAdapter comme celui ci-dessus :

class MyWindowListener extends WindowAdapter {
  public void WindowClosing(WindowEvent e) {
System.exit(0);
}
}

Ceci ne marche pas, mais il nous rendra fous à comprendre pourquoi, car tout va compiler et s'exécuter correctement, sauf que la fermeture de la fenêtre ne fera pas sortir du programme. Voyez-vous le problème ? Il est situé dans le nom de la méthode : WindowClosing() au lieu de windowClosing(). Une simple erreur de majuscule se traduit par l'ajout d'une méthode nouvelle. Ce n'est cependant pas cette méthode qui est appelée lorsque la fenêtre est fermée, de sorte qu'on n'obtient pas le résultat attendu. En dépit de cet inconvénient, une interface garantit que les méthodes sont correctement implémentées.

Surveiller plusieurs événements

Pour nous prouver que ces événements sont bien déclenchés, et en tant qu'expérience intéressante, créons une applet qui surveille les autres comportement d'un JButton, autres que le simple fait qu'il soit appuyé ou pas. Cet exemple montre également comment hériter de notre propre objet bouton, car c'est ce qui est utilisé comme cible de tous les événements intéressants. Pour cela, il suffit d'hériter de JButton name="fnB69" href="#fn69">[69].

La classe MyButton est une classe interne de TrackEvent, de sorte que MyButton peut aller dans la fenêtre parent et manipuler ses champs textes, ce qu'il faut pour pouvoir écrire une information d'état dans les champs du parent. Bien sûr ceci est une solution limitée, puisque MyButton peut être utilisée uniquement avec TrackEvent. Ce genre de code est parfois appelé "fortement couplé" :

//: c13:TrackEvent.java
// Montre les evenements lorsqu'ils arrivent.
// <applet code=TrackEvent
//  width=700 height=500></applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import com.bruceeckel.swing.*;

public class TrackEvent extends JApplet {
HashMap h = new HashMap();
String[] event = {
   "focusGained", "focusLost", "keyPressed",
   "keyReleased", "keyTyped", "mouseClicked",
   "mouseEntered", "mouseExited","mousePressed",
   "mouseReleased", "mouseDragged", "mouseMoved"
};
MyButton
b1 = new MyButton(Color.blue, "test1"),
b2 = new MyButton(Color.red, "test2");
  class MyButton extends JButton {
   void report(String field, String msg) {
((JTextField)h.get(field)).setText(msg);
}    
FocusListener fl = new FocusListener() {
    public void focusGained(FocusEvent e) {
   report("focusGained", e.paramString());
}
    public void focusLost(FocusEvent e) {
   report("focusLost", e.paramString());
}
};
KeyListener kl = new KeyListener() {
    public void keyPressed(KeyEvent e) {
   report("keyPressed", e.paramString());
}
    public void keyReleased(KeyEvent e) {
   report("keyReleased", e.paramString());
}
    public void keyTyped(KeyEvent e) {
   report("keyTyped", e.paramString());
}
};
MouseListener ml = new MouseListener() {
    public void mouseClicked(MouseEvent e) {
   report("mouseClicked", e.paramString());
}
    public void mouseEntered(MouseEvent e) {
   report("mouseEntered", e.paramString());
}
    public void mouseExited(MouseEvent e) {
   report("mouseExited", e.paramString());
}
    public void mousePressed(MouseEvent e) {
   report("mousePressed", e.paramString());
}
    public void mouseReleased(MouseEvent e) {
   report("mouseReleased", e.paramString());
}
};
MouseMotionListener mml =
    new MouseMotionListener() {
    public void mouseDragged(MouseEvent e) {
   report("mouseDragged", e.paramString());
}
    public void mouseMoved(MouseEvent e) {
   report("mouseMoved", e.paramString());
}
};
   public MyButton(Color color, String label) {
    super(label);
setBackground(color);
addFocusListener(fl);
addKeyListener(kl);
addMouseListener(ml);
addMouseMotionListener(mml);
}
}  
  public void init() {
Container c = getContentPane();
c.setLayout(new GridLayout(event.length+1,2));
   for(int i = 0; i JTextField t = new JTextField();
t.setEditable(false);
c.add(new JLabel(event[i], JLabel.RIGHT));
c.add(t);
h.put(event[i], t);
}
c.add(b1);
c.add(b2);
}
  public static void main(String[] args) {
Console.run(new TrackEvent(), 700, 500);
}
} ///:~

Dans le constructeur de MyButton, la couleur des boutons est positionnée par un appel à SetBackground(). Les listeners sont tous installés par de simples appels de méthodes.

La classe TrackEvent contient une HashMap pour contenir les chaînes représentant le type d'événement et les JTextFields dans lesquels l'information sur cet événement est conservée. Bien sûr, ceux-ci auraient pu être créés en statique plutôt qu'en les mettant dans une HashMap, mais je pense que vous serez d'accord que c'est beaucoup plus facile à utiliser et modifier. En particulier, si on a besoin d'ajouter ou supprimer un nouveau type d'événement dans TrackEvent, il suffit d'ajouter ou supprimer une chaîne dans le tableau event, et tout le reste est automatique.

Lorsque report() est appelé on lui donne le nom de l'événement et la chaîne des paramètres de cet événement. Il utilise le HashMap h de la classe externe pour rechercher le JTextField associé à l'événement portant ce nom, et place alors la chaîne des paramètres dans ce champ.

Cet exemple est amusant à utiliser car on peut réellement voir ce qui se passe avec les événements dans son programme.

Un catalogue de composants Swing

Maintenant que nous connaissons les layout managers et le modèle d'événements, nous sommes prêts pour voir comment utiliser les composants Swing. Cette section est une visite non exhaustive des composants Swing et des fonctionnalités que vous utiliserez probablement la plupart du temps. Chaque exemple est conçu pour être de taille raisonnable de manière à pouvoir facilement récupérer le code dans d'autres programmes.

Vos pouvez facilement voir à quoi ressemble chacun de ces exemples en fonctionnement, en visualisant les pages HTML dans le code source téléchargeable de ce chapitre.

Gardez en tête :

  1. La documentation HTML de java.sun.com comprend toutes les classes et méthodes de Swing (seules quelques-unes sont montrées ici),
  2. Grâce aux conventions de nommage utilisées pour les événements Swing, il est facile de deviner comment écrire et installer un gestionnaire d'un événement de type donné. Utilisez le programme de recherche ShowAddListeners.java introduit plus avant dans ce chapitre pour faciliter votre investigation d'un composant particulier.
  3. Lorsque les choses deviendront compliquées, passez à un GUI builder.

Boutons

Swing comprend un certain nombre de boutons de différents types. Tous les boutons, boîtes à cocher [check boxes], boutons radio [radio buttons], et même les éléments de menus [menu items] héritent de AbstractButton(qui, vu qu'ils comprennent les éléments de menus, auraient probablement été mieux nommés AbstractChooser ou quelque chose du genre). Nous verrons l'utilisation des éléments de menus bientôt, mais l'exemple suivant montre les différents types de boutons existants :

//: c13:Buttons.java
// Divers boutons Swing.
// <applet code=Buttons
//  width=350 height=100></applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.plaf.basic.*;
import javax.swing.border.*;
import com.bruceeckel.swing.*;

public class Buttons extends JApplet {
JButton jb = new JButton("JButton");
BasicArrowButton
up = new BasicArrowButton(
BasicArrowButton.NORTH),
down = new BasicArrowButton(
BasicArrowButton.SOUTH),
right = new BasicArrowButton(
BasicArrowButton.EAST),
left = new BasicArrowButton(
BasicArrowButton.WEST);
  public void init() {
Container cp = getContentPane();
cp.setLayout(new FlowLayout());
cp.add(jb);
cp.add(new JToggleButton("JToggleButton"));
cp.add(new JCheckBox("JCheckBox"));
cp.add(new JRadioButton("JRadioButton"));
JPanel jp = new JPanel();
jp.setBorder(new TitledBorder("Directions"));
jp.add(up);
jp.add(down);
jp.add(left);
jp.add(right);
cp.add(jp);
}
  public static void main(String[] args) {
Console.run(new Buttons(), 350, 100);
}
} ///:~

On commence par le BasicArrowButtonde javax.swing.plaf.basic, puis on continue avec les divers types de boutons. Si vous exécutez cet exemple, vous verrez que le toggle button (bouton inverseur) garde sa dernière position, enfoncé ou relâché. Mais les boîtes à cocher et les boutons radio se comportent de manière identique, on les clique pour les (dé)sélectionner (ils sont hérités de JToggleButton).

Groupes de boutons

Si on désire des boutons radio qui se comportent selon un "ou exclusif", il faut les ajouter à un groupe de boutons. Mais, comme l'exemple ci-dessous le montre, tout AbstractButton peut être ajouté à un ButtonGroup.

Pour éviter de répéter beaucoup de code, cet exemple utilise la réflexion pour générer les groupes de différents types de boutons. Ceci peut se voir dans makeBPanel(), qui crée un groupe de boutons et un JPanel. Le second argument de makeBPanel() est un tableau de String. Pour chaque String, un bouton de la classe désignée par le premier argument est ajouté au JPanel :

//: c13:ButtonGroups.java
// Utilise la reflexion pour creer des groupes
// de differents types de AbstractButton.
// <applet code=ButtonGroups
//  width=500 height=300></applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.border.*;
import java.lang.reflect.*;
import com.bruceeckel.swing.*;

public class ButtonGroups extends JApplet {
  static String[] ids = {
   "June", "Ward", "Beaver",
   "Wally", "Eddie", "Lumpy",
};
  static JPanel
makeBPanel(Class bClass, String[] ids) {
ButtonGroup bg = new ButtonGroup();
JPanel jp = new JPanel();
String title = bClass.getName();
title = title.substring(
title.lastIndexOf('.') + 1);
jp.setBorder(new TitledBorder(title));
   for(int i = 0; i AbstractButton ab = new JButton("failed");
    try {
     // Obtient la méthode de construction dynamique
     // qui demande un argument String :
   Constructor ctor = bClass.getConstructor(
      new Class[] { String.class });
     // Creation d'un nouve objet :
   ab = (AbstractButton)ctor.newInstance(
      new Object[]{ids[i]});
} catch(Exception ex) {
   System.err.println("can't create " +
     bClass);
}
bg.add(ab);
jp.add(ab);
}
   return jp;
}
  public void init() {
Container cp = getContentPane();
cp.setLayout(new FlowLayout());
cp.add(makeBPanel(JButton.class, ids));
cp.add(makeBPanel(JToggleButton.class, ids));
cp.add(makeBPanel(JCheckBox.class, ids));
cp.add(makeBPanel(JRadioButton.class, ids));
}
  public static void main(String[] args) {
Console.run(new ButtonGroups(), 500, 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