Compléments de programmation Java
Axe ISI
ENS Mines, Saint-Étienne
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 :
- l'affichage de composants graphiques (fenêtres, boutons, listes, champs de texte, ...)
- différents modes de dispostion de ces composants dans un espace
- la gestion d'événements (click sur un bouton, sélection dans une liste, ...)
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); }
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)
.
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 :
- un texte simple :
javax.swing.JLabel
- une zone de texte modifiable :
javax.swing.JTextField
oujavax.swing.JTextArea
- un bouton :
javax.swing.JButton
- une liste d'éléments :
javax.swing.JList
- ...
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.
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.
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);
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);
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.
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 :
- le modèle contenant les données représentées par le composant et son état courant ;
- la vue correspondant à l'apparence graphique du composant ;
- le contrôleur réagissant à l'apparition de certains événements pour y associer des traitements.
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 :
ActionEvent
: Se produit lorsqu'une action est effectuée sur un composant. Ex : clic sur un bouton.ItemEvent
: Se produit lorsqu'une sélection a été effectuée sur un composant. Ex : cochage d'une case.KeyEvent
: Se produit lorsque un événement provient du clavier. Ex : pression d'une touche.MouseEvent
: Se produit lorsque un événement provient de la souris. Ex : déplacement de la souris.WindowEvent
: Se produit lorsqu'une action est effectuée sur une fenêtre. Ex : clic sur l'icone de fermeture d'une fenêtre.
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.
- Ajoutez un bouton "Quitter" à votre classe.
- Créez une nouvelle classe
QuitController
qui implémente l'interfacejava.awt.event.ActionListener
. Cette implémentation d'interface permet à votre classe de jouer le rôle de contrôleur pour des évenements de typejava.awt.event.ActionEvent
. Surchargez la méthodepublic 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'instructionSystem.exit(0)
). - 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 - Modifiez le code afin de quitter lorsque l'on appuie sur l'icône de fermeture de fenêtre.
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".
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.
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.
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 :
Rendu du travail
A la fin de la séance, envoyez les archives .jar correspondant à votre travail achevé à Gauthier Picard.
Liens Utiles
Gauthier Picard,