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 

La liste des méthodes public contient les méthodes qui ne sont pas associées à une propriété ou un événement, telles que croak(), ainsi que celles qui le sont. Ce sont toutes les méthodes pouvant être appelées par programme pour un Bean, et l'outil de construction d'applications peut décider de les lister toutes lors de la création des appels de méthodes, pour nous faciliter la tâche.

Enfin, on peut voir que les événements sont tous triés entre le listener, ses méthodes et les méthodes pour ajouter et supprimer les listeners. Fondamentalement, une fois obtenu le BeanInfo, on peut trouver tout ce qui est important pour un Bean. On peut également appeler les méthodes de ce Bean, bien qu'on n'ait aucune autre information à l'exception de l'objet (ceci est également une caractéristique de la réflexion).

Un Bean plus complexe

L'exemple suivant est un peu plus compliqué, bien que futile. Il s'agit d'un JPanel qui dessine un petit cercle autour de la souris chaque fois qu'elle se déplace. Lorsqu'on clique, le mot Bang! apparaît au milieu de l'écran, et un action listener est appelé.

Les propriétés modifiables sont la taille du cercle, ainsi que la couleur, la taille et le texte du mot affiché lors du clic. Un BangBean a également ses propres addActionListener() et removeActionListener(), de sorte qu'on peut y attacher son propre listener qui sera appelé lorsque l'utilisateur clique sur le BangBean. Vous devriez être capables de reconnaître le support des propriétés et événements :

//: bangbean:BangBean.java
// Un Bean graphique.
package bangbean;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import com.bruceeckel.swing.*;

public class BangBean extends JPanel
     implements Serializable {
  protected int xm, ym;
  protected int cSize = 20; // Taille du cercle
  protected String text = "Bang!";
  protected int fontSize = 48;
  protected Color tColor = Color.red;
  protected ActionListener actionListener;
  public BangBean() {
    addMouseListener(new ML());
    addMouseMotionListener(new MML());
  }
  public int getCircleSize() { return cSize; }
  public void setCircleSize(int newSize) {
    cSize = newSize;
  }
  public String getBangText() { return text; }
  public void setBangText(String newText) {
    text = newText;
  }
  public int getFontSize() { return fontSize; }
  public void setFontSize(int newSize) {
    fontSize = newSize;
  }
  public Color getTextColor() { return tColor; }
  public void setTextColor(Color newColor) {
    tColor = newColor;
  }
  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.setColor(Color.black);
    g.drawOval(xm - cSize/2, ym - cSize/2,
      cSize, cSize);
  }
  // Ceci est un "unicast listener", qui est
  // la forme la plus simple de gestion des listeners :
  public void addActionListener (
      ActionListener l)
        throws TooManyListenersException {
    if(actionListener != null)
      throw new TooManyListenersException();
    actionListener = l;
  }
  public void removeActionListener(
      ActionListener l) {
    actionListener = null;
  }
  class ML extends MouseAdapter {
    public void mousePressed(MouseEvent e) {
      Graphics g = getGraphics();
      g.setColor(tColor);
      g.setFont(
        new Font(
          "TimesRoman", Font.BOLD, fontSize));
      int width =
        g.getFontMetrics().stringWidth(text);
      g.drawString(text,
        (getSize().width - width) /2,
        getSize().height/2);
      g.dispose();
      // Appel de la méthode du listener :
      if(actionListener != null)
        actionListener.actionPerformed(
          new ActionEvent(BangBean.this,
            ActionEvent.ACTION_PERFORMED, null));
    }
  }
  class MML extends MouseMotionAdapter {
    public void mouseMoved(MouseEvent e) {
      xm = e.getX();
      ym = e.getY();
      repaint();
    }
  }
  public Dimension getPreferredSize() {
    return new Dimension(200, 200);
  }
} ///:~

La première chose qu'on remarquera est que BangBean implémente l'interface Serializable. Ceci signifie que l'outil de construction d'applications peut conserver toutes les informations sur le BangBean en utilisant la sérialisation, lorsque les valeurs des propriétés ont été ajustées par l'utilisateur. Lors de la création du Bean au moment de l'exécution du programme, ces propriétés sauvegardées sont restaurées de manière à obtenir exactement ce qu'elles valaient lors de la conception.

