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 

//: c13:Box3.java
// Utilisation de Glue.
// <applet code=Box3
// width=450 height=300> </applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;

public class Box3 extends JApplet {
  public void init() {
  Box bv = Box.createVerticalBox();
  bv.add(new JLabel("Hello"));
  bv.add(Box.createVerticalGlue());
  bv.add(new JLabel("Applet"));
  bv.add(Box.createVerticalGlue());
  bv.add(new JLabel("World"));
  Box bh = Box.createHorizontalBox();
  bh.add(new JLabel("Hello"));
  bh.add(Box.createHorizontalGlue());
  bh.add(new JLabel("Applet"));
  bh.add(Box.createHorizontalGlue());
  bh.add(new JLabel("World"));
  bv.add(Box.createVerticalGlue());
  bv.add(bh);
  bv.add(Box.createVerticalGlue());
  getContentPane().add(bv);
}
  public static void main(String[] args) {
  Console.run(new Box3(), 450, 300);
}
} ///:~

Un strut fonctionne dans une direction, mais une rigid area (surface rigide) fixe l'espace entre les composants dans chaque direction :

//: c13:Box4.java
// Des Rigid Areas sont comme des paires de struts.
// <applet code=Box4
// width=450 height=300> </applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;

public class Box4 extends JApplet {
  public void init() {
  Box bv = Box.createVerticalBox();
  bv.add(new JButton("Top"));
  bv.add(Box.createRigidArea(
    new Dimension(120, 90)));
  bv.add(new JButton("Bottom"));
  Box bh = Box.createHorizontalBox();
  bh.add(new JButton("Left"));
  bh.add(Box.createRigidArea(
    new Dimension(160, 80)));
  bh.add(new JButton("Right"));
  bv.add(bh);
  getContentPane().add(bv);
}
  public static void main(String[] args) {
  Console.run(new Box4(), 450, 300);
}
} ///:~

Il faut savoir que les rigid areas sont un peu controversées. Comme elles utilisent des valeurs absolues, certaines personnes pensent qu'elles causent plus de problèmes qu'elles n'en résolvent.

La meilleure approche ?

Swing est puissant; il peut faire beaucoup de choses en quelques lignes de code. Les exemples de ce livre sont raisonnablement simples, et dans un but d'apprentissage il est normal de les écrire à la main. On peut en fait réaliser pas mal de choses en combinant des layouts simples. A un moment donné, il devient cependant déraisonnable de coder à la main des formulaires de GUI. Cela devient trop compliqué et ce n'est pas une bonne manière d'utiliser son temps de programmation. Les concepteur de Java et de Swing ont orienté le langage et ses bibliothèques de manière à soutenir des outils de construction de GUI, qui ont été créés dans le but de rendre la tâche de programmation plus facile. A partir du moment où on comprend ce qui se passe dans les layouts et comment traiter les événements (décrits plus loin), il n'est pas particulièrement important de connaître effectivement tous les détails sur la façon de positionner les composants à la main. Laissons les outils appropriés le faire pour nous (Java est après tout conçu pour augmenter la productivité du programmeur).

Le modèle d'événements de Swing

Dans le modèle d'événements de Swing, un composant peut initier (envoyer [fire]) un événement. Chaque type d'événement est représenté par une classe distincte. Lorsqu'un événement est envoyé, il est reçu par un ou plusieurs écouteurs [listeners], qui réagissent à cet événement. De ce fait, la source d'un événement et l'endroit où cet événement est traité peuvent être séparés. Puisqu'on utilise en général les composants Swing tels quels, mais qu'il faut écrire du code appelé lorsque les composants reçoivent un événement, ceci est un excellent exemple de la séparation de l'interface et de l'implémentation.

Chaque écouteur d'événements [event listener] est un objet d'une classe qui implémente une interface particulière de type listener. En tant que programmeur, il faut créer un objet listener et l'enregistrer avec le composant qui envoie l'événement. Cet enregistrement se fait par appel à une méthode addXXXListener() du composant envoyant l'événement, dans lequel XXX représente le type d'événement qu'on écoute.On peut facilement savoir quels types d'événements peuvent être gérés en notant les noms des méthodes addListener, et si on essaie d'écouter des événements erronés, l'erreur sera signalée à la compilation. On verra plus loin dans ce chapitre que les JavaBeans utilisent aussi les noms des méthodes addListener pour déterminer quels événements un Bean peut gérer.

