Interfaces graphiques en Java

La plupart des points abordés lors de ce TP figurent dans le livret du cours Langage et concepts de programmation orientée-objet du Pôle informatique.

Construction d'une interface graphique

Packages java.awt et javax.swing

Les deux principaux paquetages utilisés pour le développement d'interfaces graphiques sont les paquetages java.awt et javax.swing. Le paquetage java.awt existe depuis les toutes premières distributions java et contient des classes implémentant les parties génériques de toute interface graphique. Le paquetage javax.swing, introduit ultérieurement, contient des versions améliorées de certaines classes du package java.awt (composants graphiques dits lightweight) qui affichent des composants avec une apparence différente.

Les classes contenues dans ces deux packages fournissent des implémentations pour des traitements utilisés dans la plupart des interfaces graphiques tels que :

Dans le cadre de ce TP, nous utiliserons principalement le paquetage javax.swing.

Fenêtres et boîtes de dialogues

Une fenêtre correspond à une instance de la classe javax.swing.JFrame. La manière la plus classique de créer et d'afficher une fenêtre est de l'instancier en invoquant un constructeur qui créé tous ces composants graphiques, de définir sa taille initiale, puis de la rendre visible.

L'instanciation d'une fenêtre se fait en appelant l'un des constructeurs de la classe JFrame ou d'une de ses sous-classes.

JFrame myFrame = new JFrame("le titre de ma fenetre");

Pour fixer les dimensions de la fenêtre, on fait la plupart du temps appel à la méthode pack() qui les calcule en fonction des dimensions préférés des composants de la fenêtre.

myFrame.pack();

Enfin, l'affichage se fait par l'invocation de la méthode setVisible.

myFrame.setVisible(true);

Par exemple, le code suivant permet d'afficher une telle fenêtre :

public static void main(String[] args) {
  JFrame myFrame = new JFrame("Hello World!");
  myFrame.pack();
  myFrame.setVisible(true);
}
./images/jframe.png

Les boîtes de dialogue sont des instances de la classe javax.swing.JDialog et sont gérées de la même manière excepté qu'elles sont rattachées à une autre fenêtre (ou boîte de dialogue) lors de l'appel du constructeur. Par exemple :