On peut voir que tous les champs sont private, ce qu'on fait en général avec un Bean : autoriser l'accès uniquement à travers des méthodes, normalement en utilisant le système de propriétés.

En observant la signature de addActionListener(), on voit qu'il peut émettre une TooManyListenersException. Ceci indique qu'il est unicast, ce qui signifie qu'il signale à un seul listener l'arrivée d'un événement. En général on utilise des événements multicast, de sorte que de nombreux listeners puissent être notifiés de l'arrivée d'un événement. Cependant on entre ici dans des problèmes que vous ne pouvez pas comprendre avant le chapitre suivant; on en reparlera donc (sous le titre «JavaBeans revisited»). Un événement unicast contourne le problème.

Lorsqu'on clique avec la souris, le texte est placé au milieu du BangBean, et si le champ de l'actionListener n'est pas nul, on appelle son actionPerformed(), ce qui crée un nouvel objet ActionEvent. Lorsque la souris est déplacée, ses nouvelles coordonnées sont lues et le panneau est redessiné (ce qui efface tout texte sur ce panneau, comme on le remarquera).

Voici la classe BangBeanTest permettant de tester le bean en tant qu'applet ou en tant qu'application :

//: c13:BangBeanTest.java
// <applet code=BangBeanTest
// width=400 height=500></applet>
import bangbean.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import com.bruceeckel.swing.*;

public class BangBeanTest extends JApplet {
  JTextField txt = new JTextField(20);
  // Affiche les actions lors des tests :
  class BBL implements ActionListener {
    int count = 0;
    public void actionPerformed(ActionEvent e){
      txt.setText("BangBean action "+ count++);
    }
  }
  public void init() {
    BangBean bb = new BangBean();
    try {
      bb.addActionListener(new BBL());
    } catch(TooManyListenersException e) {
      txt.setText("Too many listeners");
    }
    Container cp = getContentPane();
    cp.add(bb);
    cp.add(BorderLayout.SOUTH, txt);
  }
  public static void main(String[] args) {
    Console.run(new BangBeanTest(), 400, 500);
  }
} ///:~

Lorsqu'un Bean est dans un environnement de développement, cette classe n'est pas utilisée, mais elle est utile pour fournir une méthode de test rapide pour chacun de nos Beans. BangBeanTest place un BangBean dans une applet, attache un simple ActionListener au BangBean pour afficher un compteur d'événements dans le JTextField chaque fois qu'un ActionEvent arrive. En temps normal, bien sûr, l'outil de construction d'applications créerait la plupart du code d'utilisation du Bean.

Lorsqu'on utilise le BangBean avec le BeanDumper, ou si on place le BangBean dans un environnement de développement acceptant les Beans, on remarquera qu'il y a beaucoup plus de propriétés et d'actions que ce qui n'apparaît dans le code ci-dessus. Ceci est dû au fait que BangBean hérite de JPanel, et comme JPanel est également un Bean, on en voit également ses propriétés et événements.

Empaquetage d'un Bean

Pour installer un Bean dans un outil de développement visuel acceptant les Beans, le Bean doit être mis dans le conteneur standard des Beans, qui est un fichier JAR contenant toutes les classes, ainsi qu'un fichier manifest qui dit «ceci est un Bean». Un fichier manifest est un simple fichier texte avec un format particulier. Pour le BangBean, le fichier manifest ressemble à ceci (sans la première et la dernière ligne) :

//:! :BangBean.mf
Manifest-Version: 1.0

Name: bangbean/BangBean.class
Java-Bean: True
///:~

La première ligne indique la version de la structure du fichier manifest, qui est 1.0 jusqu'à nouvel ordre de chez Sun. la deuxième ligne (les lignes vides étant ignorées) nomme le fichier BangBean.class, et la troisième précise que c'est un Bean. Sans la troisième ligne, l'outil de construction de programmes ne reconnaîtra pas la classe en tant que Bean.

Le seul point délicat est de s'assurer d'avoir le bon chemin dans le champ «Name:». Si on retourne à BangBean.java, on voit qu'il est dans le package bangbean (et donc dans un sous-répertoire appelé bangbean qui est en dehors du classpath), et le nom dans le fichier manifest doit contenir cette information de package. De plus, il faut placer le fichier manifest dans le répertoire au-dessus de la racine du package, ce qui dans notre cas veut dire le placer dans le répertoire au-dessus du répertoire bangbean. Ensuite il faut lancer jar depuis le répertoire où se trouve le fichier manifest, de la façon suivante :