Toute notre logique des événements va se trouver dans une classe listener. Lorsqu'on crée une classe listener, la seule restriction est qu'elle doit implémenter l'interface appropriée. On peut créer une classe listener globale, mais on est ici dans un cas où les classes internes sont assez utiles, non seulement parce qu'elles fournissent un groupement logique de nos classes listener à l'intérieur des classes d'interface utilisateur ou de logique métier qu'elles desservent, mais aussi (comme on le verra plus tard) parce que le fait qu'un objet d'une classe interne garde une référence à son objet parent fournit une façon élégante d'appeler à travers les frontières des classes et des sous-systèmes.

Jusqu'ici, tous les exemples de ce chapitre ont utilisé le modèle d'événements Swing, mais le reste de cette section va préciser les détails de ce modèle.

Evénements et types de listeners

Chaque composant Swing contient des méthodes addXXXListener() et removeXXXListener() de manière à ce que les types de listeners adéquats puissent être ajoutés et enlevés de chaque composant. On remarquera que le XXX dans chaque cas représente également l'argument de cette méthode, par exemple : addMyListener(MyListener m). Le tableau suivant contient les événements, listeners et méthodes de base associées aux composants de base qui supportent ces événements particuliers en fournissant les méthodes addXXXListener() et removeXXXListener(). Il faut garder en tête que le modèle d'événements est destiné à être extensible, et donc on pourra rencontrer d'autres types d'événements et de listeners non couverts par ce tableau.

Evénement, interface listener et méthodes add et remove Composants supportant cet événement
ActionEvent ActionListener addActionListener() removeActionListener() JButton, JList, JTextField, JMenuItem et ses dérivés, comprenant JCheckBoxMenuItem, JMenu, et JpopupMenu.
AdjustmentEvent AdjustmentListener addAdjustmentListener() removeAdjustmentListener() JScrollbar et tout ce qu'on crée qui implémente l'interface Adjustable.
ComponentEvent ComponentListener addComponentListener() removeComponentListener() *Component et ses dérivés, comprenant JButton, JCanvas, JCheckBox, JComboBox, Container, JPanel, JApplet, JScrollPane, Window, JDialog, JFileDialog, JFrame, JLabel, JList, JScrollbar, JTextArea, et JTextField.
ContainerEvent ContainerListener addContainerListener() removeContainerListener() Container et ses dérivés, comprenant JPanel, JApplet, JScrollPane, Window, JDialog, JFileDialog, et JFrame.
FocusEvent FocusListener addFocusListener() removeFocusListener() Component et dérivés*.
KeyEvent KeyListener addKeyListener() removeKeyListener() Component et dérivés*.
MouseEvent (à la fois pour les clics et pour le déplacement) MouseListener addMouseListener() removeMouseListener() Component et dérivés*.
MouseEvent [68](à la fois pour les clics et pour le déplacement) MouseMotionListener addMouseMotionListener() removeMouseMotionListener() Component et dérivés*.
WindowEvent WindowListener addWindowListener() removeWindowListener() Window et ses dérivés, comprenant JDialog, JFileDialog, and JFrame.
ItemEvent ItemListener addItemListener() removeItemListener() JCheckBox, JCheckBoxMenuItem, JComboBox, JList, et tout ce qui implémente l'interface ItemSelectable.
TextEvent TextListener addTextListener() removeTextListener() Tout ce qui est dérivé de JTextComponent, comprenant JTextArea et JTextField.

On voit que chaque type de composant ne supporte que certains types d'événements. Il semble assez difficile de rechercher tous les événements supportés par chaque composant. Une approche plus simple consiste à modifier le programme ShowMethodsClean.java du chapitre 12 de manière à ce qu'il affiche tous les event listeners supportés par tout composant Swing entré.

Le chapitre 12 a introduit la réflexion et a utilisé cette fonctionnalité pour rechercher les méthodes d'une classe donnée, soit une liste complète des méthodes, soit un sous-ensemble des méthodes dont le nom contient un mot-clé donné. La magie dans ceci est qu'il peut automatiquement nous montrer toutes les méthodes d'une classe sans qu'on soit obligé de parcourir la hiérarchie des héritages en examinant les classes de base à chaque niveau. De ce fait, il fournit un outil précieux permettant de gagner du temps pour la programmation : comme les noms de la plupart des méthodes Java sont parlants et descriptifs, on peut rechercher les noms de méthodes contenant un mot particulier. Lorsqu'on pense avoir trouvé ce qu'on cherchait, il faut alors vérifier la documentation en ligne.

Comme dans le chapitre 12 on n'avait pas encore vu Swing, l'outil de ce chapitre était une application de ligne de commande. En voici une version plus pratique avec interface graphique, spécialisée dans la recherche des méthodes addListener dans les composants Swing :