JDialog myDialog = new JDialog(myFrame,"le titre de ma
boite de dialogue",true);

La valeur booléenne passée comme troisième argument du constructeur indique si la boîte de dialogue est bloquante. Si c'est le cas, la fenêtre "parente" (celle indiquée en premier argument) est bloquée lorsque la boîte de dialogue est visible.

Exercice 1

Ecrivez une classe qui hérite de JFrame. Insérez dans cette classe un constructeur qui fait appel au constructeur de JFrame ainsi qu'une méthode main pour créer, fixer la taille et afficher votre fenêtre. Pour fixer une dimension à cette fenêtre vide, consulter la documentation de l'API au sujet de la méthode setSize(Dimension).

./images/dimension.png

Composants et conteneurs

Pour définir précisément une interface graphique, on créé généralement une classe qui hérite de JFrame et dont le constructeur instancie plusieurs composants graphiques et leur assigne une position dans la fenêtre. Le paquetage javax.swing contient des classes pour les composants les plus fréquemment utilisés, par exemple :

La création d'un composant nécessite l'appel d'un constructeur de l'une de ces classes (consultez la documentation associée pour connaître les paramètres des constructeurs).

Les conteneurs sont des composants spécifiques (instances de la classe java.awt.Container) qui sont constitués de plusieurs composants. La classe javax.swing.JPanel est très souvent utilisée pour créer un conteneur. Le contenu d'une fenêtre est notamment fixé en assignant un panel par la méthode setContentPane ou en modifiant le panel accessible par la méthode getContentPane.

Exercice 2

Modifiez le constructeur de la classe précédemment créée pour y ajouter un label avec le texte "Entrez votre nom", un champ de texte (classe javax.swing.JTextField) et deux boutons "ok" et "cancel". Changez ensuite la méthode utilisée pour dimensionner la fenêtre en utilisant plutôt la méthode pack() et observez les conséquences de ce changement.

./images/okcancel.png

Disposition des composants

L'objet qui gère la disposition des composants est une instance implémentant l'interface java.awt.LayoutMangager. Cet objet doit être ajouté au conteneur, soit lors de l'appel à son constructeur, soit par la méthode setLayout. Un seul LayoutManager est autorisé par container. Quelques classes utilisées pour la disposition des composants sont présentées ci-dessous.

BorderLayout

Le conteneur est découpé en cinq cases : une case par côté (gauche, droite, haut et bas) et une au centre. L'ajout d'un composant se fait en précisant la case à utiliser grâce à des variables de classe de BorderLayout :

myContainer.setLayout(new BorderLayout());
myContainer.add(MyComponent,BorderLayout.NORTH);
myContainer.add(MyComponent2,BorderLayout.CENTER); 

La taille de chaque case dépend : (i) de la largeur maximale entre la case du nord, du sud et l'addition des largeurs des cases du centre, de l'est et de l'ouest; (ii) de la hauteur maximale entre la case à l'est, à l'ouest et l'addition des hauteurs des cases du centre, du nord et du sud. La case du centre est étirée pour remplir tout l'espace restant.

./images/borderlayout.png

FlowLayout

Les composants sont ajoutés les uns à la suite des autres et de la gauche vers la droite. Dès qu'une ligne est remplie de composants (c'est-à-dire dès que la largeur du conteneur est atteinte, les composants restants sont ajoutés sur une ligne en dessous. Il n'y a pas d'autres contraintes de disposition :

myContainer.setLayout(new FlowLayout());
myContainer.add(MyComponent);
./images/flowlayout.png

GridLayout

Le conteneur est découpé en une grille composée de n cases de taille égale. Le constructeur du GridLayout requiert en paramètres d'entrée le nombre de lignes et le nombre de colonnes de la grille. Les composants sont ajoutés les uns à la suite des autres en remplissant d'abord la première ligne, puis la seconde, etc. Il n'y a pas d'autres contraintes de disposition :

myContainer.setLayout(new GridLayout(3,4));
myContainer.add(MyComponent);
./images/gridlayout.png

Exercice 3

Modifiez la classe précédente pour que son panel principal utilise une instance de BorderLayout pour la disposition. le label doit se situer en haut et le champ de texte au milieu. Pour la case du bas, créez une instance de JPanel qui contiendra les deux boutons. La disposition des composants dans ce panel peut être gérée soit par FlowLayout ou GridLayout. Modifiez les dimensions de la fenêtre quand elle est affichée (à l'aide de la souris et pas directement dans le code) pour constater l'effet produit sur les composants.

./images/layoutframe.png

Gestion d'événements

Le schéma Modèle-Vue-Contrôleur (MVC)

Le schéma modèle-vue-contrôleur (MVC) est un mode de conception d'applications graphiques qui consiste à distinguer des données de leur apparence graphique et des traitements qui sont effectués quand une action donnée intervient. Un composant graphique est décomposé en trois parties :

Si on prend l'exemple d'une liste, le modèle est un objet (qui implémente l'interface javax.swing.ListModel) encapsulant tous les éléments de la liste. Sa vue (instance de la classe javax.swing.JList) gère l'affichage graphique qui représente cette liste. Enfin il peut exister plusieurs contrôleurs. Leur rôle est de recevoir les événements d'une certaine nature pour exécuter un traitement donné. Dans le cas d'une liste, un événement peut être la sélection d'un élément de la liste (représenté par une instance de la classe javax.swing.event.ListSelectionEvent). Tous les contrôleurs associés à cette liste pour cet événement doivent implémenter l'interface javax.swing.event.ListSelectionListener.

Pour les composants les plus simples, un modèle par défaut est automatiquement créé. La décomposition entre vue et modèle est donc invisible pour le développeur. Dans le cadre de ce TP, nous allons plus particulièrement étudier le fonctionnement des contrôleurs.

Événements

Un événement graphique est représenté dans le langage Java comme un objet dont la classe hérite de java.awt.AWTEvent. Parmi les sous-classes de AWTEvent, on peut citer les plus couramment utilisées :

Des méthodes sont attachées à chacune de ces classes pour avoir accès à plus de détails sur l'événement. On peut, par exemple, récupérer le composant source de l'événement, la position de la souris lors du clic, etc.

Interfaces de type Listener

Le contrôleur qui intercepte un certain type d'événement doit implémenter une des interfaces héritant de java.util.EventListener. L'interface à implémenter dépend du type d'événement à intercepter. La table ci-dessous présente les interfaces correspondant à des événements.

Contrôleur Événement Méthodes à implémenter
java.awt.event.ActionListener java.awt.event.ActionEvent + public void actionPerformed(ActionEvent)
java.awt.event.ItemListener java.awt.event.ItemEvent + public void itemStateChanged(ItemEvent)
java.awt.event.MouseListener java.awt.event.MouseEvent + public void mouseClicked(MouseEvent)
+ public void mouseEntered(MouseEvent)
+ public void mouseExited(MouseEvent)
+ public void mousePressed(MouseEvent)
+ public void mouseReleased(MouseEvent)
java.awt.event.WindowListener java.awt.event.WindowEvent + public void windowActivated(WindowEvent)
+ public void windowClosed(MouseEvent)
+ public void windowClosing(MouseEvent)
+ public void windowDeactivated(MouseEvent)
+ public void windowDeiconified(MouseEvent)
+ public void windowIconified(MouseEvent)
+ public void windowOpened(MouseEvent)

Certaines de ces interfaces demandent qu'un grand nombre de méthodes soient implémentées (par ex, WindowListener). Des classes, appelées adapter, implémentant ces interfaces sont proposées dans l'API, pour lesquelles toutes les méthodes ont des implémentations vides. Cette facilité de programmation permet de n'implémenter que la méthode souhaitée. Par exemple, on utilisera la classe WindowAdapter pour implémenter un traitement à effectuer à la fermeture d'un fenêtre (méthode windowClosing()) sans avoir à écrire des méthodes vides pour tous les autres cas où aucun traitement n'est requis.

Après la création d'un objet contrôleur, il est nécessaire de le rattacher à un ou plusieurs composants. Le contrôleur intercepte uniquement les événements des composants auquel il est rattaché. Cette opération se fait par l'appel à une méthode du composant de la forme add...Listener. Par exemple, le rattachement d'un contrôleur myActionListener à un bouton s'écrit :

myButton.addActionListener(myActionListener);

Exercice 4

Dans cet exercice, vous allez écrire un contrôleur permettant de réagir à un clic sur un bouton.

  1. Ajoutez un bouton "Quitter" à votre classe.
  2. Créez une nouvelle classe QuitController qui implémente l'interface java.awt.event.ActionListener. Cette implémentation d'interface permet à votre classe de jouer le rôle de contrôleur pour des évenements de type java.awt.event.ActionEvent. Surchargez la méthode public void actionPerformed(ActionEvent e) pour écrire ce qui doit se passer quand ce contrôleur reçoit un événement. Ici, il faudra simplement quitter le programme (par l'instruction System.exit(0)).
  3. Associez une instance de votre classe QuitControler au bouton de la fenêtre, de manière à ce que ce contrôleur réagisse aux événements qui se produisent sur ce bouton
  4. Modifiez le code afin de quitter lorsque l'on appuie sur l'icône de fermeture de fenêtre.
./images/listener.png

Exercice 5

Ajoutez des contrôleurs et des actions de réaction au clic sur les boutons "ok" et "cancel". Par exemple, changer le nom de la fenêtre par le texte du champ de texte lorsque l'on clique sur "ok" et remise à zéro du champ lorsque l'on clique sur "cancel".

./images/listeners.png

Exercice 6

Créez une fenêtre contenant un unique panel qui change de couleur à chaque clic de souris suivant le cycle suivant : rouge, vert, bleu, rouge, vert, etc. Pour ceci il faut créer un contrôleur capable de réagir aux événements de souris. A chaque clic de souris, modifiez le titre de la fenêtre avec les coordonnées du pointeur.

./images/red.png  ./images/green.png  ./images/blue.png

Exercice 7

Créez une fenêtre deux champs de texte éditables et un bouton. Lorsque l'on clique sur le bouton la fenêtre doit prendre les dimensions renseignées dans les champs de texte. Si aucune valeur n'est fournie, la fenêtre ne change pas de dimension.

./images/resize.png

Utilisation de Jigloo

Comme nous avons pu le voir en cours, Jigloo est un éditeur graphique d'interfaces Swing ou SWT, distribué en tant que Plugin Eclipse.

Exercice 8

Installez le plugin Jigloo dans Eclipse (marche uniquement sur les postes Windows). Les instructions d'installation se trouve sur le site de Cloud Garden.

Exercice 9

Afin de vous familiariser avec Jigloo, reprenez les exercices 4 et 5 avec cet outil plutôt que de programmer la composition et l'affectation d'écouteurs à la main.

Exercice 10

Afin de vous perfectionner avec l'utilisation de Jigloo, développez une calculette graphique comme celle que l'on peut trouver sur la plupart des systèmes d'exploitation, comme par exemple celle-ci :

./images/calculette.png

Rendu du travail

A la fin de la séance, envoyez les archives .jar correspondant à votre travail achevé à Gauthier Picard.

Liens Utiles


Gauthier Picard,