TP No.3
Swing: l'architecture MVC
Durée: 2 heures


Lors du TP no.1, je vous avais grossièrement introduit ce qu'était l'architecture MVC: Modèle-Vue-Controleur. Nous avons alors utilisé ce modèle de développement pour créer un jeu de Taquin où chacune de ces trois parties était bien distinguée.
Depuis vous avez pu voir, en cours comme en TD, que les composants Swing étaient également basés sur cette architecture. Les composants JTextField et JTextArea par exemple, constituent deux vues différentes pour le modèle Document. Un Document est en quelque sorte un conteneur qui permet de stocker et de manipuler du texte, alors que JTextField et JTextArea ne correspondent réellement qu'à des représentations graphiques différentes d'un même composant. Ils ne permettent de gérer que ce qui a trait au graphique, comme l'affichage et les interactions avec l'utilisateur (saisie au clavier, sélection et déplacement du curseur à la souris, etc.)

Nous allons voir aujourd'hui comment utiliser le modèle de certains des composants Swing.


1. IHM de recherche.

Nous allons développer au cours de ce TP une petite interface de recherche de personnes par nom. Voici un petit apperçu de ce que vous devrez obtenir:

Un champs de texte permet à l'utilisateur de saisir le nom à rechercher et un composant JTable s'occupe d'afficher la liste de personnes.
Cette interface utilisera les classes Personne et EssaisPersonne, qui ne doivent pas vous être totalement inconnues. Les sources de ces deux classes sont disponibles ici. S'y trouve également un jeu d'essais fichierPersonne.txt.

Commencez par déclarer une classe RechercheIHM qui dessine cette interface. Je vous rappelle que le composant JTable ne peut pas manipuler lui même les données de la table. Celà se fait à travers un modèle de table. Vous utiliserez le modèle DefaultTableModel comme nous l'avons vu en TD. De même, n'oubliez pas de placer la JTable dans un JScrollPane. Celà nous permettra d'avoir des barres de défilement lorsque la table deviendra trop grande pour être entièrement affichée dans la fenêtre.

DefaultTableModel possède une méthode permettant d'initialiser le contenu de la table:

    setDataVector(Vector< Vector<Object> > content, Vector<Object> cols_name)
content représente le contenu de la table sous forme de vecteur de lignes, chaque ligne étant elle-même représentée par un vecteur d'objets (des String en général), et où cols_name est un vecteur contenant les noms des différentes colonnes à créer.

Il nous faut donc disposer d'un moyen de créer ces vecteurs à partir d'une liste de Personne. Déclarez et écrivez le corps des deux méthodes suivantes:

    public Vector<String> toLigne(Personne p);
    public Vector< Vector<String> > toDataVector(Vector<Personne> personnes);
toLigne doit créer une ligne pour la table (vecteur de chaîne de caractères) à partir d'une Personne. Cette première méthode sera alors utilisée par toDataVector pour créer un vecteur de lignes à partir d'un vecteur de Personne.

Le DefaultTableModel devra être déclaré comme attribut de la classe RechercheIHM afin de pouvoir modifier plus tard le contenu de la table à la volée. De même, déclarez un attribut

    Vector<Personne> personnes;
dans lequel vous chargerez la liste des personnes grâce à la méthode lirePersonne() de la classe EssaisPersonne.
Une fois cette liste chargée, utilisez les deux méthodes précédemment définies pour initialiser le modèle de table.


2. Saisie contrôlée.

Sur les trois critères disponibles, seul le nom est utilisé pour effectuer la recherche. Comme vous pouvez le voir, tous les noms sont écrits en lettres majuscules. Utilisez votre cours et les connaissances acquises en TD pour définir un document NomDocument, dérivé de la classe PlainDocument, pour que le champs de texte transforme automatiquement les caractères saisis en majuscules.


3. Recherche en temps réel.

Maintenant que l'IHM a été mise en place, il va nous falloir l'activer. Commençons par regarder ce que nous souhaitons obtenir:

A chaque fois qu'un caractère est saisi dans le champs de texte, la table est mise à jour en temps réel pour n'afficher que les personnes dont le nom commence par ce même texte.

D'abord, déclarons la méthode qui va s'occuper de la recherche:

    public void extraire(String recherche_str);
Cette méthode va chercher dans la liste personnes quels sont ceux dont le nom commence par recherche_str et les empiler dans un nouveau vecteur. La méthode toDataVector, que nous avons définie tout à l'heure, sera utilisée pour modifier le contenu du modèle de table à partir de ce nouveau vecteur.

Maintenant que nous avons notre fonction de traîtement, attaquons nous à la gestion des évènements. Pour qu'une action soit engendrée à chaque fois que le contenu du champs de texte est modifié, il faut utiliser un DocumentListener.
Etant donné que c'est le document qui sert de modèle aux composants texte, c'est en effet à lui de manipuler les données et donc de s'occuper de tout ce qui a trait à leur modification. Ainsi, un document génère des évènements de type DocumentEvent à chaque fois que son contenu est modifié.

Définissez une classe RechercheListener qui implante l'interface DocumentListener. Déclarez les attributs et constructeurs appropriés de manière à ce que le listener contienne une copie de l'IHM avec laquelle il devra interagir.
Servez vous de l'API Java pour voir quelles sont les méthodes à redéfinir pour une classe implantant l'interface DocumentListener, et écrivez le corps de ces méthodes de telle manière que l'ajout ou la suppression de texte dans le document engendre une mise à jour de la JTable.

Enfin, modifiez la classe RechercheIHM pour enregistrer une instance de ce listener auprès du document source.
L'IHM est désormais active.


3. Next...

L'IHM de recherche que nous venons de créer utilise certains composants Swing construit sur l'architecture MVC. Nous avons vu comment utiliser les spécificités de cette architecture pour différents types de manipulation. Ceci a pu vous donner une vague idée de la logique qui se trouve derrière cette philosophie de programmation.

Dans les prochains TPs, nous verrons comment gérer les entrées / sorties de manière à ouvrir ou enregister des fichiers, ou encore comment utiliser JDBC pour permettre à l'application de communiquer avec des bases de données SQL.

A bientôt pour de nouvelles aventures.

"May the Force be with you..."