//: c13:ShowAddListeners.java
// Affiche les methodes "addXXXListener"
// d'une classe Swing donnee.
// <applet code = ShowAddListeners
// width=500 height=400></applet>
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.lang.reflect.*;
import java.io.*;
import com.bruceeckel.swing.*;
import com.bruceeckel.util.*;

public class ShowAddListeners extends JApplet {
  Class cl;
  Method[] m;
  Constructor[] ctor;
  String[] n = new String[0];
  JTextField name = new JTextField(25);
  JTextArea results = new JTextArea(40, 65);
  class NameL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      String nm = name.getText().trim();
      if(nm.length() == 0) {
        results.setText("No match");
        n = new String[0];
        return;
      }
      try {
        cl = Class.forName("javax.swing." + nm);
      } catch(ClassNotFoundException ex) {
        results.setText("No match");
        return;
      }
      m = cl.getMethods();
      // Conversion en un tableau de Strings :
      n = new String[m.length];
      for(int i = 0; i         n[i] = m[i].toString();
      reDisplay();
    }
  }
  void reDisplay() {
    // Creation de l'ensemble des resultats :
    String[] rs = new String[n.length];
    int j = 0;
    for (int i = 0; i       if(n[i].indexOf("add") != -1 &&
        n[i].indexOf("Listener") != -1)
          rs[j++] =
            n[i].substring(n[i].indexOf("add"));
    results.setText("");
    for (int i = 0; i       results.append(
        StripQualifiers.strip(rs[i]) + "\n");
  }
  public void init() {
    name.addActionListener(new NameL());
    JPanel top = new JPanel();
    top.add(new JLabel(
      "Swing class name (press ENTER):"));
    top.add(name);
    Container cp = getContentPane();
    cp.add(BorderLayout.NORTH, top);
    cp.add(new JScrollPane(results));
  }
  public static void main(String[] args) {
    Console.run(new ShowAddListeners(), 500,400);
  }
} ///:~

La classe StripQualifiers définie au chapitre 12 est réutilisée ici en important la bibliothèque com.bruceeckel.util.

L'interface utilisateur graphique contient un JTextField name dans lequel on saisit le nom de la classe Swing à rechercher. Les résultats sont affichés dans une JTextArea.

On remarquera qu'il n'y a pas de boutons ou autres composants pour indiquer qu'on désire lancer la recherche. C'est parce que le JTextField est surveillé par un ActionListener. Lorsqu'on y fait un changement suivi de ENTER, la liste est immédiatement mise à jour. Si le texte n'est pas vide, il est utilisé dans Class.forName() pour rechercher la classe. Si le nom est incorrect, Class.forName() va échouer, c'est à dire qu'il va émettre une exception. Celle-ci est interceptée et le JTextArea est positionné à "No match". Mais si on tape un nom correct (les majuscules/minuscules comptent), Class.forName() réussit et getMethods() retourne un tableau d'objets Method. Chacun des objets du tableau est transformé en String à l'aide de toString() (cette méthode fournit la signature complète de la méthode) et ajoutée à n, un tableau de Strings. Le tableau n est un membre de la classe ShowAddListeners et est utilisé pour mettre à jour l'affichage chaque fois que reDisplay() est appelé.

reDisplay() crée un tableau de Strings appelé rs (pour "result set" : ensemble de résultats). L'ensemble des résultats est conditionnellement copié depuis les Strings de n qui contiennent add et Listener. indexOf() et substring() sont ensuite utilisés pour enlever les qualificatifs tels que public, static, etc. Enfin, StripQualifiers.strip() enlève les qualificatifs de noms.

Ce programme est une façon pratique de rechercher les capacités d'un composant Swing. Une fois connus les événements supportés par un composant donné, il n'y a pas besoin de rechercher autre chose pour réagir à cet événement. Il suffit de :

  1. Prendre le nom de la classe événement et retirer le mot Event. Ajouter le mot Listener à ce qui reste. Ceci donne le nom de l'interface listener qu'on doit implémenter dans une classe interne.
  2. Implémenter l'interface ci-dessus et écrire les méthodes pour les événements qu'on veut intercepter. Par exemple, on peut rechercher les événements de déplacement de la souris, et on écrit donc le code pour la méthode mouseMoved() de l'interface MouseMotionListener (il faut également implémenter les autres méthodes, bien sûr, mais il y a souvent un raccourci que nous verrons bientôt).
  3. Créer un objet de la classe listener de l'étape 2. L'enregistrer avec le composant avec la méthode dont le nom est fourni en ajoutant add au début du nom du listener. Par exemple, addMouseMotionListener().

Voici quelques-unes des interfaces listeners :

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