jar cfm BangBean.jar BangBean.mf bangbean

Ceci suppose qu'on veut que le fichier JAR résultant s'appelle BangBean.jar, et qu'on a placé le fichier manifest dans un fichier appelé BangBean.mf.

On peut se demander ce qui se passe pour les autres classes générées lorsqu'on a compilé BangBean.java. Eh bien, elles ont toutes abouti dans le sous-répertoire bangbean. Lorsqu'on donne à la commande jar le nom d'un sous-répertoire, il embarque tout le contenu de ce sous-répertoire dans le fichier jar (y compris, dans notre cas, le code source original BangBean.java qu'on peut ne pas vouloir inclure dans les Beans). Si on regarde à l'intérieur du fichier JAR, on découvre que le fichier manifest n'y est pas, mais que jar a créée son propre fichier manifest (partiellement sur la base du nôtre) appelé MANIFEST.MF et l'a placé dans le sous-répertoire META-INF (pour meta-information). Si on ouvre ce fichier manifest on remarquera également qu'une information de signature numérique a été ajoutée par jar pour chaque fichier, de la forme :

Digest-Algorithms: SHA MD5
SHA-Digest: pDpEAG9NaeCx8aFtqPI4udSX/O0=MD5-Digest: O4NcS1hE3Smnzlp2hj6qeg==

En général, on ne se préoccupe pas de tout ceci, et lors de modifications il suffit de modifier le fichier manifest d'origine et rappeler jar pour créer un nouveau fichier JAR pour le Bean. On peut aussi ajouter d'autres Beans dans le fichier JAR en ajoutant simplement leurs informations dans le manifest.

Une chose à remarquer est qu'on mettra probablement chaque Bean dans son propre sous-répertoire, puisque lorsqu'on crée un fichier JAR on passe à la commande jar le nom d'un sous-répertoire et qu'il place tout ce qui est dans ce répertoire dans le fichier JAR. On peut voir que Frog et BangBean sont chacun dans leur propre sous-répertoire.

Une fois le Bean convenablement inséré dans un fichier JAR, on peut l'installer dans un environnement de développement de programmes acceptant les Beans. La façon de le faire varie d'un outil à l'autre, mais Sun fournit gratuitement un banc de test pour les JavaBeans dans leur Beans Development Kit (BDK) appelé la beanbox (le BDK se télécharge à partir de java.sun.com/beans). Pour placer un Bean dans la beanbox, il suffit de copier le fichier JAR dans le sous-répertoire jars du BDK avant de lancer la beanbox.

Un support des Beans plus sophistiqué

On a vu comme il était simple de fabriquer un Bean. Mais on n'est pas limité à ce qu'on a vu ici. L'architecture des JavaBeans fournit un point d'entrée simple, mais peut aussi s'adapter à des cas plus complexes. Ceux-ci ne sont pas du ressort de ce livre, mais on va les introduire rapidement ici. On trouvera plus de détails à java.sun.com/beans.

Un endroit où l'on peut apporter des perfectionnements est le traitement des propriétés. Les exemples ci-dessus n'ont montré que des propriétés uniques, mais il est également possible de représenter plusieurs propriétés dans un tableau. C'est ce qu'on appelle une propriété indexée [indexed property]. Il suffit de fournir les méthodes appropriées (également en suivant une convention de nommage pour les noms de méthodes) et l'Introspector reconnaît une propriété indexée, de sorte qu'un outil de construction d'applications puisse y répondre correctement.

Les propriétés peuvent être liées [bound], ce qui signifie qu'elles avertiront les autres objets à l'aide d'un PropertyChangeEvent. Les autres objets peuvent alors décider de se modifier eux-mêmes suite à la modification de ce Bean.

Les propriétés peuvent être contraintes [constrained], ce qui signifie que les autres objets peuvent mettre leur veto sur la modification de cette propriété si c'est inacceptable. Les autres objets sont avertis à l'aide d'un PropertyChangeEvent, et ils peuvent émettre un PropertyVetoException pour empêcher la modification et pour rétablir les anciennes valeurs.

