Exercice
Swing est une bibliothèque Java pour la réalisation d'interfaces graphiques, elle s'appuie sur une bibliothèque de plus bas niveau nommée AWT (Abstract Window Toolkit). La documentation de ces bibliothèques se trouve sur le site d'Oracle, vous êtes fortement encouragés à aller la consulter.
Vous aurez besoin d'importer la plupart des classes que vous allez utiliser, faites le au fur et à mesure.
1. Création et affichage d'une fenêtre
- Créer un nouveau projet
gloo.testswing
sous Eclipse, créer le module correspondant. Dans ce projet, ajouter, dans le paquetagegloo.testswing
, une classeTestSwing
qui contiendra la fonctionmain()
de votre programme.
- Conformémént aux indications données ici (les threads seront vus ultérieurement), modifier votre classe pour qu'elle implémente l'interface
Runnable
, demander à Eclipse d'ajouter la méthoderun()
(quick fix sur le nom de la classe → add unimplemented methods), appeler dansmain()
la fonctioninvokeLater()
de la classeSwingUtilities
avec une nouvelle instance de votre classe comme argument.SwingUtilities
est souligné en rouge, et le quick fix Import 'SwingUtilities' (javax.swing) n'est pas sufisant pour résoudre le problème : le paquetagejavax.swing
figure dans le modulejava.desktop
, il faut donc indiquer que ce module est requis (instructionrequires
) dans votre fichiermodule-info.java
.
Après sauvegarde de vos fichiers suite à ces modifications, demandez de l'aide à votre encadrant si des erreurs subsistent.
La classe principale pour la réalisation d'une interface graphique est la classe JFrame
qui permet de créer une fenêtre.
- Créer dans la fonction
run()
une instance de la classeJFrame
ayant pour titreGraphisme avec Swing
.
- Pour fixer la taille de la fenêtre, vous appellerez sur votre instance la méthode :
void setPreferredSize(Dimension preferredSize); // permet d'indiquer la taille par défaut de la fenêtre
- Vous aurez besoin d'une instance de la classe
Dimension
. Cette classe (voir sa documentation) possède un constructeur qui prend en argument un entier pour la largeur et un pour la hauteur.
- Pour que votre application se termine quand on ferme la fenêtre, vous utiliserez la méthode suivante :
void setDefaultCloseOperation(int operation); // Utilisez JFrame.EXIT_ON_CLOSE comme argument
- Enfin, pour que la fenêtre s'affiche, vous utiliserez les méthodes suivantes. Attention, ces méthodes doivent être appelées à la fin de votre code.
void pack(); // permet de mettre en forme la fenêtre void setVisible(boolean visible); // permet d'afficher (ou de masquer) la fenêtre.
- Vérifier que votre fenêtre s'affiche correctement.
Il est possible d'ajouter différents composants (boutons, menus déroulants, cases à cocher, zones de texte...) dans une fenêtre. Pour le projet intégré, vous aurez besoin d'y ajouter une zone de dessin. Pour cela, il faut utiliser une classe qui hérite de la classe JPanel
(qui représente un composant vide, voir sa documentation) et redéfinir la méthode de dessin paint()
utilisée par tous les composants graphiques.
- Définir une classe
MonDessin
qui hérite deJPanel
.
- Redéfinir (Menu Source → Override/implement Methods…) dans cette classe la méthode
void paint(Graphics g)
de manière à dessiner un rectangle de couleur rouge et de taille 570 x 292. Cette méthode reçoit en argument un objet de typeGraphics
(voir sa documentation), on peut voir cet objet comme le crayon servant à dessiner sur notre surface. Cette méthodepaint()
de votre classeMonDessin
sera appelée automatiquement et l'objetGraphics
reçu en argument sera lui aussi créé automatiquement, vous devez juste définir dans cette méthode le comportement à adopter lorsque la fenêtre est dessinée.
- Pour dessiner votre figure, vous utiliserez les méthodes suivantes de la classe AWT
Graphics
:
void setColor(Color color); // permet de changer la couleur du crayon // La classe Color définit des constantes pour les principales couleurs void fillRect(int x, int y, int width, int height); // permet de dessiner un rectangle plein
- Vous devez ensuite créer dans votre fonction
run()
une instance de votre classeMonDessin
. Vous associerez cette instance à votre instance deJFrame
avec la méthodeadd(Component)
qui peut prendre l'instance deMonDessin
en argument.
Nous souhaitons maintenant afficher une image. Pour cela, nous allons utiliser la classe ImageIO
.
- Télécharger l'image suivante dans le répertoire contenant votre projet (à côté des sous dossiers
src
etbin
déjà présents) : image.
- Définir un attribut de type
Image
dans votre classeMonDessin
. Créer un constructeur pour votre classeMonDessin
. Utiliser la méthodeImageIO.read(File file)
pour charger votre image dans le constructeur deMonDessin
. La classeFile
possède un constructeur qui prend en argument une chaine de caractère indiquant le chemin d'accès au fichier (le nom suffit si le fichier est dans le répertoire de votre projet).
- Eclipse va vous signaler une erreur :
Unhandled exception type IOException
. Choisir l'optionSurround with try/catch
proposée.
- Modifier enfin votre méthode
paint
pour afficher l'image par dessus le rectangle rouge dans la zone de dessin, en utilisant la méthodedrawImage(Image img, int x, int y, ImageObserver observer)
de la classeGraphics
. Vous laisserez le dernier paramètre ànull
.
2. Gestion des évènements claviers
Nous voulons que l'interface réagisse lorsque l'utilisateur appuie sur une touche du clavier. Par exemple, nous voulons que le rectangle change de couleur (rouge/orange/vert) chaque fois que l'utilisateur appuie sur la touche Ⓡ, Ⓞ ou Ⓥ.
Le principe de mise en œuvre est le suivant : une classe qui implémente l'interface KeyListener
(dans le paquetage java.awt.event
, voir sa documentation) et qui est associée à la fenêtre au premier plan recevra les événements claviers et pourra ainsi modifier ce qu'affiche la fenêtre en fonction des événements reçus.
Ne pas confondre une interface au sens Java ou UML (type définissant un ensemble de méthodes abstraites) avec une interface homme-machine (IHM) permettant les échanges entre un utilisateur et une application.
- Ajouter à la classe
MonDessin
une méthode permettant de modifier la couleur du rectangle ; cette méthode recevra donc comme unique argument la nouvelle couleur à utiliser. C'est uniquement dans la méthodepaint(Graphics g)
que vous utiliserez cette nouvelle couleur, il faudra donc mémoriser dans un attribut la couleur reçue.
Attention : vous ne devez pas vous même appeler la méthode paint(Graphics g)
pour prendre en compte la nouvelle couleur : pour forcer le JPanel
à se redessiner afin que cette nouvelle couleur soit prise en compte, appelez la méthode repaint()
sur votre objet MonDessin
qui se chargera d'appeler la méthode paint(Graphics g)
avec le bon argument.
- Créer une nouvelle classe
GestionClavier
qui implémenteKeyListener
.
Pour implémenter une interface, on utilise le mot clef implements
au lieu de extends
pour l'héritage. Il faut également définir toutes les méthodes de l'interface.
Les méthodes à définir sont keyPressed(KeyEvent e)
, keyReleased(KeyEvent e)
et keyTyped(KeyEvent e)
, seule la deuxième nous sera utile. La classe KeyEvent
contient des méthodes permettant d'avoir des informations sur la touche qui a déclenché l'évènement (nous utiliserons getKeyCode()
) ainsi que des constantes correspondant aux touches du clavier (par exemple KeyEvent.VK_V
).
- Ajouter en attribut à
GestionClavier
un objet de typeMonDessin
que vous lui transmettrez comme argument du constructeur.
- Implémenter la reconnaissance du caractère tapé et le changement de couleur associé dans la méthode
keyReleased()
.
- Dans votre fonction
run()
, après la création de l'objet de typeMonDessin
, créer une instance deGestionClavier
et associer la à votre instance deJFrame
en utilisant la méthodeaddKeyListener(KeyListener)
.
- Vérifier le bon fonctionnement.
Voici le schéma global des classes avec les opérations utiles :
3. Bonne conception
Dans une application informatique il est nécessaire de distinguer les objets représentant les données manipulées par l'application (on parle d'objets métiers) des objets chargés d'afficher ces données (les objets de l'IHM, objets frontières). Cette approche est justifiée par les points suivants :
- il est souvent utile d'afficher des données métiers de différentes façons (exemple classique : les différents graphiques, pour une même série de données, proposés par les tableurs) ;
- une application doit souvent pouvoir s'exécuter sur différentes plate-formes (Windows®, Unix®, iOS®, Android®...) ;
- l'IHM doit être adaptée aux spécificités culturelles des utilisateurs (langue, façon d'afficher ou d'écrire les nombres...).
Pour notre exemple de dessin, nous allons considérer1 que la couleur d'affichage du rectangle est une donnée métier, ainsi que l'image affichée. Par contre, les touches à utiliser pour choisir la couleur dépendent de la langue de l'utilisateur. Un Anglais utilisera par exemple le Ⓖ à la place du Ⓥ pour choisir la couleur verte.
Par ailleurs, le déroulement des échanges entre un utilisateur et un système informatique doit en général respecter des règles qui peuvent par exemple imposer un certain ordre dans les échanges ou vérifier que certaines conditions sont remplies pour permettre le passage à l'étape suivante. Le ou les objets qui s'occupent de ces contrôles sont appelés contrôleurs et servent d'intermédiaires entre les objets de l'IHM et les objets métiers.
Nous utilisons ici une interprétation restrictive du patron d'architecture MVC (Model-View-Controller) : dans le cadre d'une mise en œuvre classique de ce patron, les objets d'affichage accèdent en lecture aux objets métiers sans passer par le contrôleur.
Dans notre exemple de dessin, nous imposerons que les couleurs changent en respectant l'ordre d'un feu tricolore.
- Créer 3 paquetages
boundary
,control
etentity
.
- Réorganiser votre code de façon à avoir :
- dans
boundary
, une classeMaFenetre
qui hérite deJFrame
et qui servira aussi d'écouteur clavier ; - dans
boundary
, une classeMonAfficheur
qui hérite deJPanel
et qui s'occupe de l'affichage ; - dans
entity
, une classeMaCouleur
qui mémorise la couleur courante et y donne accès en lecture et en modification ; - dans
entity
, une classeMonImage
qui donne accès à l'image (mémorisée dans uneBufferedImage
) et à ses dimensions ; - dans
control
, une classeMonControleur
qui sert d'intermédiaire.
- dans
Les objets IHM connaissent le contrôleur : la fenêtre l'informe des demandes de changement de couleur, et l'afficheur lui demande la couleur courante, l'image et ses dimensions.
Le contrôleur connait MonImage
et MaCouleur
2 : il sert d'intermédiaire pour les demandes de l'afficheur. Il vérifie que le changement de couleur demandé est valide avant de demander ce changement à l'objet couleur.
- Écrire les constructeurs des classes pour établir ces liens (c'est le constructeur de la fenêtre qui crée l'afficheur).
- Compléter le reste du code pour avoir une application fonctionnelle.
Architecture de l'application :
1 Ce choix est ici purement didactique, les objets métiers représentent, dans le cas général, des données liées au domaine applicatif ; voir par exemple le cas du projet intégré. ⇑
2 Donc, ici, tous les objets métiers ; dans le cas général, le contrôleur ne doit en connaître que quelques-uns ; voir par exemple le cas du projet intégré. ⇑

© 2025 CentraleSupélec