Travaux Pratiques Java/Swing

Eric Lecolinet - Télécom ParisTech - Dept. INFRES

Liens et rappels

Compilation

Exemple
          javac MonFichier.java
produit:
          MonFichier.class

Remarques

Exécution

Les fichiers .class sont des bytecodes qui ne sont exécutables que par la machine virtuelle Java (ils ne sont pas directement exécutables sous Unix). Il faut donc explicitement utiliser l'interpréteur java de la manière suivante:

    java  MonFichier   (sans l'extension .class)

Pour trouver les erreurs, on peut utiliser jdb, le debogueur Java. Des IDEs comme Eclipse faciliteront grandement l'édition et la mise au point du code.

Autres remarques


Exemples et exercices


Créer un nouveau répertoire et y copier les fichiers sources se trouvant dans ce fichier zip (NOTE: attention à ne pas écraser vos propres fichiers source si vous rechargez ce fichier zip par la suite !)

Bases de l'interaction: Boutons et Listeners

Le programme BipBip.java montre une interface graphique minimale (un bouton qui emet un "bip" et affiche un message quand on clique dessus). Exécutez ce programme (voir "Mode d'Emploi" ci-dessus pour la compilation et l'exécution), puis regardez le code source et les commentaires qui suivent.

La classe BibBip

Le fichier BipBip.java définit une classe unique BipBip qui a le même nom que le fichier à l'extension près. Cette classe doit obligatoirement être publique (voir "Quelques remarques sur les programmes Java").

BipBip est une sous-classe de JFrame, la classe Java Swing qui sert à contruire la fenêtre principale d'une application. Les objects graphiques doivent être ajoutés au JFrame (ou plus exactement à son contentPane: noter qu'avec les versions anciennes de Swing il faut le faire explicitement via la fonction getContentPane())

BipBip implémente l'interface ActionListener. C'est ce qui permettra de gérer le comportement du bouton comme on le verra ci-après (noter que le terme interface fait ici référence aux interfaces de programmation, pas aux interfaces graphiques !)

La classe BipBip définit trois méthodes détaillées ci-après. On remarquera en passant que les méthodes peuvent être définies (et appelées) dans n'importe quel ordre en Java (contrairement au langage C où il faut théoriquement déclarer les fonctions (i.e. spécifier leur signature) avant de les appeler).

La méthode main()

Tout programme Java doit obligatoirement définir une méthode main() qui sert de point d'entrée à l'éxecution du programme (à la différence des Applets qui doivent définir la méthode init()). Cette méthode doit être public static (static signifie que c'est une méthode de classe et non d'instance).

main() instancie la classe "BibBip", ce qui a pour effet de créer l'interface graphique. Cette instance est le composant graphique de plus haut niveau de l'application et "contient" donc les autres composants graphiques. En d'autres termes, cette instance est la racine de l'arbre d'instanciation de la GUI.

Il faut enfin calculer la disposition spatiale des de composants graphiques puis les rendre visible. Ceci ce fait en appelant les méthodes pack() et show() sur l'objet de plus haut niveau.

La méthode BibBip()

C'est le constructeur de la classe BibBip. Cette méthode:

Remarque:

Quasiment tous les composants graphiques Java doivent avoir un parent (à part JFrame, etc.) pour apparaître et fonctionner correctement. Ceci est généralement fait via la méthode add() du parent. N'oubliez donc pas cette étape (vous pourriez obtenir des résultats etranges ou des NullPointerException dans le cas contraire).

La méthode actionPerformed()

Cette méthode est appelée chaque fois que l'on clique sur le bouton. Elle est héritée de l'interface ActionListener. que la classe BibBip "implémente". Le code montre comment afficher un message sur le Terminal (l'équivalent du printf() du C) et émettre un bip.


Gérer plusieurs widgets (comportement et présentation)

L'exemple LesBoutons.java montre une manière élémentaire de disposer plusieurs widgets dans un Container et de gérer leur comportement. On remarquera que:

Exercice

Associer les comportements suivants aux 3 boutons :

Indications: consulter les méthodes des classes en question dans le manuel de référence. Pensez à utiliser la fonctionnalité "Find in Page" (ou similaire) de Netscape, Mozilla ou IExplorer pour retrouver les méthodes dans les pages décrivant les classes Java).



BorderLayout et composants élémentaires

L'exemple LesComponents.java introduit quelques-uns des composants Swing les plus courants et montre l'utilisation du LayoutManager BorderLayout.

Cette GUI comprend 2 conteneurs intermédiaires (de classe JPanel). Ces conteneurs sont internes et peuvent être emboîtés entre-eux (il ne créent pas de nouvelle fenêtre contrairement aux JFrames, et aux JDialogs que nous verrons ensuite). Leur Layout Manager par défaut est FlowLayout (enfants mis à la suite, passage à la ligne quand il n'y a plus de place).

Cet exemple introduit un nouveau Layout Manager nommé BorderLayout. Celui-ci impose une disposition spatiale de type points cardinaux. Les enfants sont ajoutés au conteneur associé au moyen d'une forme particulière de la méthode add() qui permet de spécifier leur emplacement. Par exemple:

         add(BorderLayout.NORTH, panel1);

Il est à noter que BorderLayout ne retaille pas tous les enfants de la même manière quand on retaille la fenêtre. Faites un essai : que constate t-on ?

Remarque: le LayoutManager par défaut du contentPane est déjà BorderLayout. La ligne: contentPane.setLayout(new BorderLayout()) est donc optionnelle dans ce cas.

Cet exemple introduit également les objets JTextField (une ligne de texte), JTextArea (une zone de texte multi-lignes) et JCheckBox (un bouton à états ; attention au "B" majuscule !)

Les trois check boxes crées dans cet exemple sont stockés dans un tableau qui est lui-même créé au début du programme:

         JCheckBox[] checkboxes = new JCheckBox[3];

Il est important de noter que cette ligne initialise le tableau mais pas les éléments contenus !. En d'autres termes, on crée un tableau de 3 éléments dont chaque élément "pointe" sur null (c'est-à-dire sur aucun objet). Chaque élément du tableau est initialisé ultérieurement lorsque l'on crée les check boxes :

         checkboxes[0] = new JCheckBox("Oui", true);
         ...etc...

Remarque: la définition:

         Checkbox[] checkboxes;

ne serait pas valide en C ou en C++. Pour les nostaliques, Java permet également d'écrire:

         Checkbox checkboxes[]; 

Le programme définit enfin la méthode actionPerformed qui est appelée quand on clique sur le bouton Ok ou quand on frappe la touche ENTER sur le text field (on remarquera que c'est la même méthode du même listener qui est appelés dans les 2 cas bien que les classes soient différentes).

Exécuter ces actions et regarder le code source. Regarder également les messages affichés sur le Terminal (et en particulier la valeur de l'actionCommand).

Exercice

Compléter la méthode actionPerformed de telle sorte que l'état de chacun des trois check boxes (sélectionné / non sélectionné) s'affiche sur le JTextArea chaque fois que l'on active le JTextField ou le Button.

Remarque: on pourra faire cet exercice en récupérant l'état courant des 3 check boxes (voir la doc. et méthodes isSelected(), getSelected()). Il ne sera pas nécessaire de leur ajouter un ActionListener dans ce cas de figure.


GridLayout, RadioButtons et ScrollPanes

Le programme MaRadio.java reprend l'exemple précédent en introduisant le Layout Manager GridLayout et en montrant comment utiliser des RadioButtons (c'est-à-dire des check boxes exclusifs).

Ces RadioButtons :

Retailler la fenêtre. Que constate t-on ? Noter que le JTextArea et les JPanels ne sont pas agencés de la même faÁon que dans l'exemple précédent par rapport au BorderLayout du JFrame.

Cet exemple montre donc comment on peut emboîter plusieurs conteneurs avec des LayoutManagers différents pour obtenir des changements de taille appropriés lorsque l'on retaille la fenêtre.

Le programme MaRadio.java montre enfin comment utiliser des boucles et des tableaux pour simplifier et compacifier l'écriture du code.

Exercice

Si l'on clique de nombreuses fois sur les radio buttons, on constatera un comportement qui peut paraître étrange. Que se passe-t'il ? Pourquoi ?

Une solution pour éviter cet effet consisterait à utiliser des ascenceurs. C'est ce que l'on vous demande de faire en utilisant la classe JScrollPane.



Les Boîtes de Dialogue

La classe JDialog permet de créer des fenêtres secondaires, c'est-à-dire des boîtes de dialogue. Un JDialog ressemble à un JFrame, mais avec quelques particularités. Tout d'abord les JDialogs sont iconifiés et détruits en même temps que la fenetre principale. D'autre part, ils peuvent être rendus modaux (i.e. bloquants) au moyen de la méthode setModal().

Les JDialogs sont des fenêtres vides qu'il convient de compléter (de la même manière que les autres conteneurs Java). Swing permet également de créer des boîtes de dialogue prédéfinies au moyen des classes JFileChooser (pour choisir un fichier), JOptionPane (pour afficher un message) etc.

L'exemple LesDialogs1.java illustre ces différents aspects en reprenant des parties des exemples précédents. On regardera et vérifiera en particulier:

Amélioration: programmation OO

Dans l'exemple précédent on programmé "à la mode C" : les boîtes de dialogue sont crées en appelant une méthode qui joue à peu près le même rÙle qu'une bête fonction C (noter que CreerMonDialog() est d'ailleurs une méthode de classe).

La logique de la programmation orientée objet voudrait plutÙt que l'on définisse une nouvelle classe (qui hérite de JDialog) pour pouvoir créer proprement les boîtes de dialogue. Cette faÁon de faire permet de décomposer un programme en sous-parties clairement identifiées, bien séparées et facilement réutilisables, au lieu d'aboutir à un gigantesque sac de noeuds au bout d'un certain temps de développement.

C'est ce que l'on se propose de faire dans le programme LesDialogs2.java.

Regarder le code correspondant à l'instantiation et à la définition de la nouvelle classe MonDialog. Celle-ci hérite de JDialog et implémente également ActionListener. Il est donc nécessaire de définir la méthode actionPerformed correspondante qui va permettre de coder le comportement des widgets contenus dans ce JDialog. Et ... c'est justement ce que l'on va vous demander de faire dans l'exercice suivant !

Remarques:

Exercices

1. Compléter l'exemple afin que:

2. Rajouter une troisième boîte de dialogue qui permettrait de sélectionner un nom de fichier et de l'afficher dans le JTextArea de la fenêtre principale (NB: utiliser JFileChooser).



Enrichissement d'objets Swing par héritage

Il est fréquent qu'une application nécessite l'utilisation d'objets ayant des fonctionnalités dont ne disposent pas les objets standard fournis dans les librairies Java. Plusieurs stratégies sont alors possibles:

Avec cette deuxième approche, la fonctionnalité supplémentaire est intégrée à l'objet lui-même, typiquement sous la forme d'une méthode d'instance. Ceci permet :

Nous allons illustrer cette approche en enrichissant les JButtons de telle sorte qu'ils affichent une bulle d'aide chaque fois que la souris entre dans le bouton (noter que cette fonctionnalité existe en standard mais nous allons la réimplémenter pour les besoins de l'exercice). Le programme TipProg.java montre comment définir une nouvelle classe de boutons, appelée TipButton (dans le fichier TipButton.java) qui effectue (partiellement) ce travail. Les TipButton ont exactement les mêmes fonctionnalités que les JButton standard, sauf que leur couleur change quand la souris passe dessus et un message d'aide est affiché dans la Terminal d'où on a lancé le programme.

Cet exemple, pour l'instant incomplet (le message devrait être affiché dans une bulle d'aide), illustre plusieurs aspects importants :

Héritage et enrichissement progressif

Surcharge

Constructeurs

Variables de Classe et d'Instance

Méthodes de Classe et d'Instance

Réutilisation de la classe TipButton

Pour rendre notre nouvel objet TipButton facilement réutilisable et accessible depuis n'importe quel programme, il est nécessaire de le rendre public et ddonc e le séparer de la classe TipProg en le mettant dans un fichier spécifique (noter qu'un même fichier java ne peut contenir qu'une seule classe publique).

La compilation de TipProg.java entrainera automatiquement celle de TipButton. Il suffira donc de faire :

        javac  TipProg.java
puis:
        java  TipProg
(car TipProg.class definit la méthode main)

Exercices



Les Layout Managers

Les 3 Layout Managers les plus courants ont déjà été présentés au cours du TP précédent:

Les deux Layout Managers standard restants sont: Ces Layout Managers sont illustrés depuis cette page. (sous forme d'applets Java). Retailler interactivement ces applets. Que constate-t'on ?



Graphique et Inner Classes

Le programme DesGribouillis1.java montre comment gérer des événements souris pour permettre de faire du dessin à main leveé. Ces programmes nécessitent de travailler avec les Listeners: MouseListener et MouseMotionListener. Contrairement à l'ActionListener vu dans les exemples précédents, ces deux Listeners possèdent plusieurs méthodes qu'il est donc nécessaire d'implémenter même si elle ne font rien (corps vide).

Le programme DesGribouillis2.java montre une version plus élégante du même programme en utilisant les classes imbriquées (inner classes). Ce programme contient deux types de classes imbriquées

Dans les deux cas, les classes imbriquées ont accès aux variables de la classe imbricante (c'est-à-dire la classe DesGribouillis2 dans ce cas).

Exercice

Compléter cet exemple de telle sorte que les boutons qui doivent changent la couleur de la zone de dessin (background) ou celle du pinceau (foreground) soient actifs. Pour ce faire, rajouter un ActionListener approprié à chacune des deux classes BgButton et FgButton en utilisant la technique des classes imbriquées anonymes.



Communication entre Applications

Cette section montre comment faire communiquer 2 applications distinctes par des sockets. Plus précisément, on va faire dialoguer:

Le programme Java jouera le rôle du client et enverra une requête au serveur lorsque l'utilisateur cliquera sur certaines touches. Le programme C jouera le rôle du serveur. Il devra récupérer ces requêtes, les traiter (= effectuer un calcul à partir de ces données) et renvoyer un résultat au programme Java.

Ce type d'architecture est tès général et peut être décliné sous diverses formes. Notre exemple (fichiers Client.java, serv.c et Makefile) montre un cas simple où les requêtes envoyées par le client sont traitées en séquence par le serveur. Des architectures plus complexes pourraient permettre de traiter des requêtes en parallèle.

Exercice

Compléter l'exemple de manière à réaliser une calculette:

Faire en sorte que l'interface graphique ressemble effectivement à une calculette et que l'affichage reste cohérent lorsque l'utilisateur la retaille.

Note: la partie C doit être limitée au strict minimum (utiliser la fonction sscanf()).



Les Menus

Le programme LesMenus.java montre comment créer des barres de menus (JMenuBar) et des menus déroulants (JMenu). On notera en particulier que les boutons contenus dans les menus ne sont pas des boutons ordinaires mais des JMenuItem.

Exercice

Compléter le programme DesGribouillis2.java afin de produire une interface graphique complète, comportant:


Les Applets

Les Applets sont des programmes Java qui sont exécutables depuis un navigateur Web. Ces programmes ont (entre-autres) pour particularité de ne pas posséder de méthode main() (laquelle est obligatoire pour tous les autres programmes Java). A la place, elles doivent comporter une méthodes init() qui sert à initialiser l'applet lorsqu'elle est chargée par le browser.

D'autre part, les Applets sont realisées en dérivant la classe JApplet (et non JFrame) comme pour les applications "normales"). Ceci s'explique par le fait que l'Applet est normalement inclue dans une page Web (JFrame ouvrirait une fenetre en dehors du navigateur Web).

Une applet est spécifée dans une page HTML de la manière suivante:


 <APPLET CODE = "MonApplet.class" CODEBASE="MesClasses" WIDTH=150 HEIGHT=25>
 </APPLET>
CODEBASE est une balise optionnelle qui sert à indiquer où se trouve le fichier .class.
WIDTH et HEIGHT (obligatoires) servent à spécifier la place occupée par l'applet dans la page Web

Les applets peuvent également être testées au moyen de commande appletviewer. Cette commande prend pour argument une page HTML (un fichier ou une URL)


Pour rendre le TP

1) Verifier que votre code compile et s'exécute correctement depuis une salle de TP Unix de l'Ecole.

2) Ecrire un bref fichier README indiquant les questions traitées et contenant les réponses aux questions et tous les commentaires que vous jugerez utiles. Ce fichier doit être en PDF ou en HTML (en UTF-8) ou au format texte (en UTF-8 également). N'oubliez pas de mettre votre nom (au moins dans le README et de préférence aussi dans les autres fichiers)

3) Créer un fichier zip ou tar.gz (pas d'autre format!) contenant votre README et le code source (ne mettez pas l'executable ni les .class)

4) Aller à l'URL http://services.infres.enst.fr/rendutp/, cliquer (le bon ;-) lien puis entrer votre nom et télécharger le fichier zip ou tar.gz

NB: seule la dernière version téléchargée est prise en compte



Eric Lecolinet - http://www.telecom-paristech.fr/~elc - Telecom ParisTech