On peut également modifier la façon de représenter le Bean lors de la conception :

  1. On peut fournir une feuille de propriétés spécifique pour un Bean particulier. La feuille de propriétés normale sera utilisée pour tous les autres Beans, mais la feuille spéciale sera automatiquement appelée lorsque ce Bean sera sélectionné.
  2. On peut créer un éditeur spécifique pour une propriété particulière, de sorte que la feuille de propriétés normale est utilisée, mais si on veut éditer cette propriété, c'est cet éditeur qui est automatiquement appelé.
  3. On peut fournir une classe BeanInfo spécifique pour un Bean donné, pour fournir des informations différentes de celles créées par défaut par l'Introspector.
  4. Il est également possible de valider ou dévalider le mode expert dans tous les FeatureDescriptors pour séparer les caractéristiques de base de celles plus compliquées.

Davantage sur les Beans

Il y a un autre problème qui n'a pas été traité ici. Chaque fois qu'on crée un Bean, on doit s'attendre à ce qu'il puisse être exécuté dans un environnement multithread. Ceci signifie qu'il faut comprendre les problèmes du threading, qui sera présenté au Chapitre 14. On y trouvera un paragraphe appelé «JavaBeans revisited» qui parlera de ce problème et de sa solution.

Il y a plusieurs livres sur les JavaBeans, par exemple JavaBeans par Elliotte Rusty Harold (IDG, 1998).

Résumé

De toutes les librairies Java, c'est la librairie de GUI qui a subi les changements les plus importants de Java 1.0 à Java 2. L'AWT de Java 1.0 était nettement critiqué comme étant une des moins bonnes conceptions jamais vues, et bien qu'il permette de créer des programmes portables, la GUI résultante était aussi médiocre sur toutes les plateformes. Il était également limité, malaisé et peu agréable à utiliser en comparaison des outils de développement natifs disponibles sur une plate-forme donnée.

Lorsque Java 1.1 introduisit le nouveau modèle d'événements et les JavaBeans, la scène était installée. Il était désormais possible de créer des composants de GUI pouvant être facilement glissés et déposés à l'intérieur d'outils de développement visuels. De plus, la conception du modèle d'événements et des Beans montre l'accent mis sur la facilité de programmation et la maintenabilité du code (ce qui n'était pas évident avec l'AWT du 1.0). Mais le travail ne s'est terminé qu'avec l'apparition des classes JFC/Swing. Avec les composants Swing, la programmation de GUI toutes plateformes devient une expérience civilisée.

En fait, la seule chose qui manque est l'outil de développement, et c'est là que se trouve la révolution. Visual Basic et Visual C++ de Microsoft nécessitent des outils de développement de Microsoft, et il en est de même pour Delphi et C++ Builder de Borland. Si on désire une amélioration de l'outil, on n'a plus qu'à croiser les doigts et espérer que le fournisseur le fera. Mais java est un environnement ouvert, et de ce fait, non seulement il permet la compétition des outils, mais il l'encourage. Et pour que ces outils soient pris au sérieux, ils doivent permettre l'utilisation des JavaBeans. Ceci signifie un terrain de jeu nivelé : si un meilleur outil de développement apparaît, on n'est pas lié à celui qu'on utilisait jusqu'alors, on peut migrer vers le nouvel outil et augmenter sa productivité. Cet environnement compétitif pour les outils de développement ne s'était jamais vu auparavant, et le marché résultant ne peut que générer des résultats positifs pour la productivité du programmeur.

Ce chapitre était uniquement destiné à vous fournir une introduction à la puissance de Swing et vous faire démarrer, et vous avez pu voir comme il était simple de trouver son chemin à travers les librairies. Ce qu'on a vu jusqu'ici suffira probablement pour une bonne part à vos besoins en développement d'interfaces utilisateurs. Cependant, Swing ne se limite pas qu'à cela. Il est destiné à être une boîte à outils de conception d'interfaces utilisateurs très puissante. Il y a probablement une façon de faire à peu près tout ce qu'on peut imaginer.

Si vous ne trouvez pas ici ce dont vous avez besoin, fouillez dans la documentation en ligne de Sun, et recherchez sur le Web, et si cela ne suffit pas, cherchez un livre consacré à Swing. Un bon endroit pour démarrer est The JFC Swing Tutorial, par Walrath & Campione (Addison Wesley, 1999).

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