Java – Conception d’interface graphique (IHM )(GUI Jean-Pierre Fournier http://www.iut-orsay.fr/~fournier ) Une application informatique comporte : une partie visible (front office) interface homme-machine graphique interactif présenté par l’application interface homme-machine graphique interactif présenté par un navigateur Web (avec formulaire et applet) une partie invisible (back office) effectue les calculs, fournit et stocke les données jeudi 15 novembre 2007 Java - AWT 2 Une application interactive comporte Un modèle Récupère et stocke les données, fournit des informations Une vue Présente les données que gère le modèle Un contrôleur Réagit aux actions de l'utilisateur Ces trois éléments respectent le mode d'organisation MVC (préconisé depuis 1979) [Trygve Reenskaug] jeudi 15 novembre 2007 Java - AWT 3 MVC Modèle Vue jeudi 15 novembre 2007 IHM Java - AWT Contrôleur 4 L’interface homme-machine doit absolument rester réactive => jamais de traitement long est sensible aux événements clics souris touches clavier présence, gestes, sourires ? des événements peuvent provenir d’autres applications (fenêtres masquées, démasquées…) jeudi 15 novembre 2007 Java - AWT 5 Les éléments nécessaires des composants élémentaires (boutons, labels, listes de choix, zones textuelles, zones de dessin…), Button, Label, Choice, TextField, Canvas… des conteneurs (cadres, panneaux…) : pour assembler des composants élémentaires constituant un groupe, Frame, Panel, JPanel… des organiseurs de présentation qui définissent comment les composants élémentaires seront placés les uns par rapport aux autres, GridLayout, FlowLayout, CardLayout… des réacteurs : petits traitements destinés à être déclenchés par les événements spécifiés par WindowListener, Actionlistener, KeyListener… jeudi 15 novembre 2007 Java - AWT 6 Java propose plusieurs familles de composants AWT (Abstract Window Toolkit) : package java.awt, les composants simples (depuis 1.0), s’appuient sur les outils natifs, rapides et généralement suffisants Swing (JFC) : package javax.swing, composants complexes donnant le même aspect partout, plus riche et (beaucoup) plus lent SWT : variante « IBM » de l’AWT, employée notamment pour Eclipse jeudi 15 novembre 2007 Java - AWT 7 Exemple d'une application complète : le jeu du pendu Il comprendra 3 composants (M V C) Le modèle saura quel mot doit être deviné Le modèle saura, à chaque instant, quelles lettres ont été proposées par le joueur Le modèle saura si la partie est terminée, etc… La vue présentera l'état du jeu en temps réel Le contrôleur réagira aux actions de l'utilisateur (proposition de lettre, etc…), en informera le modèle et préviendra la vue de la nécessité éventuelle de rafraîchir ce qu'elle présente jeudi 15 novembre 2007 Java - AWT 8 Exemple d’une application complète : le jeu de affichage pendu permanent du Nous voudrions ce type titre bouton pour: d’interface quitter temps écoulé zone de saisie jeudi 15 novembre 2007 zone de dessin bouton pour quitter Java - AWT 9 Etape 0 : M Le jeu du pendu est un jeu dans lequel le joueur doit deviner un mot en proposant des lettres D'autres jeux : mots croisés, sudoku, superPendu, etc. ont le même fondement Donc : définition d'une interface qui sera commune aux modèles de tous ces jeux (si elle n'existe pas encore) Puis : implémentation de cette interface dans le cas particulier du pendu simple jeudi 15 novembre 2007 Java - AWT 10 L'interface des modèles public void proposition(char lettre, int x, int y); public String phraseAAfficher(); public int nombrePropositions(); public int nombreErreurs(); public boolean gagne(); public boolean perdu(); jeudi 15 novembre 2007 Java - AWT 11 Fin de l'étape 0 Écriture de notre modèle du jeu de pendu Réalisation des tests unitaires qui permettront de garantir son bon fonctionnement Écriture de la classe qui lancera l'application : Elle instancie la classe Modèle Elle instancie la ou les classes Contrôleur Elle instancie la classe Vue Elle transmet à la vue les références du modèle et des contrôleurs Elle transmet aux contrôleurs les références de la vue et du modèle jeudi 15 novembre 2007 Java - AWT 12 Etape 1 : V créer une classe dérivant de Frame ici : class VueJeuDeLettres extends Frame{ // définition des composants // constructeur // méthodes diverses jeudi 15 novembre 2007 Java - AWT 13 Etape1 : décider quels composants graphiques seront employés un Canvas pour la zone de dessin un Label pour l’affichage du temps écoulé un TextField pour la zone de saisie de lettres un Button pour quitter jeudi 15 novembre 2007 Java - AWT 14 Etape 2 : décider comment ils se placeront si on ne fait rien : FlowLayout par défaut décision : un BorderLayout pour la fenêtre principale, un GridLayout pour répartir également les composants à droite (impose de les rassembler dans un conteneur) autres possibilités : CardLayout, GridbagLayout… ou aussi : null => les objets doivent alors être tous placés et replacés en absolu jeudi 15 novembre 2007 Java - AWT 15 Etape 3 : se préoccuper des dimensions et de la place de la fenêtre deux solutions : décider de la taille extérieure de la fenêtre et adapter tous les composants pour obtenir l’effet désiré : compliqué un Label ou un Button dont la taille est figée ne pourra pas nécessairement bien présenter son texte décider la taille minimale de chaque composant et laisser java en déduire la taille de la fenêtre (méthode pack()) attention : certains composants (Canvas) ont une taille initiale 0 x0 dans tous les cas : l’application doit s’attendre à ce que sa fenêtre soit redimensionnée par l’utilisateur => dessins proportionnels et non absolus ! jeudi 15 novembre 2007 Java - AWT 16 Etape 4 : préparer les réactions aux événements (liaison V <-> C) en associant à chaque composant un ou plusieurs bouts de codes destinés à être déclenchés par le gestionnaire de fenêtres si l’utilisateur produit des événements dans cette fenêtre Attention : ces méthodes sont appelées très fréquemment, leur clic sur un bouton, traitement doit être ultrarapide déplacement de la souris sur un objet action au clavier et même s’il produit des événements dans d’autres applications, masquant ou démasquant : méthodes public void update(Graphics cg) et public void paint(Graphics cg) Ces deux méthodes ne sont jamais appelées par votre programme, qui ne peut appeler que repaint() jeudi 15 novembre 2007 Java - AWT 17 Examen du code du jeu de Pendu… jeudi 15 novembre 2007 Java - AWT 18 Les réacteurs (au sein du contrôleur) sont des petits bouts de code préparés dans l’application alors qu’elle n’est pas encore visible pour être activés par les gestionnaire de fenêtres à la suite d’événements ils doivent absolument respecter les interfaces prévus (package java.awt.event) jeudi 15 novembre 2007 Java - AWT ActionListener AdjustmentListener AWTEventListener ComponentListener ContainerListener FocusListener HierarchyBoundsListener HierarchyListener InputMethodListener ItemListener KeyListener MouseListener MouseMotionListener MouseWheelListener TextListener WindowFocusListener WindowListener WindowStateListener 19 Dans notre cas Une réaction à prévoir quand l'utilisateur ferme la fenêtre (action sur la croix Windows) Un contrôleur devra donc implémenter la méthode windowClosing() de WindowListener, Une réaction à prévoir s'il clique sur le bouton "Quitter" Un contrôleur devra donc implémenter la méthode actionPerformed(…) de ActionListener, Une réaction à prévoir s'il propose une lettre dans la zone de saisie Un contrôleur devra donc implémenter la méthode keyTyped(…) de KeyListener. jeudi 15 novembre 2007 Java - AWT 20 Trois manières de les placer cas d’une réaction unique associée à un événement unique dans une seule fenêtre cas d’une réaction unique à des événements multiples d’une même fenêtre cas d’une réaction unique à des événements multiples dans diverses fenêtres de l’application jeudi 15 novembre 2007 Java - AWT 21 Placement de réacteur (1) quitter.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { ((Window) ((Component) arg0.getSource()).getParent().getParent()).dispose(); } }); Le contrôleur ne reçoit que l'événement qui s'est produit, il doit essayer de retrouver la fenêtre principale : compliqué ! Dans ce cas, le contrôleur est caché à l'intérieur de la vue : interdit dans le modèle MVC jeudi 15 novembre 2007 Java - AWT 22 Placement de réacteur (2) class Pendu extends Frame implements ActionListener{ … dans le constructeur quitter.addActionListener(this); … public void actionPerformed(ActionEvent e){…} parmi les méthodes de la classe Pendu Dans ce cas, le contrôleur et la vue ne font qu'un : interdit dans le modèle MVC jeudi 15 novembre 2007 Java - AWT 23 Placement de réacteur (3) dans le constructeur … quitter.addActionListener(new Controleur()); … une autre classe, dans public class Controleur un autre fichier implements ActionListener{…} Il pourra y avoir plusieurs contrôleurs… jeudi 15 novembre 2007 Java - AWT 24 Cas de contrôleurs multiples Ils ont en commun la vue et le modèle Attributs privés : références de la vue et du modèle abstract class ControleurBase Class ControleurBouton extends ControleurBase implements ActionListener jeudi 15 novembre 2007 Accesseurs protégés Class ControleurFenetre extends ControleurBase implements WindowListener Java - AWT Class ControleurClavier extends ControleurBase implements KeyListener 25 Le réacteur reçoit un objet « événement » qui sait quel événement s’est produit, quel composant est concerné (getSource()), où il s’est produit (x, y)… on peut alors préciser de quel composant il s’agit Soit parce qu'un seul composant est lié à ce contrôleur soit parce qu’on le sait : Button leBouton = (Button) arg.getSource(); soit en demandant : if (arg0.getSource() instanceof java.awt.Button){…} on peut alors remonter dans l’arborescence des composants attachés succession de getParent() Mais il vaut mieux que le contrôleur sache pour quelle application il travaille jeudi 15 novembre 2007 Java - AWT 26 Le réacteur doit être ultrarapide tant qu’il travaille, le gestionnaire d’application qui l’a appelé ne peut plus réagir aux événements donc tout doit être fait avant l’apparition de la fenêtre pour qu’il ne reste plus que des traitements rapides dans les réacteurs ex : si un événement entraîne l’apparition d’une nouvelle fenêtre, la construction de cette fenêtre a été anticipée, et le réacteur fait seulement un setVisible(true) ex : si un événement entraîne une recherche dans une base de données, toute sa préparation doit avoir été faite avant un traitement long ou répétitif peut toujours être placé dans un autre processus, le réacteur se contente alors de le lancer… jeudi 15 novembre 2007 Java - AWT 27 Listener vs Adapter les interfaces de certains réacteurs sont riches ex : WindowListener a 6 méthodes (extrait de la doc Java) void windowActivated(WindowEvent e) Invoked when the Window is set to be the active Window. void windowClosed(WindowEvent e) Invoked when a window has been closed as the result of calling dispose on the window. void windowClosing(WindowEvent e) Invoked when the user attempts to close the window from the window's system menu. void windowDeactivated(WindowEvent e) Invoked when a Window is no longer the active Window. void windowDeiconified(WindowEvent e) Invoked when a window is changed from a minimized to a normal state. void windowIconified(WindowEvent e) Invoked when a window is changed from a normal to a minimized state. void windowOpened(WindowEvent e) Invoked the first time a window is made visible. pour éviter d’avoir à écrire des méthodes avec des corps vides, des classes java le font pour vous : exemple : abstract class WindowAdapter implements WindowListener jeudi 15 novembre 2007 Java - AWT 28 Délégation de travaux longs ou répétés Les Threads permettent de lancer des tâches indépendantes le traitement à réaliser est placé dans la méthode public void run() le processus est lancé par start() il s’arrête de lui-même quand il a fini jeudi 15 novembre 2007 Java - AWT 29 Exemple dans le jeu de Pendu à la fin du constructeur : dort pendant 0.5 seconde new Thread(){ public synchronized void run(){ long initial = System.currentTimeMillis()/1000; while (!modele.gagne() && !modele.perdu() && laVue.isVisible()){ try { wait(500); } catch (InterruptedException e) {/* */} tempsEcoule.setText((System.currentTimeMillis()/1000-initial)+" s."); } tempsEcoule.setText(((modele.gagne())?"gagné en ":"perdu en ")+(System.currentTimeMillis()/1000-initial)+" s."); } }.start(); modifie le texte du label jeudi 15 novembre 2007 Java - AWT 30 Cette présentation et le source du jeu du Pendu sont sur le wiki et sur http://www.iut-orsay.fr/~fournier jeudi 15 novembre 2007 Java - AWT 31