09.07.2015 Views

Interfaces graphiques - Introduction Introduction

Interfaces graphiques - Introduction Introduction

Interfaces graphiques - Introduction Introduction

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

<strong>Interfaces</strong> <strong>graphiques</strong>-<strong>Introduction</strong>Université de Nice - Sophia AntipolisVersion 4.5.2 – 13/3/11Richard GrinRichard Grin Interface graphique 1Contributions• Des exemples de cette partie du cours sontfortement inspirés du livreAu cœur de Java 2Volume I - Notions fondamentalesde Horstmann et CornellThe Sun Microsystems PressJava Series• De nombreuses images proviennent du tutorialen ligne d’Oracle (gratuit) :http://download.oracle.com/javase/tutorialRichard Grin Interface graphique 2Plan de cette partie• Généralités sur les interfaces <strong>graphiques</strong>• Affichage d’une fenêtre• Classes de base ; AWT et Swing• Placer des composants dans une fenêtre• Gestion des événements• Modèle MVC ; exemple des listes• Dessiner ; afficher une imageGénéralités sur lesinterfaces <strong>graphiques</strong>Richard Grin Interface graphique 3Richard Grin Interface graphique 4Interface avec l’utilisateur• La quasi-totalité des programmes informatiquesnécessitent– l’affichage de questions posées à l’utilisateur– l’entrée de données par l’utilisateur– l’affichage des résultats obtenus par le traitementinformatique• Cet échange d’informations peut s’effectuer avecune interface utilisateur (UI en anglais) en modetexte (ou console) ou en mode graphiqueRichard Grin Interface graphique 5Interface graphique• Une interface graphique est formée d’une ouplusieurs fenêtres qui contiennent diverscomposants <strong>graphiques</strong> (widgets) tels que– boutons– listes déroulantes– menus– champ texte–etc.• Les interfaces <strong>graphiques</strong> sont souvent appelésGUI d’après l’anglais Graphical User InterfaceRichard Grin Interface graphique 61


Un exempleProgrammation avecinterface graphique• L’utilisateur peut interagir à tout moment avecplusieurs objets <strong>graphiques</strong> : bouton, listedéroulante, menu, champ texte, etc.• Ces actions peuvent modifier totalement lecheminement du programme• L’ordre d’exécution des instructions ne peut êtreprévu à l’écriture du codeRichard Grin Interface graphique 7Richard Grin Interface graphique 8Programmation conduitepar les événements• Une interface graphique impose une façonparticulière de programmer• La programmation « conduite par lesévénements » est du type suivant :– les actions de l’utilisateur (déplacement, clicde souris, frappe de touche du clavier,…)engendrent des événements qui sont misdans une file d’attente– le programme récupère un à un cesévénements et les traiteRichard Grin Interface graphique 9Boîtes à outils <strong>graphiques</strong>• Les boîtes à outils <strong>graphiques</strong> offrent desfacilités pour utiliser et gérer la file d’attente desévénements• En particulier pour associer les événementséavec les traitements qu’ils doivent déclencherRichard Grin Interface graphique 10La solution Java : les écouteurs• Le JDK utilise une architecture de type« observateur - observé » :– les composants <strong>graphiques</strong> (boutons, listes,menus,…) sont les observés– chaque composant graphique a sesobservateurs (ou écouteurs, listeners) quis’enregistrent auprès de lui comme écouteurd’un certain type d’événement (par exemple,clic de souris)Rôle d’un écouteur• Il est prévenu par le composant graphiquedès qu’un événement qui le concernesurvient sur ce composant• Il exécute alors l’action à effectuer enréaction à l’événement• Par exemple, l’écouteur du bouton « Exit »demande une confirmation à l’utilisateur ettermine l’applicationRichard Grin Interface graphique 11Richard Grin Interface graphique 122


API utilisées pour les interfaces<strong>graphiques</strong> en JavaLes API• 2 bibliothèques :–AWT(Abstract Window Toolkit, JDK 1.1)– Swing (JDK 1.2)• Swing et AWT font partie de JFC (JavaFoundation Classes) qui offre des facilités pourconstruire des interfaces <strong>graphiques</strong>• Swing est construit au-dessus de AWT– même gestion des événements– les classes de Swing héritent des classes de AWTRichard Grin Interface graphique 13Richard Grin Interface graphique 14Swing ou AWT ?• Tous les composants de AWT ont leuréquivalent dans Swing– en plus joli– avec plus de fonctionnalitésMais Swing est pluslourd et plus lentque AWT• Swing offre de nombreux composants quin’existent pas dans AWT⇒ Il est fortement conseillé d’utiliser lescomposants Swing et ce cours sera donc centrésur SwingRichard Grin Interface graphique 15Paquetages principaux•AWT: java.awt et java.awt.event• Swing : javax.swing, javax.swing.event, etdes sous-paquetages de javax.swing dont lesprincipaux p sont– liés à des composants ; table, tree, text (et sessous-paquetages), filechooser, colorchooser–liés au look and feel général de l’interface (plaf =pluggable look and feel) ; plaf, plaf.basic,plaf.metal, plaf.windows, plaf.motifRichard Grin Interface graphique 16Affichage du GUI, cas simpleAfficher une fenêtre• Une classe (appellons-la Fenetre)représente la fenêtre principale del’application ; elle hérite de la classe JFrame• La construction de l’interface graphique peutse faire dans le constructeur de la classeFenetre• L’affichage est alors effectué en appelant leconstructeur de la fenêtre :new Fenetre();Richard Grin Interface graphique 17Richard Grin Interface graphique 183


Construire le GUIimport javax.swing.JFrame;ousetTitle("...")public class Fenetre extends JFrame {public Fenetre() {super("Une fenêtre");// ou pack();compacte le contenu de la fenêtresetSize(300, 200);ou setBounds(…)setVisible(true);}affiche la fenêtrepublic static void main(String[] args) {new Fenetre();}Taille d’une fenêtre• pack() donne à la fenêtre la taille nécessairepour respecter les tailles préférées descomposants de la fenêtre (tout l’écran si cette tailleest supérieure à la taille de l’écran)• Taille ou un emplacement précis sur l’écran (en pixels) :setLocation(int xhg, int yhg) (ou Point en paramètre)setSize(int largeur, int hauteur) (ou Dimensionen paramètre)setBounds(int x, int y, int largeur, inthauteur) (ou Rectangle en paramètre)Richard Grin Interface graphique 19Richard Grin Interface graphique 20Positionnement d’une fenêtre et icône(On doit importer java.awt.*)public Fenetre() {// Centrage de la fenêtreToolkit tk = Toolkit.getDefaultToolkit();Dimension d = tk.getScreenSize();int hauteurEcran = d.height;int largeurEcran = d.width;setSize(largeurEcran/2, hauteurEcran/2);setLocation(largeurEcran/4, hauteurEcran/4);// tant qu’on y est, ajoutons l’icône…Image img = tk.getImage("icone.gif");}setIconImage(img);. . .setLocationRelativeTo(null)centre une fenêtre sur l’écranRichard Grin Interface graphique 21Affichage du GUI,cas plus complexe• Pour accélérer le démarrage de l’applicationgraphique et pour éviter que l’interfacegraphique ne se fige dans certains cas, il estconseillé de lancer l’affichage selon leschéma indiqué dans le transparent suivant• Des explications détaillées sont donnéesdans la section « Swing et threads »Richard Grin Interface graphique 22Afficher une fenêtre (2 ème façon)private static void construireAfficherGUI() {new Fenetre();}public static void main(String[] args) {SwingUtilities.invokeLater(new i t Runnable() {public void run() {construireAfficherGUI();}});}Garder une fenêtre au premier plan• Depuis le JDK 5, il est possible d’indiquerqu’une fenêtre doit être au premier plan (parrapport aux autres fenêtres) :frame.setAlwaysOnTop(true);Richard Grin Interface graphique 23Richard Grin Interface graphique 244


Classe java.awt.Toolkit• Les sous-classes de la classe abstraiteToolkit implantent la partie de AWT qui esten contact avec le système d’exploitation hôte• Quelques méthodes publiques :getScreenSize, getScreenResolution,getDefaultToolkit, beep, getImage,createImage, getSystemEventQueue• getDefaultToolkit fournit une instance dela classe qui implante Toolkit (classedonnée par la propriété awt.toolkit)Richard Grin Interface graphique 25Émettre un bip• La méthode beep() de la classe Toolkitpermet d’émettre un bip :tk.beep();• L’instance de Toolkit s’obtient par laméthode getDefaultToolkit()• Le plus souvent ce bip prévient l’utilisateurde l’arrivée d’un problème ou d’unévénementRichard Grin Interface graphique 26Écran de démarrage• Depuis Java 6, lorsque l’application prendun certain temps à s’initialiser, il estpossible de faire afficher une image (JPEG,GIF ou PNG) de démarrage pour fairepatienter l’utilisateur et lui signaler quel’application a vraiment démarré• L’image sera effacée dès l’ouverture de lapremière fenêtre de l’applicationExemple• Il suffit de passer l’image en paramètre dujava qui lance l’application ; par exemplejava –splash:image.jpg Main• Si l’application est dans un jar, il estpossible d’indiquer l’image « splash » dansle fichier MANIFEST du jar ; par exempleManifest-Version: 1.0Main-Class: TestSplashScreen-Image: image.gifRichard Grin Interface graphique 27Richard Grin Interface graphique 28Écran de démarrage plus complexe• Pour faire afficher un écran de démarrageplus complexe qu’une simple image fixe, ilest possible de récupérer une référence versl’image « splash » pour obtenir un graphiquemodifiable• Il est possible de faire effacer l’image avantl’apparition de la première fenêtre par laméthode close() de SplashScreenRichard Grin Interface graphique 29Exempleimport java.awt.SplashScreen;...SplashScreen splash =SplashScreen.getSplashScreen();S l Graphics2D g =(Graphics2D)splash.createGraphics();...splash.close();Richard Grin Interface graphique 305


Composants lourds et légersClasses Container etJComponentComposants lourds• Pour afficher des fenêtres (instances deJFrame), Java s’appuie sur les fenêtresfournies par le système d’exploitation hôtedans lequel tourne la JVM• Les composants Java qui, comme les JFrame, ,s’appuient sur des composants du système hôtesont dit « lourds »• L’utilisation de composants lourds améliore larapidité d'exécution mais nuit à la portabilité etimpose les fonctionnalités des composantsRichard Grin Interface graphique 31Richard Grin Interface graphique 32Composants légers• AWT utilise les widgets du systèmed’exploitation pour tous les composants<strong>graphiques</strong> (fenêtres, boutons, listes, menus,…)•Swing ne les utilise que pour les fenêtres de base« top-level »• Les autres composants, dits légers, sont dessinéspar Swing dans ces containers lourds• Attention, les composants lourds s’affichent toujoursau-dessus des composants légersRichard Grin Interface graphique 33Containers lourds• Il y a 3 sortes de containers lourds (un autre,JWindow, est plus rarement utilisé) :– JFrame fenêtre pour les applications– JApplet pour les applets– JDialog pour les fenêtres de dialogue• Pour construire une interface graphique avecSwing, il faut créer un (ou plusieurs) containerlourd et placer à l’intérieur les composantslégers qui forment l’interface graphiqueRichard Grin Interface graphique 34FrameJFrameHiérarchie d’héritage descontainers lourdsContainerWindowJComponentPanelDialogJDialogJWindowAppletJAppletLibérer les ressourcesassociées à une JFrame• En tant que composant lourd, une JFrame utilisedes ressources du système sous-jacent• Si on ne veut plus utiliser une JFrame (ouJDialog ou JWindow), ) mais continuerl’application, il faut lancer la méthodedispose() de la fenêtre ; les ressources serontrendues au système• Voir aussi la constante DISPOSE_ON_CLOSE del’interface javax.swing.WindowConstantsRichard Grin Interface graphique 35Richard Grin Interface graphique 366


Classe JComponentClasse abstraite JComponent• La plupart des widgets de Swing sont desinstances de sous-classes de la classeJComponent• Les instances des sous-classes deJComponent sont de composants « légers »• JComponent héritent de la classe Container• Tous les composants légers des sous-classesde JComponent peuvent donc contenird’autres composantsComposantsAWTObjectComponentContainerJComponentLes composantsde Swing sontdes containersRichard Grin Interface graphique 37Richard Grin Interface graphique 38Les Containers• Des composants sont destinés spécifiquementà recevoir d’autres éléments <strong>graphiques</strong> :– les containers « top-level » lourds JFrame,JApplet, JDialog, JWindow– les containers « intermédiaires » légersJPanel, JScrollPane, JSplitPane,JTabbedPane, Box (ce dernier est léger maisn’hérite pas de JComponent)JPanel• JPanel est la classe mère des containersintermédiaires les plus simples•Un JPanel sert à regrouper des composants dansune zone d'écran• Il n’a pas d’aspect visuel déterminé ; son aspectvisuel est donné par les composants qu’il contient• Il peut aussi servir de composant dans lequel onpeut dessiner ce que l’on veut, ou faire afficherune image (par la méthode paintComponent)Richard Grin Interface graphique 39Richard Grin Interface graphique 40Ajouter des composantsdans une fenêtreRichard Grin Interface graphique 41Le « ContentPane »• Avant le JDK 5, il n’était pas possibled’ajouter directement d’autres composantsaux containers « top-level »• Ces containers sont associés à un autrecontainer, le « content t pane » dans lequel el onajoute les composants• On obtient ce content pane par (topLevel estun container lourd ; JFrame par exemple)Container contentPane =topLevel.getContentPane();Richard Grin Interface graphique 427


Types de Layout manager• Les types les plus courants de gestionnaire demise en place (dans java.awt) :– BorderLayout : placer aux 4 pointscardinaux– FlowLayout : placer à la suite– GridLayout : placer dans une grille– GridBagLayout : placements complexesTypes de Layout manager• BoxLayout : placer verticalement ouhorizontalement (dans javax.swing)• Un nouveau gestionnaire de mise en place a étéintroduit par Java SE 6 (dans javax.swing) :– GroupLayout : placer des groupes decomposants en distinguant le placementvertical et horizontalRichard Grin Interface graphique 55Richard Grin Interface graphique 56BorderLayout• Affiche au maximum 5 composants (aux 4 pointscardinaux et au centre)• Essaie de respecter la hauteur préférée du nord etdu sud et la largeur préférée de l’est et de l’ouest ;le centre occupe toute la place restante• layout manager par défaut de JFrame et JDialogRichard Grin Interface graphique 57BorderLayout• Les composants sont centrés dans leur zone• On peut spécifier des espacement horizontauxet verticaux minimaux entre les composants• Si on oublie de spécifier le placement lors del'ajout dun d'un composant, celui-ci ci est placé aucentre (source de bug !)• Règle pratique : l’est et l’ouest peuvent êtreétirés en hauteur mais pas en largeur ; lecontraire pour le nord et le sud ; le centre peutêtre étiré en hauteur et en largeurRichard Grin Interface graphique 58Placement dans une fenêtre complexe• Pour disposer les composants d’une fenêtre destructure graphique complexe on peut :– utiliser des containers intermédiaires, ayantleur propre type de gestionnaire deplacement, et pouvant éventuellementcontenir d'autres containers– utiliser un gestionnaire de placement de typeGridBagLayout (plus souple mais parfoisplus lourd à mettre en œuvre)– mixer ces 2 possibilitésRichard Grin Interface graphique 59panelBoutonsau nordUtiliser un JPanelRichard Grin Interface graphique 6010


Utiliser un JPanelpublic Fenetre() {. . .JPanel panelBoutons = new JPanel();JButton b1 = new JButton("Cliquez moi !");JButton b2 = new JButton("Et moi aussi !");panelBoutons.add(b1); // FlowLayoutpanelBoutons.add(b2);this.add(panelBoutons, BorderLayout.NORTH);JTextArea textArea = new JTextArea(15, 5);this.add(textArea, BorderLayout.CENTER);JButton quitter = new JButton("Quitter");this.add(quitter, BorderLayout.SOUTH);. . .}FlowLayout• Rangement de haut en bas et de gauche à droite• Les composants sont affichés à leur taillepréférée• layout manager par défaut de JPanel etJApplet• Attention, la taille préférée d’un container géré par unFlowLayout est calculée en considérant que tous lescomposants sont sur une seule ligneSource de la classeExécutionRichard Grin Interface graphique 61Richard Grin Interface graphique 62Code avec FlowLayoutJPanel panel = new JPanel();// FlowLayout par défaut dans un JPanel// mais si on ne veut pas centrer :panel.setLayout(new FlowLayout(FlowLayout.LEFT));// On pourrait aussi ajouter des espaces// entre les composants avec// new FlowLayout(FlowLayout.LEFT, 5, 8)JButton bouton = new JButton("Quitter");JTextField zoneSaisie = new JTextField(20);panel.add(bouton);panel.add(zoneSaisie);Richard Grin Interface graphique 63GridLayout• Les composants sont disposés en lignes et encolonnes• Les composants ont tous la même dimension• Ils occupent toute la place qui leur est allouée•On remplit la grille ligne par ligne ou colonnepar colonne (suivant le nombre indiqué auconstructeur)Richard Grin Interface graphique 64Code avec GridLayout// 5 lignes, n colonnes// (on pourrait ajouter des espaces entre composants)panel.setLayout(new GridLayout(5,0));panel.add(bouton); // ligne 1, colonne 1panel.add(zoneSaisie); add(zoneSaisie); // ligne 2, colonne 1• On doit indiquer le nombre de lignes ou le nombre decolonnes et mettre 0 pour l’autre nombre (si on donne les2 nombres, le nombre de colonnes est ignoré !)• L’autre nombre est calculé d’après le nombred’éléments ajoutésGridBagLayout• Utilise un quadrillage dont les composants peuventoccuper plusieurs « cases » ; la disposition dechaque composant est précisée par une instance dela classe GridBagConstraints•C’est le layout manager le plus souple mais aussile plus complexeRichard Grin Interface graphique 65Richard Grin Interface graphique 6611


Contraintes de basepanel.setLayout(new GridBagLayout());GridBagConstraints contraintes =new GridBagConstraints();// ligne et colonne du haut gauchecontraintes.gridx = 0;contraintes.gridy = 0;// taille en lignes et colonnes (occupe 2 lignes ici)contraintes.gridheight = 2;contraintes.gridwidth = 1;// Chaque élément peut avoir ses propres contraintespanel.add(bouton, contraintes);Autres contraintes : placement• fill détermine si un composant occupe toutela place dans son espace réservé (constantes dela classe GridBagConstraint : BOTH, NONE,HORIZONTAL, VERTICAL) ; par défaut NONE• anchor dit où placer le composant quand il estplus petit que son espace réservé ; on peutdonner des valeurs absolues (CENTER, SOUTH,NORTHEAST,…) ou relatives à l’orientation ducomposant (PAGE_START, PAGE_END,LINE_START,…) ; par défaut CENTERRichard Grin Interface graphique 67Richard Grin Interface graphique 68Répartition de l’espace libre• weightx, weighty (de type double) indiquecomment sera distribué l’espace libre. Plus le nombreest grand, plus le composant récupérera d’espace ; pardéfaut 0Si tous les poids sont 0, l’espace est réparti dans les espacesplacés entre les composants• Algorithme : poids d’une colonne = weightxmaximum des composants de la colonne ; total =somme des poids de toutes les colonnes ; l’espace libred’une colonne est donné par weightx/total• Souvent associé à fill pour les composants quidoivent grandir avec la taille du composantRichard Grin Interface graphique 69La place minimale occupée• insets ajoute des espaces autour descomposants : contraintes.insets = newInsets(5,0,0,0) (ou contraintes.insets.top=5) ; par défaut 0 partout• ipadx, iapdy ajoutent des pixels à la tailleminimum des composants ; par défaut 0• Le layout manager tient compte des taillespréférées et minimales pour afficher lescomposantsRichard Grin Interface graphique 70Remarque sur le positionnement• La valeur par défaut pour gridx et gridyest GridBagConstraints.RELATIVE (seplace en dessous ou à droite du précédent)• Il suffit donc de fixer gridx à la valeurd’une colonne pour les composant de lacolonne et de garder la valeur par défaut degridy pour placer tous les composants de lacolonne les uns à la suite des autres (idempour les lignes en fixant gridy)Richard Grin Interface graphique 71ExempleGridBagConstraints contraintes =new GridBagConstraints();contraintes.gridx = 0;contraintes.gridy = 0;contraintes.gridwidth = 1;contraintes.gridheight = 1;this.add(new JLabel("Sujet : "), contraintes);contraintes.gridx = 2;// Zone de saisie occupe tout son espace réservé en// largeur et elle occupe le plus d'espace possiblecontraintes.fill = GridBagConstraints.HORIZONTAL;contraintes.weightx = 1;saisieSujetMail = new JTextField(20);this.add(saisieSujetMail, contraintes);Richard Grin Interface graphique 7212


Quand faut-il choisir GridBagLayout ?• Dès que l’interface devient tropcompliquée, il est souvent plus simpled’utiliser GridBagLayout plutôt que detrouver des constructions très rusées avecdes containers intermédiaires• On peut rendre le code plus lisible avec desméthodes qui facilitent l’affectation descontraintesBoxLayout• Aligne les composants sur une colonne ou une ligne(on choisit à la création)• Respecte la largeur (resp. hauteur) préférée etmaximum, et l’alignement horizontal (resp. vertical)• Layout manager par défaut de Box et de JToolBarRichard Grin Interface graphique 73Richard Grin Interface graphique 74BoxLayout• Pour un alignement vertical, les composants sontaffichés centrés et si possible– à leur largeur préférée– respecte leur hauteur maximum et minimum(get{Maxi|Mini}mumSize())• Pour un alignement horizontal, idem enintervertissant largeur et hauteur• Pour changer les alignements, on peut utiliser lesméthodes de la classe ComponentsetAlignment{X|Y}Alignment{X|Y}• Constantes de Component :•{LEFT|CENTER|RIGHT}_ALIGNMENT•{TOP|BOTTOM}_ALIGNMENT• Méthodes :• setAlignmentX etsetAlignmentYRichard Grin Interface graphique 75Richard Grin Interface graphique 76Problèmes d’alignement• Si tous les composants gérés par unBoxLayout n’ont pas le même alignement,on peut avoir des résultats imprévisibles• Par exemple, le seul composant aligné àgauche peut être le seul qui n’est pas alignéà gauche !• Il vaut donc mieux avoir le mêmealignement pour tous les composantsClasse Box• Cette classe est un container qui utilise unBoxLayout pour ranger ses composantshorizontalement ou verticalement• Elle fournit des méthodes static pour obtenirdes composants invisibles pour affiner ladisposition de composants dans un containerquelconque : glue, étais et zones rigidesRichard Grin Interface graphique 77Richard Grin Interface graphique 7813


Classe Box ; composants invisiblescontainer.add(composant1);// On ajoute ici un composant invisiblecontainer.add(. . .);container.add(composant2);Box.createRigidArea(new Dimension(5,0))))Box.createHorizontalGlue())new Box.Filler(new Dimension(5,100),new Dimension(5,100),new Dimension(Short.MAX_VALUE,100))Richard Grin Interface graphique 79Code avec Box// Le plus simple est d'utiliser une Box// mais on peut aussi mettre un BoxLayout// pour gérer un autre containerBox b = Box.createVerticalBox();b.add(bouton);// On peut ajouter des zones invisiblesi ibl// entre les composants :b.add(Box.createVerticalStrut(5));// ou b.add(Box.createRigidArea(// new Dimension(5, 15))b.add(zoneSaisie);Richard Grin Interface graphique 80CardLayout• CardLayout : affiche un seul composant à lafois ; les composants sont affichés à tour de rôle•Ce layout manager est plus rarement utilisé queles autres• JTabbedPane est un composant qui offre lemême type de disposition, en plus simple maisplus puissant, avec des ongletsGroupLayout• Introduit dans le JDK 6• L’idée originale est de dissocier leplacement horizontal des composants deleur placement vertical• Le problème est supposé moins complexe siles 2 dimensions sont dissociées• Pour les outils de création de GUI (Matisse)• Pas toujours facile à lire ni à modifierRichard Grin Interface graphique 81Richard Grin Interface graphique 82Groupes• Les composants sont regroupés dans desgroupes de placement (layout groups)• Chaque groupe de placement peut placer sescomposants suivant 2 arrangements suivantles 2 dimensions horizontale et verticale :séquentiel ou parallèle• Les groupes peuvent être emboîtés les unsdans les autres2 types d’arrangement• Séquentiel : les composants sont placés lesuns à la suite des autres• Parallèle : les composants sont placés aumême endroit (ce qui suppose unarrangement séquentiel dans l’autredimension)Richard Grin Interface graphique 83Richard Grin Interface graphique 8414


Extrait du tutorielJava d’OracleLe résultat recherché :ExempleHorizontalG1 G2 G3G1VerticalG2• Placement horizontal :groupe séquentiel (C1, C2,groupe parallèle (C3, C4))• Placement vertical :groupe séquentiel (groupe parallèle (C1, C2, C3),C4)Richard Grin Interface graphique 85Code de l’exemple (1)GroupLayout layout =new GroupLayout(getContentPane());this.setLayout(layout);layout.setHorizontalGroup(layout.createSequentialGroup().addComponent(c1).addComponent(c2).addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING).addComponent(c3).addComponent(c4) Alignement);à gaucheRichard Grin Interface graphique 86Code de l’exemple (2)layout.setVerticalGroup(layout.createSequentialGroup().addGroup();layout.createParallelGroup(GroupLayout.Alignment.BASELINE).addComponent(c1).addComponent(c2).addComponent(C3)).addComponent(c4)Alignementsur la « ligned’écriture »Taille des composants• Un composant ou un groupe peut s’agrandir ounon quand l’espace qui lui est alloué est agrandi• Voici un composant qui ne s’agrandit pas et quicontient 2 composants de la même taille :layout.createParallelGroup(GroupLayout.Alignment.LEADING, false).addComponent(c3, GroupLayout.DEFAULT_SIZE,GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE).addComponent(c4, GroupLayout.DEFAULT_SIZE,GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)Richard Grin Interface graphique 87Richard Grin Interface graphique 88Les règles pour la taille• addComponent prend en paramètre lestailles minimale, préférée, et maximale• La taille du groupe est celle de la taillepréférée du plus grand composant• Les composants sont élargis à la taille dugroupe si leur taille maximum le permetRichard Grin Interface graphique 89Compléments (1)• Il est possible de changer les espacementsentre composants en ajoutant des espaces(gaps) p ); méthodes addPreferredGap etaddContainerGap)• Il est possible d’automatiser l’ajoutd’espaces en appelantsetAutoCreateGaps(true) etsetAutoCreateContainerGaps(true)Richard Grin Interface graphique 9015


Compléments (2)• Dans certains cas, il peut être nécessaired’assurer la même taille pour 2 composantsqui n’appartiennent pas à un même groupe• Pour cela il faut utiliser la méthode linkSize• Par exemple, pour assurer la même taillehorizontale à c1, c4 et c25 :layout.linkSize(SwingConstants.Horizontal,c1, c4, c25);Richard Grin Interface graphique 91Exemples et contre-exemplesExercice : donner 2 solutions• avec des composants emboîtés• avec un GridBagLayoutRichard Grin Interface graphique 92BonMauvaisExposition du problèmeTraiter les événements :les écouteurs• L’utilisateur utilise le clavier et la souris pourintervenir sur le déroulement du programme• Le système d’exploitation engendre desévénementsé à partir des actions de l’utilisateur• Le programme doit lier des traitements à cesévénementsRichard Grin Interface graphique 93Richard Grin Interface graphique 94Types d’événements• Événements de bas niveau, générés directementpar des actions élémentaires de l’utilisateur• Événements « logiques » de plus haut niveau,engendrés par plusieurs actions élémentaires,qui correspondent à une action complète del’utilisateurRichard Grin Interface graphique 95Exemples d’événements• De bas niveau :– appui sur un bouton de souris ou une touche du clavier– relâchement du bouton de souris ou de la touche– déplacer le pointeur de souris• Logiques :– frappe d’un A majuscule– clic de souris– lancer une action (clic sur un bouton par exemple)– choisir un élément dans une liste– modifier le texte d'une zone de saisieRichard Grin Interface graphique 9616


Événements engendrés• La frappe d’un A majuscule engendre 5événements :• 4 événements de bas niveau :– appui sur la touche Majuscule– appui sur la touche A– relâchement de la touche A– relâchement de la touche Majuscule• 1 événement logique, de haut niveau :– frappe du caractère « A » majusculeRichard Grin Interface graphique 97Classes d’événements• Les événements sont représentés par des instancesde sous-classes de java.util.EventObject• Événements liés directement aux actions del’utilisateur :fenêtre ouverte,KeyEvent, MouseEventfermée, icônifiéeou désicônifiée• Événements de haut niveau :FocusEvent, WindowEvent, ActionEvent,ItemEvent, ComponentEventdéclencherune actionchoix dans une liste,dans une boîte à cochercomposantdéplacé, retaillé,Richard Grin Interface graphique 98caché ou montréacquérir ouperdre le focusÉcouteurs• Chacun des composants <strong>graphiques</strong> a sesobservateurs (ou écouteurs, listeners)• Un écouteur doit s’enregistrer auprès descomposants qu’il souhaite écouter, en luiindiquant le type d’événement qui l’intéresse(par exemple, clic de souris)• Il peut ensuite se désenregistrerRelation écouteurs - écoutés• Un composant peut avoir plusieursécouteurs (par exemple, 2 écouteurs pourles clics de souris et un autre pour lesfrappes de touches du clavier)• Un écouteur peut écouter plusieurscomposantsRichard Grin Interface graphique 99Richard Grin Interface graphique 100Une question• Quel message sera envoyé par le composantà ses écouteurs pour les prévenir quel’événement qui les intéresse est arrivé ?• Réponse : à chaque type d’écouteurcorrespond une interface que doitimplémenter la classe de l’écouteur ; parexemple ActionListener,MouseListener ou KeyListener• Le message doit correspondre à une desméthodes de cette interfaceÉvénements étudiés dans ce cours• En exemple, ce cours étudie principalement– les événements ActionEvent qui conduisentà des traitements simples (écouteurActionListener) )– les événements KeyEvent, au traitement pluscomplexe (écouteur KeyListener etadaptateur KeyAdapter)Richard Grin Interface graphique 101Richard Grin Interface graphique 10217


ActionEvent• Cette classe décrit des événements de hautniveau qui vont le plus souvent déclencher untraitement (une action) :– clic sur un bouton– return dans une zone de saisie de texte– choix dans un menu• Ces événements sont très fréquemment utiliséset ils sont très simples à traiterRichard Grin Interface graphique 103Interface ActionListener• Un objet ecouteur intéressé par les événements detype « action » (classe ActionEvent) doitappartenir à une classe qui implémente l’interfacejava.awt.event.ActionListener interface videqui sert de• Définition de ActionListener : marqueur pourpublic interface ActionListenertous lesextends EventListener {écouteursvoid actionPerformed(ActionEvent e);}message qui sera envoyécontient des informationsà l’écouteur (méthode callback)sur l’événementRichard Grin Interface graphique 104Inscription commeActionListener• On inscrit un tel écouteur auprès d’uncomposant nommé composant par la méthodecomposant.addActionListener(ecouteur);p ( )• On précise ainsi que ecouteur est intéressé parles événements ActionEvent engendrés parcomposantMessage déclenchépar un événement• Un événement unActionEvent engendré par uneaction de l’utilisateur sur bouton, provoqueral’envoi d’un message actionPerformed àchaque écouteur de bouton :ecouteur.actionPerformed(unActionEvent);• Ces messages sont envoyés automatiquement à tous lesécouteurs qui se sont enregistrés auprès du boutonRichard Grin Interface graphique 105Richard Grin Interface graphique 106Interface Action• Cette interface hérite de ActionListener• Elle permet de partager des informations etdes comportements communs à plusieurscomposants• Par exemple, un bouton, un choix de menuet une icône d’une barre de menu peuventfaire quitter une application•Elle est étudiée à la fin de cette partie du coursConventions de nommage• Si un composant graphique peut engendrer desévénements de type TrucEvent sa classe (ouune de ses classes ancêtres) déclare lesméthodes {add|remove}TrucListener()• L’interface écouteur s'appelleraTrucListenerRichard Grin Interface graphique 107Richard Grin Interface graphique 10818


Écouteur MouseListener• Des interfaces d’écouteurs peuvent avoir denombreuses méthodes• Par exemple, les méthodes déclarées parl’interface MouseListener sont :void mouseClicked(MouseEvent e)void mouseEntered(MouseEvent e)void mouseExited(MouseEvent e)void mousePressed(MouseEvent e)void mouseReleased(MouseEvent e)Adaptateurs• Pour éviter au programmeur d’avoir àimplanter toutes les méthodes d’une interface« écouteur », AWT fournit des classes (on lesappelle des adaptateurs) qui implantent toutesces méthodes• Le code des méthodes ne fait rien• Ça permet au programmeur de ne redéfinirdans une sous-classe que les méthodes quil’intéressentRichard Grin Interface graphique 109Richard Grin Interface graphique 110Exemples d’adaptateurs• Les classes suivantes du paquetagejava.awt.event sont des adaptateurs :KeyAdapter, MouseAdapter,MouseMotionAdapter, FocusAdapter,ComponentAdapter, WindowAdapterRichard Grin Interface graphique 111Fermer une fenêtre• Pour terminer l’application à la fermeture de la fenêtre,on ajoute dans le constructeur de la fenêtre un écouteur :public Fenetre() { // constructeur. . .addWindowListener(new WindowAdapter() {public void windowClosing(WindowEvent e) {System.exit(0);Classe anonyme internepour décrire l’écouteur}});. . .}ne pas confondre avecwindowClosed()appelée quand les ressourcessystème de la fenêtre sontlibérées (méthode dispose())Richard Grin Interface graphique 112Fermer une fenêtre• Depuis JDK 1.3, on peut se passer d’écouteur pourarrêter l’application à la fermeture d'une fenêtre :public Fenetre() { // constructeursetDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);. . .}• Autres actions possibles à la fermeture d’une fenêtre (cesont des constantes de l'interface WindowConstants,implémenté par la classe JFrame) :DO_NOTHING_ON_CLOSE, HIDE_ON_CLOSE, DISPOSE_ON_CLOSEPaquetage java.awt.event• Ce paquetage comprend les interfaces« écouteurs » et les classes d’événementsRichard Grin Interface graphique 113Richard Grin Interface graphique 11419


Classe EventObjectÉcriture d’un écouteur• Classe ancêtre des classes d’événements• Une instance d’une classe d’événements estpassée en paramètre aux méthodes desécouteurs• Cette instance décrit l’événement qui aprovoqué l’appel de la méthodeRichard Grin Interface graphique 115Richard Grin Interface graphique 116Méthode getSource• L’écouteur peut interroger l’événement pourlui demander le composant qui l’a généré• La méthode « public ObjectgetSource() » de EventObject renvoie lecomposant d’où est parti l’événement, parexemple un bouton ou un champ de saisie detexte• Souvent indispensable si l’écouteur écouteplusieurs composantsExemple de code d’un écouteur• Le code suivant modifie le texte du bouton surlequel l’utilisateur a cliqué :class EcouteurBouton extends MouseAdapter {public void mousePressed(MouseEvent e) {}((JButton)e.getSource()).setText("Appuyé");t("A }public void mouseClicked(MouseEvent e) {((JButton)e.getSource()).setText(e.getClickCount() + "clics");}Richard Grin Interface graphique 117Richard Grin Interface graphique 118Tester les touches modificatrices• La classe InputEvent fournit la méthodegetModifiers() qui renvoie un entierindiquant les touches modificatrices (Shift,Ctrl, Alt, Alt Gr) )qui étaient appuyées aumoment de l’événement souris ou clavier• Depuis la version 1.4, il est conseillé d’utiliserplutôt la méthode getModifiersEx() quicorrige des petits défauts de la méthodeprécédenteRichard Grin Interface graphique 119Tester les touches modificatrices (2)• On utilise les constantes static de type intsuivantes de la classe InputEvent :SHIFT_MASK , CTRL_MASK, ALT_MASK,META_MASK, ALT_GRAPH_MASK• Autres constantes de la classe InputEventpour tester les boutons de la souris :BUTTON1_MASK, BUTTON2_MASK,BUTTON3_MASK• Si on utilise getModifiersEx, il faut utiliserles constantes qui comportent DOWN dans leurnom ; par exemple, SHIFT_DOWN_MASKRichard Grin Interface graphique 12020


Exemple (suite)public void actionPerformed(ActionEvent e) {Object source = e.getSource();if (source == b1) {... // action liée au bouton b1}else if (source == b2) {. . . // action liée au bouton b2}else if (source == tf) {. . . // action liée au JTextField tf}. . .}Risque de nombreuxif … elseSuppose quel’on est dansla portée de b1qui contient uneréférence auboutonVariante de la solution 1• Quand les composants à écouter sont trèsnombreux, on peut regrouper dans le codeles traitements par types de composantsRichard Grin Interface graphique 127Richard Grin Interface graphique 128Exemple variante solution 1public void actionPerformed(ActionEvent e) {Object source = e.getSource();String classeSource = source.getClass().getName();if (classeSource.equals("JButton")) {if (source == b1) {. . . // action liée au bouton b1}else if (source == b2) {. . . // action liée au bouton b2}}else if (classeSource.equals("JTextField") {. . . // action liée au JTextField tf}RegroupementoptionnelRichard Grin Interface graphique 129ActionCommand• On peut associer à chaque bouton (et choix demenus) une chaîne de caractères parbouton.setActionCommand("chaine");• Cette chaîne– par défaut, est le texte affiché sur le bouton– permet d’identifier un bouton ou un choix de menu,indépendamment du texte affiché– peut être modifiée suivant l’état du bouton– peut être récupérée par la méthodegetActionCommand() de la classe ActionEventRichard Grin Interface graphique 130Solution 2 : classe interne• Solution plus extensible : chaque composant (ouchaque type ou groupe de composants) a sapropre classe écouteur• Le plus souvent, la classe écouteur est une classeinterne de la classe CExemplepublic class Fenetre extends JFrame {private JButton b1, b2;. . .public Fenetre() {. . .ActionListener eb = new EcouteurBouton();b1.addActionListener(eb);b1.setActionCommand("b1");b2.addActionListener(eb);b2.setActionCommand("b2");. . .}Richard Grin Interface graphique 131Richard Grin Interface graphique 13222


Exemple// Classe interne de Fenetreclass EcouteurBouton implements ActionListener {public void actionPerformed(ActionEvent e) {String commande = e.getActionCommand();if (commande.equals("b1")) {. . . // action liée au bouton b1}else if (commande.equals("b2")) {. . . // action liée au bouton b2}Exemple d'utilisation de}getActionCommand()}. . .Richard Grin Interface graphique 133Solution 3 : classe interne anonyme• Si la classe écoute un seul composant et necomporte pas de méthodes trop longues, laclasse est le plus souvent une classe interneanonyme• L’intérêt est que le code de l’écouteur estproche du code du composant• Rappel : une classe interne locale peut utiliserles variables locales et les paramètres de laméthode, seulement s’ils sont finalRichard Grin Interface graphique 134ExempleInstance d’une classe« anonyme » quiimplémente ActionListenerJButton ok = new JButton("OK");ok.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {// action à exécuter. . .}});Événements clavierRichard Grin Interface graphique 135Richard Grin Interface graphique 136Événements clavier• Un événement clavier de bas niveau est engendrépar une action élémentaire de l’utilisateur, parexemple, appuyer sur une touche du clavier(génère un appel à la méthode keyPressed() deKeyListener))• Plusieurs actions élémentaires de l’utilisateurpeuvent engendrer une seul événement de hautniveau ; ainsi, appuyer et relâcher sur les touchesShift et A pour obtenir un A majuscule, génère unappel à la méthode keyTyped() de KeyListenerRichard Grin Interface graphique 137KeyEvent• Cette classe correspond aux événements (evt)engendrés par l’utilisation du clavier• 3 types d'événements repérés par evt.getID() :– KeyEvent.KEY_PRESSED et KeyEvent.KEY_RELEASEDsont des événements de bas niveau et correspondent àune action sur une seule touche du clavier– KeyEvent.KEY_TYPED est un événement de haut niveauqui correspond à l’entrée d'un caractère Unicode (peutcorrespondre à une combinaison de touches commeShift-A pour A)Richard Grin Interface graphique 13823


KeyEvent• Si on veut repérer la saisie d’un caractèreUnicode, il est plus simple d’utiliser lesévénements de type KEY_TYPED• Pour les autres touches qui ne renvoient pasde caractères Unicode, telle la touche F1 oules flèches, il faut utiliser les événementsKEY_PRESSED ou KEY_RELEASEDKeyListenerpublic interface KeyListenerextends EventListener {void keyPressed(KeyEvent e);void keyReleased(KeyEvent e);void keyTyped(KeyEvent e);}• Si on n'est intéressé que par une des méthodes, onpeut hériter de la classe KeyAdapter• Dans ces méthodes, on peut utiliser les méthodesgetKeyChar() (dans keyTyped) etgetKeyCode() (dans les 2 autres méthodes)Richard Grin Interface graphique 139Richard Grin Interface graphique 140Exemple de KeyListenerclass EcouteCaracteres extends KeyAdapter {public void keyTyped(KeyEvent e) {if (e.getKeyChar() == 'q')quitter();}public void keyReleased(KeyEvent e) {if (e.getKeyCode() == KeyEvent.VK_ESCAPE)actionQuandEsc();}Événements sourisRichard Grin Interface graphique 141Richard Grin Interface graphique 1422 types d’écouteurs• Un type d’événement MouseEvent pour 2types d’écouteurs• MouseListener : bouton enfoncé ou relâché,clic (bouton enfoncé et tout t de suite relâché),entrée ou sortie du curseur dans la zone d’uncomposant• MouseMotionListener : déplacement ouglissement (drag) de sourisÉcouteur MouseListener• L’interface MouseListener contient lesméthodes– mousePressed(MouseEvent)– mouseReleased(MouseEvent)– mouseClicked(MouseEvent)– mouseEntered(MouseEvent)– mouseExited(MouseEvent)• Adaptateur MouseAdapterRichard Grin Interface graphique 143Richard Grin Interface graphique 14424


Écouteur MouseMotionListener• L’interface MouseMotionListenercontient les méthodes– mouseMoved(MouseEvent)– mouseDragged(MouseEvent)• Adaptateur MouseMotionAdapterRoulette de la souris• La version 1.4 du SDK a ajouté le typed’événement MouseWheelEvent, associéaux souris à roulettes• Ces événement peuvent être pris en comptepar les écouteurs MouseWheelListener• Le composant ScollPane a un tel écouteur,ce qui permet de déplacer la vue en utilisantla roulette de la sourisRichard Grin Interface graphique 145Richard Grin Interface graphique 146Traitement des MouseWheelEvent• Ce type d’événement a un traitementparticulier : l’événement est propagé aupremier composant intéressé, englobant lecomposant où est situé le curseur• Le plus souvent aucun codage ne seranécessaire pour traiter ce type d’événementscar le composant qui recevra l’événementsera un ScrollPane qui est d’origine codépour le traiterFocusRichard Grin Interface graphique 147Richard Grin Interface graphique 148Avoir le focus• La gestion du focus a changé à partir duSDK 1.4• Ce cours s’appuie sur la version 1.4• Un seul composant peut « avoir le focus » à unmoment donné• C’est le composant qui va recevoir tous lescaractères tapés au clavier par l’utilisateur• La fenêtre qui « a le focus » est celle quicontient ce composant• Un composant ne peut engendrer unKeyEvent que s’il a le focusRichard Grin Interface graphique 149Richard Grin Interface graphique 15025


Cycles pour obtenir le focus• Les composants ont le focus à tour de rôle• Par exemple, quand l’utilisateur tape [Enter]dans un champ de saisie, le composant« suivant » obtient le focus• Des touches spéciales permettent aussi depasser d’un composant à l’autre ([Tab] et [Maj]-[Tab] sous Windows et sous Unix)• L’ordre est le plus souvent déterminé par laposition sur l’écran des composants• Il peut aussi être fixé par programmationRichard Grin Interface graphique 151Obtenir le focus• 2 autres façons pour obtenir le focus• Le plus souvent un composant obtient le focusquand l’utilisateur clique sur lui• Il peut aussi l’obtenir par programmation avec laméthode boolean requestFocusInWindow()de la classe Component (meilleur querequestFocus du SDK 1.3)Richard Grin Interface graphique 152Obtenir le focus• Tous les composants ne peuvent avoir le focus• En général les zones de saisie de texte peuventl’avoir mais pas les boutons ou les labels• Dans la classe Component– la méthode boolean isFocusable() permet desavoir si un composant peut l’avoir(isFocusTraversal en SDK 1.3)– void setFocusable(boolean) permet de modifiercette propriétéMéthodes diverses• Dans la classe Component,– 2 méthodes pour passer au composant suivantou précédent : transfertFocus ettransfertFocusBackward– des méthodes pour gérer les cycles de passagede focus• Dans la classe Window, beaucoup deméthodes diverses à utiliser si on veut gérerle focus au niveau des fenêtresRichard Grin Interface graphique 153Richard Grin Interface graphique 154Focus et KeyListener• Si votre keyListener ne réagit pas,demandez vous si le composant qui estécouté a bien le focus• Vous pouvez utiliser pour cela la méthodeboolean isFocusOwner() de la classeComponent (hasFocus() du SDK 1.3 estobsolète)FocusListener• Écouteur pour savoir quand le focus changede main• Méthodes focusGained et focusLostRichard Grin Interface graphique 155Richard Grin Interface graphique 15626


Compléments : modifier lestouches de déplacement• Les touches qui permettent de se déplacerentre les composants peuvent être modifiéesen utilisant le KeyboardFocusManagerRichard Grin Interface graphique 157Compléments : modifier lapolitique de déplacement• L’ordre de déplacement peut être modifiéen choisissant une politique de déplacement• Par défaut, sous Swing, l’ordre dedéplacement dans un container dépend de laposition sur l’écran• On peut choisir un autre ordre avec laméthode de la classe ContainersetFocusTraversalPolicy(FocusTraversalPolicy policy)Richard Grin Interface graphique 158Mécanismes internespour traiter les événementsEtapes du traitement• Les événements sont1. mis dans la file d’attente des événements2. récupérés un à un par le thread de distributiondes événements3. redistribués par le thread aux composantsconcernés4. traités par les écouteurs concernés ducomposant (enregistrés auprès du composant)5. traités par le composant pour son proprecompteRichard Grin Interface graphique 159Richard Grin Interface graphique 160File d’attente des événements• Les événements sont engendrés le plussouvent par une action de l’utilisateurperçue par le système de fenêtrage natifsous-jacent• Les événements sont mis automatiquementpar le système AWT dans la file d’attentedes événements (instance unique de la classeEventQueue)Thread de distributiondes événements• Un thread unique (Event Dispatch Thread)1. récupère un à un les événements dans la filed’attente des événements2. passe la main au composant concerné (appelde dispatchEvent(AWTEvent) de la classeComponent)• Ce thread est créé automatiquement dès qu’uneune classe de AWT est utiliséeRichard Grin Interface graphique 161Richard Grin Interface graphique 16227


Traitement des événements• Le composant fait appel à processEvent(e)(classe Component) qui fait appel à laméthode appropriée :– processMouseEvent(MouseEvent) pour lesévénements liés à la souris– processFocusEvent(FocusEvent) pour lesévénements liés au focus– processKeyEvent(KeyEvent) pour ceux liésà la frappe de touches du clavier–etc.Appel des écouteurs• Le composant lance la méthode « callback »des écouteurs correspondant au type del’événement (MouseClicked,KeyReleased, ,…)Richard Grin Interface graphique 163Richard Grin Interface graphique 164Consommation d’un InputEvent• Après avoir été traité par tous les écouteurs,l’événement est traité par le composant luimême• Par exemple, un caractère tapé par l’utilisateursera affiché dans un JTextField• Un écouteur d’un InputEvent (Mouse et KeyEvent) peut empêcher cette dernière étape enconsommant l’événement (méthodeconsume() de la classe InputEvent)Richard Grin Interface graphique 165Génération d’un événement• On peut créer un événement par du code, nonprovoqué par une action de l’utilisateur, pourl’envoyer directement à un certain composant• On le fait en 2 étapes :– créer l’événement– envoyer un message « dispatchEvent » aucomposant, avec l’événement créé en paramètre• Remarque : les événements de haut niveau tels queActionEvent ne peuvent être simulés de cette façonRichard Grin Interface graphique 166Exemple de générationd’un événement• Dans un écouteur de menu (unActionListener qui est ici la fenêtre quicontient le menu), le code suivant simule lafermeture de la fenêtre par l’utilisateur (etsans doute la sortie de l’application) :if (e.getActionCommand().equals("sortie"))dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));Swing et threadsRichard Grin Interface graphique 167Richard Grin Interface graphique 16828


Les différents threads dans un GUI• Le thread qui lance l’interface graphique,qui peut initialiser l’environnementd’exécution du GUI• Le thread de distribution des événementsqui gère l’affichage et les traitements desévénements• Les threads lancés en arrière-plan quiexécutent les tâches de longue durée« Event dispatch thread »• Un seul thread appelé le thread de distributiondes événements (event dispatch thread) ,nommé TDE dans la suite, effectue– la récupération des événements dans la filed’attente– le traitement de ces événements (méthodesdes écouteurs)– l’affichage de l’interface graphiqueRichard Grin Interface graphique 169Richard Grin Interface graphique 170Traitements longs des événements• Tout traitement long effectué par ce threadfige l’interface graphique qui répond mal, oumême plus du tout, aux actions de l’utilisateur• Il faut donc effectuer les traitements longs(accès à une base de données, calculs oudessins complexes,…) des écouteurs et desméthodes d’affichage dans des threads à partSwing n’est pas « thread-safe »• Pour des raisons de performance et defacilité de mise en œuvre par lesprogrammeurs, les composants de Swing nesont pas prévus pour être utilisés parplusieurs tâches en même temps• Si un composant a déjà été affiché, tout codequi modifie l’état (le modèle) du composantdoit donc être exécuté par le thread dedistribution des événementsRichard Grin Interface graphique 171Richard Grin Interface graphique 172Problème• Comment faire si le traitement d’unévénement comporte une tâche longue àexécuter avant de modifier l’état d’uncomposant ?• Par exemple, si les éléments d’une liste sontrécupérés dans une base de données• La solution : la tâche longue est exécutée dansun thread à part ; au moment de modifier lecomposant, on passe la main au TDEModifier un composantdepuis d’autres threads• 2 méthodes utilitaires public static de laclasse javax.swing.SwingUtilitiespermettent de lancer des opérations quimodifient des composants de l’interfacegraphique depuis un autre thread que le threadde distribution des événements :– void invokeLater(Runnable runnable)– void invokeAndWait(Runnable runnable)Richard Grin Interface graphique 173Richard Grin Interface graphique 17429


Utiliser d’autres threads …• invokeLater dépose une tâche à accomplirdans la file d’attente des événements ; laméthode retourne ensuite sans attendrel’exécution de la tâche par le TDE• Le TDE exécutera cette tâche comme tous lestraitements d’événements• invokeAndWait, dépose une tâche mais neretourne que lorsque la tâche est exécutée parle TDERichard Grin Interface graphique 175Utilisation de invokeLater• Le schéma est le suivant si on a un traitementlong à effectuer, qui a une interaction avecl’interface graphique :– on lance un thread qui effectue la plus grande partiede la tâche, par exemple, accès à une base de donnéeet récupération des données– au moment de modifier l’interface graphique, cethread appelle invokeLater() en lui passant unRunnable qui exécutera les instructions qui accèdentou modifient l’état du composantRichard Grin Interface graphique 176Utilisation de invokeLater()class AfficheurListe extends Thread {private Runnable modifieurListe;private Collection donneesListe;AfficheurListe(List l) {modifieurListe = new Runnable() {public void run() {. . .} Modifie la liste en}utilisant t donneesListe}public void run() {// Remplit donneesListe avec les données// lues dans la base de données. . .SwingUtilities.invokeLater(modifieurListe);}Richard Grin Interface graphique 177invokeAndWait• La différence avec invokeLater est que l’instructionqui suit l’appel de invokeAndWait n’est lancée quelorsque la tâche est terminée• On peut ainsi lancer un traitement et tenir compte desrésultats pour la suite du traitement long• Attention, invokeAndWait provoquera un blocage s’ilest appelé depuis le event dispatch thread ; ça pourraitarriver si on veut partager du code entre un écouteur et uneméthode ; pour éviter ce problème, utiliser la méthode staticEventQueue.isDispatchThread()Richard Grin Interface graphique 178SwingWorker• La fin de cette section sur les threads dansle GUI présente la classe SwingWorker• Cette classe a été introduite par Java 6 pourfaciliter l’utilisation de threads lancés enarrière-plan dans les interfaces <strong>graphiques</strong>Méthode abstraitedoInBackground• SwingWorker comprend la méthodeabstraite doInBackground• Cette méthode devra être définie dans uneclasse fille• Elle sera exécutée en parallèle au TDElorsque la méthode execute sera lancéeRichard Grin Interface graphique 179Richard Grin Interface graphique 18030


Paramètres de type• T : type du résultat final renvoyé par laméthode doInBackground et récupéré avecla méthode get() (Void, avec un Vmajuscule, si rien n’est renvoyé)• V : type des résultats intermédiaires renvoyéspar la méthode doInBackground (Void sirien n’est renvoyé)Constructeur• Un seul constructeur sans paramètre• Remarque : une instance de SwingWorkern’est nest pas réutilisable ; il faut en recréer uneà chaque lancement d’une tâche en arrièreplanRichard Grin Interface graphique 181Richard Grin Interface graphique 182Principales méthodes• void execute() : lance l’exécution dedoInBackground et retourne immédiatement• T doInBackground() : exécute une tâche enarrière-plan• void done() : lancée automatiquement parle SwingWorker juste après la fin del’exécution de doInBackground ; cetteméthode est exécutée par le TDEMéthode get• T get() : récupère le résultat de la méthodedoInBackground• Une variante de get n’attend pas plus qu’uncertain temps indiqué par 2 paramètres ;pparexemple,get(10, TimeUnit.MILLISECONDS);n’attendra pas plus de 10 millisecondesRichard Grin Interface graphique 183Richard Grin Interface graphique 184Exceptions lancées par get• Si get a été interrompue pendant qu’elle était enattente du résultat, elle lance unejava.lang.InterruptedException• Si doInBackground a lancé une exception, getlance une ExecutionException E (paquetagejava.util.concurrent) ; l’exception initiale estdonnée par getCause() de Throwable• Si doInBackground a été interrompue (par cancelétudié dans le transparent suivant), get lance uneCancellationException (java.util.concurrent)Richard Grin Interface graphique 185Méthode cancel• boolean cancel(boolean interrompt) :essaie d’arrêter l’exécution en arrière-plan dedoInBackground– le paramètre est true pour indiquer de stopper latâche en arrière-plan, même si elle a déjà démarré– si le paramètre est à false, une tâche déjàdémarrée n’est pas stoppée ; mais si le messageest envoyé avant le démarrage de cette tâche, latâche ne sera jamais exécutéeRichard Grin Interface graphique 18631


Méthode isCancelled• La méthode isCancelled renvoie true, sila tâche a été interrompue avant sa finnormaleRichard Grin Interface graphique 187Méthodes publish et process• void publish(V… resultsInt) : àutiliser dans doInBackground pour publierdes résultats intermédiaires ; ne pas laredéfinir• void process(List resultsInt) :reçoit les résultats intermédiaires envoyéspar publish (exécuté par le TDE) ; àredéfinir si on souhaite traiter ces résultatsintermédiairesRichard Grin Interface graphique 188Propriétés• Comme avec les Java beans, SwingWorkerpeut communiquer avec d'autres objets parl'intermédiaire de propriétés liées au sensdes Java beans (bounded)• Si un écouteur s’inscrit it commePropertyChangeListener, il reçoit unévénement à chaque fois quefirePropertyChange est lancée pourindiquer qu’une propriété a reçu unenouvelle valeurRichard Grin Interface graphique 189Gestion des écouteurs• voidaddPropertyChangeListener(PropertyChangeListener l) : ajoute un écouteur• voidremovePropertyChangeListener(PropertyChangeListener l) : enlève un écouteur• void firePropertyChange(StringnomPropriété, Object ancienneValeur,Object nouvelleValeur) : avertit lesécouteurs du changement de valeur d’unepropriétéRichard Grin Interface graphique 1902 propriétés prédéfinies• state : prend sa valeur dans l’énumérationSwingWorker.StateValue, PENDING(avant le démarrage de doInBackground),STARTED (après le démarrage dedoInBackground), DONE (après la fin del’exécution de doInBackground)• progress : a une valeur de 0 à 100 ; peutservir à indiquer la progression de la tâcheen arrière-planRichard Grin Interface graphique 191Méthodes pour les propriétés etl’état du SwingWorker• SwingWorker.StateValue getState()• int getProgress()• protected void setProgress(int v)• boolean isCancelled() : true si laméthode cancel a été appelée pourinterrompre la tâche• boolean isDone() : true si la méthodedoInBackground a fini son exécutionRichard Grin Interface graphique 19232


Threads pour les différentes méthodes• execute est exécuté dans le thread courant• doInBackground est exécuté en arrière-plan• process et done et toutes les notificationsdes PropertyChangeListeners sont tlancéspar SwingWorker dans le TDE pour mettre àjour l’interface ; il est donc possible et sûrd’y modifier l’interface graphiqueThreads pour les différentes méthodes• get ne doit être appelé que dans la méthodedone, sinon elle risque de bloquer le TDE• Si on veut bloquer le TDE en attente durésultat final par get, il faut le faireautrement, en affichant une fenêtre dedialogue pour prévenir l’utilisateur (voirjavadoc de get)Richard Grin Interface graphique 193Richard Grin Interface graphique 194Cas d’utilisation (1)• Si l’interface doit charger des ressources avantd’être totalement opérationnelle ; elle peut– charger les ressources dans le threadprincipal (celui qui lance l'interfacegraphique), mais alors l’interface nes’affichera pas tout de suite– faire charger les ressources par le TDE,mais alors l’interface restera figée pendantce chargement• La solution est de faire charger les ressourcespar un swingworkerRichard Grin Interface graphique 195Cas d’utilisation (2)• Le cas d’utilisation le plus fréquent est delancer une tâche longue à exécuter dans unécouteur, à la suite d’une action de l’utilisateur• La tâche longue est exécutée par la méthodedoInBackground• A la suite de cette exécution, si l’interface doitêtre mise à jour, la méthode done s’en charge(exécutée par le TDE)Richard Grin Interface graphique 196Cas d’utilisation (3)• Si l’interface doit être mise à jour durantl’exécution de la tâche, la méthode processs’en charge (exécutée par le TDE)• Remarque : rien n’assure que toutes les tâchesexécutées par process seront exécutées avantle démarrage de done. Si on veut cettefonctionnalité, il faut la programmer (pasfacile, voir cours sur les threads)Richard Grin Interface graphique 197Schéma d’utilisation (début)class Tache extendsSwingWorker() {@Overridepublic Integer doInBackground() {int val = 0; boolean fini = false;while (!fini) {val += calculLong();publish(val);fini = ...;}return val;}Richard Grin Interface graphique 19833


Schéma d’utilisation (suite)@Overridepublic void done() {try {int val = get();// Met à jour le GUI avec la valeur...}// Traiter les exceptions,// en particulier l’interruption de// la tâche (CancellationException)catch(...)}Richard Grin Interface graphique 199Schéma d’utilisation (fin)@Overridepublic void process(List vals) {// Met à jour le GUI avec les valeurs...}} // classe Tache// Utilisation du SwingWorker dans le GUIpublic void actionPerformed(ActionEvente) {Tache tache = new Tache();tache.execute();}Richard Grin Interface graphique 200Séparation du GUI etdes classes métierArchitecture des applicationsavec interface graphiqueRichard Grin Interface graphique 201• Les classes métier doivent être indépendantesdes interfaces <strong>graphiques</strong> qui les utilisent• L’architecture est la suivante :– classes métier (dans un paquetage)– classes pour l’interface graphique (GUI) (dans unautre paquetage)– les écouteurs du GUI font appel aux méthodes desclasses métierRichard Grin Interface graphique 202Classe « principale » schématiquepublic class Application {. . .public static void main(String[] args) {// Initialisation (utilise classes métier). . .// Affiche la fenêtre principale du GUISwingUtilities.invokeLater(...);}} Les traitements de fin éventuels sur les classesmétiers peuvent être placés dans les écouteursau moment de quitter le GUIRichard Grin Interface graphique 203Pattern MVC en SwingRichard Grin Interface graphique 20434


Pattern « fabrique abstraite »• Le changement de look and feel utilise lemodèle de conception (design pattern)« fabrique abstraite » qui permet de choisir unefamille de classes indépendamment du reste del’application• Chaque look and feel correspond à une fabriqued’un certain type de UI-delegate (gérée par leUI-manager)• Pour changer de look and feel, il suffit dechanger de fabriqueRichard Grin Interface graphique 211« Pluggable Look and feel »• Pour changer de look and feel pour l’ensembledes composants contenus dans un composant :UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");// Change pour les composants déjà affichésSwingUtilities.updateComponentTreeUI(fenetre);Richard Grin Interface graphique 212Architecture avec UI-delegate• Le composant– est l’interlocuteur pour les objets extérieurs– contient le code pour les comportements de base ducomposant– reçoit les événements (générés par le système)• Le modèle contient les donnéesArchitecture avec UI-delegate (2)• Le UI-delegate– représente une façon de voir ces données et detraiter les événements– écoute le composant (pour traiter te les événements e entant que contrôleur) ; il contient des classes internesauxquelles il délègue ce traitement– écoute le modèle (pour afficher une vue de cemodèle)Richard Grin Interface graphique 213Richard Grin Interface graphique 214Architecture avec UI delegateEvénementComposantécouteurs internes (parexemple, pour dessinerun bouton « enfoncé »)update >< changeUI delegateEcouteurschange >Un exempled’utilisation des modèles :les listes< getDatachange >Modèle< update update >Richard Grin Interface graphique 215Richard Grin Interface graphique 21636


Utilisation explicite du modèle• Le modèle des composants n’est pastoujours utilisé explicitement dans le code• Il est souvent caché par les implémentationsutilisées dans le cas les plus simples(boutons par exemple)• Il n’est pas caché pour les composantscomme les listes (JList), tables (JTable)ou arbres (JTree)• Dans le cas des listes, l’utilisation explicitedu modèle n'est indispensable que lorsqu’onveut modifier des éléments particuliersRichard Grin Interface graphique 217Listes• Une liste permet de présenter une liste de choixà l’utilisateur :• Celui-ci peut cliquer sur un ou plusieurs choixRichard Grin Interface graphique 218Barre de défilement pour les listes• Le plus souvent une liste a des barres dedéfilement ; pour cela, il faut insérer la listedans un ScrollPane :JScrollPane sList =new JScrollPane(uneListe);• Par défaut, 8 éléments de la liste sontvisibles ; on peut modifier ce nombre :uneListe.setVisibleRowCount(5);Mode de sélection• L’utilisateur peut sélectionner un ou plusieurséléments de la liste suivant le mode de sélectionde la liste :– SINGLE_SELECTION– SINGLE_INTERVAL_SELECTIONINTERVAL SELECTION– MULTIPLE_INTERVAL_SELECTION (mode par défaut)uneListe.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);Richard Grin Interface graphique 219Richard Grin Interface graphique 220Constructeurs simples• 2 constructeurs simples pour des listes :public JList(Object[] listData)public JList(Vector listData)• Il est possible de changer « en bloc » les donnéesdes ces listes par les méthodespublic setListData(Object[] listData)public setListData (VectorlistData)Afficher les éléments des listes• Une liste affiche ses éléments en utilisanttoString() (elle sait aussi afficher desinstances de ImageIcon)• On peut aussi programmer des affichagesparticuliers avec un « renderer »(setCellRenderer(ListCellRenderer))Richard Grin Interface graphique 221Richard Grin Interface graphique 22237


Exemple de listeJList liste = new JList(new String[]{"Un", "Deux", "Trois", "Quatre", …});JScrollPane sp = new JScrollPane(liste);liste.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);// Pour cet exemple, la classe est l'écouteurliste.addListSelectionListener(this);// Ajoute le scrollPane dans le container,// ce qui ajoutera la listec.add(sp);Utilisation standard d’une liste• Il est rare d’écrire un écouteur de liste• L’utilisation standard d’une liste est dedemander à l’utilisateur de cliquer sur un boutonlorsqu’il a fini de faire ses choix dans la liste• On récupère alors la sélection de l’utilisateur parune des méthodes getSelectedValue() ougetSelectedValues() dans la méthodeactionPerformed() de l’écouteur du boutonRichard Grin Interface graphique 223Richard Grin Interface graphique 224Écouteur d’une liste• La classe d’un écouteur de liste doit implémenterl’interface ListSelectionListener qui contientla méthodevoid valueChanged(ListSelectionEvent e)• Attention, une nouvelle sélection engendre 2événements :– un pour désélectionner la précédente sélection– l’autre pour informer de la nouvelle sélection– getValueIsAdjusting() renvoie vrai pour lepremier événement qu’on ignore le plus souventRichard Grin Interface graphique 225Exemple d’écouteur d’une listepublic class EcouteurListeimplements ListSelectionListenerpublic void valueChanged(ListSelectionEvent e) {// On ignore les désélectionsif (e.getValueIsAdjusting()) return;// On traite les sélectionsJList source = (JList)e.getSource();// getSelectedValue() si un seul choix possibleObject[] choix = source.getSelectedValues();for (int i = 0; i < choix.length; i++) {// Ajoute les choix dans une zone de textetextArea.append(choix[i] + "\n");}}}Richard Grin Interface graphique 226Listes avec modèle explicite• Une liste peut être associée à un modèleexplicite qui fournit les données affichéespar la liste• Ce modèle est une instance d’une classe quiimplémente l’interface ListModel• On construit la liste avec le constructeurpublic JList(ListModel modele)Modèle de données pour une listepublic interface ListModel {int getSize();Object getElementAt(int i);i = 0 pour le 1 erélément de la listevoid addListDataListener(ListDataListener l);void removeListDataListener(ListDataListener l);}• Pour faciliter l’écriture d’une classe quiimplémente ListModel, le JDK fournit la classeabstraite AbstractListModel qui implémente les2 méthodes de ListModel qui ajoutent et enlèventles écouteursRichard Grin Interface graphique 227Richard Grin Interface graphique 22838


Listes avec modèle simple• Les plus simples ont un modèle de la classeDefaultListModel qui hérite de la classeAbstractListModel• Avec DefaultListModel on gère lesdonnées avec des méthodes semblables auxméthodes add et remove des collections :add(int, Object),addElement(Object), remove(int),removeElement(Object), clear()Richard Grin Interface graphique 229Exemple de liste modifiable simplepays = new DefaultListModel();pays.addElement("France");pays.addElement("Italie");pays.addElement("Espagne");pays.addElement("Maroc");liste = new JList(pays);Richard Grin Interface graphique 230Listes plus complexes• On peut ne pas vouloir enregistrer tous leséléments de la liste en mémoire centrale• Le modèle héritera alors directement de laclasse AbstractListModelRichard Grin Interface graphique 231Liste modifiable sans enregistrementphysique des données/** Les 1000 premiers entiers composent le* modèle de données de la liste */class Entiers1000 extends AbstractListModel {public int getSize() {return 1000;}public Object getElementAt(int n) {return new Integer(n + 1);}}. . .JList liste = new JList(new Entiers1000());Richard Grin Interface graphique 232Un exemple fréquent d’utilisation• Souvent la valeur affichée par la liste n’estpas celle qui sera directement utilisée par lereste du programme• Par exemple, l’utilisateur sélectionne lenom d’un service et le reste du programmetravaille avec le code de ce serviceUn exemple fréquent d’utilisation (2)• Le plus simple est que la méthodetoString de la classe Service renvoie cequi doit être affiché par la liste• La méthode getSelectedValue varenvoyer une instance de Service et il estalors facile de récupérer le code du serviceen interrogeant cette instanceRichard Grin Interface graphique 233Richard Grin Interface graphique 23439


Classe GraphicsDessiner• L’affichage d’un JComponent est effectué parla méthode paintComponent(Graphics g)• g contient le contexte graphique dans lequel sefait l’affichage• Cette instance est passée en paramètre par lesystème graphique Java à la méthodepaintComponent()Richard Grin Interface graphique 235Richard Grin Interface graphique 236Ce que contient un Graphics• « endroit » où afficher• zone de « clip » (l’utilisateur peut restreindre la zonede travail)• couleur de tracé et couleur « XOR »• fonte• mode de tracé : mode « normal » ou mode « XOR »dans lequel les nouveaux tracés se tracent enchangeant les pixels de la couleur de tracé actuelle enla couleur de XOR, et vice-versaDessiner dans quel composant ?• On peut dessiner dans tout JComponent mais ilest recommandé de dessiner dans un JPanel(avec un JComponent, le fond d’écran risque donc dene pas être repeint lorsqu’il le faut)• Pour dessiner, on crée une classe qui hérite deJPanel, dans laquelle on redéfinit la méthodepaintComponent(), avec le code du dessinRichard Grin Interface graphique 237Richard Grin Interface graphique 238Écriture de la méthodepaintComponent()• La méthode paintComponent(Graphics g)doit avoir « super.paintComponent(g) »comme première instruction• Cela permet à la méthode paintComponent()de la classe JPanel d’effectuer sa part dutravail (en particulier peindre le fond d'écran)Richard Grin Interface graphique 239Précaution à prendre quand onredéfinit paintComponent()• Attention ! il est souvent indispensable deredéfinir les méthodes getPreferredSize()et getMinimumSize() du composant quiredéfinit la méthode paintComponent()(composant.setSize() ne marche pas dansce cas)• On peut aussi utiliser la méthodesetPreferredSize(Dimension) pour celaRichard Grin Interface graphique 24040


Dimensionner un composant• setSize détermine les valeurs des variablesinternes associées à la dimension d’un composant• Cette méthode ne convient pas toujours pourdimensionner un composant interne à une fenêtre• En effet, les gestionnaires de placement utilisentles méthodes get{Preferred|Minimum}Sizequi peuvent ne pas tenir compte des valeursinternes pour l’affichage d’un composant (parexemple un bouton ne tient compte que de la taille deson texte)Richard Grin Interface graphique 241Dessiner avec la classe Graphics• Graphics offre plusieurs méthodes pourafficher divers éléments :– drawString() affiche du texte– drawImage() affiche une image– drawLine() affiche un segment– fillOval() affiche une ellipse– drawPolygon() affiche un polygone–…Richard Grin Interface graphique 242Système de coordonnées• L’ unité est le pixel et l’origine est placée enhaut à gauche du composant dans lequel onaffiche(0, 0)Surface disponible pour l’affichageinsets.leftInsets insets = getInsets();insets.topSurface d’affichagedu composant(on enlève la placepour les bordures)insets.rightxgetHeight()insets.bottomBord du composantRichard Grin Interface graphique 243ygetWidth()Richard Grin Interface graphique 244Surface disponible pour l’affichagepublic void paintComponent(Graphics g) {...Insets insets = getInsets();int largeurDisponible =getWidth() - insets.left - insets.right;int hauteurDisponible =getHeight() - insets.top - insets.bottom;.../* Peindre dans le rectangle défini parles points (insets.left, insets.height) et(getWidth()-insets.right, getHeight()-insets.bottom)*/}Richard Grin Interface graphique 245« Dessiner » du textepublic Fenetre() {. . .this.add(new HelloWorldComponent());. . .}class HelloWorldComponent extends JPanel {@Overridepublic void paintComponent(Graphics g) {super.paintComponent(g);g.drawString("Hello world!", 75, 100);}Ajouter getPreferredSize si nécessaire}Richard Grin Interface graphique 24641


Dessinerpublic void paintComponent(Graphics g) {super.paintComponent(g);g.drawRect(10, 50, 100, 80);Color couleur = g.getColor();g.setColor(Color.green);g.fillRect(20, 60, 50, 40);g.drawOval(5, 40, 110, 110);g.setColor(couleur);g.drawPolygon(new int[] {200, 210, 250, 280, 200},new int[] {110, 120, 60, 200, 300},5);}Richard Grin Interface graphique 247Restreindre la zone de traitement• On peut restreindre la zone sur laquelles’appliqueront les ordres que l’on envoie à unGraphics,• Les méthodes setClip permettent de donnerune zone rectangulaire, ou même une zonequelconque délimitée par une forme (Shape)• Cette zone peut être obtenue parla méthode getClip (getClipBounds pourobtenir le rectangle circonscrit à la forme)Richard Grin Interface graphique 248Classe Graphics2D• En fait, la méthode paintComponent reçoitune instance de la classe Graphics2D, sousclassede Graphics• Graphics2D offre beaucoup plus depossibilités que Graphics• Parmi les possibilités les plus simples, ellepermet les rotations, les mises à l’échelle, lechoix de la largeur de trait, le tracé derectangle 3DClasse Graphics2D• Pour utiliser ces possibilités, il suffit de casteren Graphics2D le paramètre depaintComponent :Graphics2D g2 = (Graphics2D)g;Richard Grin Interface graphique 249Richard Grin Interface graphique 250« Rendre » un Graphics•Un Graphics est une ressource système qu’ilfaut rendre avec la méthode dispose() (classeGraphics) quand on n’en a plus besoin• On ne doit rendre que les Graphics que l’on aobtenu par une méthode telle que getGraphicsou createGraphics• Il faut laisser cette responsabilité aux méthodesappelantes si on travaille avec un Graphics quel’on a reçu en paramètre d’une méthode telle quepaint ou paintComponentJava2D• Les possibilités de base pour dessiner fournies parle SDK sont étendues énormément par Java 2Dapparue avec le SDK 1.2• Java 2D apporte à la fois plus de souplesse et denombreuses nouvelles fonctionnalités• Java 2D utilise des classes de java.awt,java.awt.color, java.awt.font,java.awt.geom, java.awt.image etjava.awt.print• Nous n’étudierons pas Java2D dans ce coursRichard Grin Interface graphique 251Richard Grin Interface graphique 25242


Faire se redessiner un composant• On envoie le message repaint() au composant(hérité de la classe Component)• Pour les dessins complexes, on peut gagner en performance sila zone à redessiner est incluse dans un rectangle, en indiquantà la méthode repaint() le rectangle à redessiner :repaint(x, y, largeur, hauteur)• Si repaint ne suffit pas pour faire affichercorrectement les composants mais que tout s’affichecorrectement après avoir redimensionné la fenêtre, ilfaut sans doute ajouter avant repaint un appel à laméthode revalidate sur le composant à réafficherRichard Grin Interface graphique 253revalidate, invalidate, validate• invalidate() rend « non correct » lecomposant qui reçoit le message, et tous lescomposants qui le contiennent• validate() envoyé à un container, lui indiquequ’il doit réorganiser ses composants (il ne tientcompte que des modifications des composants qui sedéclarent « non corrects »)• revalidate() envoyé à un composant,conjugue un invalidate() du composant et unvalidate() de son containerRichard Grin Interface graphique 254Couleurs et polices de caractères• On peut utiliser les classes Color et Fontpour améliorer la qualité de l’interfaceRichard Grin Interface graphique 255Couleurs et polices de caractèresExemplepublic void paintComponent(Graphics g) {super.paintComponent(g);Font f = new Font("SansSerif", Font.BOLD, 14);Font fi = new Font("SansSerif",Font.BOLD + Font.ITALIC, 14);g.setFont(f);g.setColor(Color.red);setBackground(Color.blue); // couleur de fondg.drawString("Hello world!", 75, 100);g.drawImage(image, 100, 75, null);g.setFont(fi);g.drawString("Hello world!", 75, 200);}Richard Grin Interface graphique 256Calcul de la taille d’un texte• On peut avoir besoin de calculer la taille d’untexte en pixels, par exemple pour le centrer• Centrer un message :de la classe ComponentFontMetrics fm = getFontMetrics(getFont());int hauteurTexte = fm.getHeight();int largeurTexte = fm.stringWidth(msg);g.drawString( msg,(int) ((largeur - largeurTexte) / 2),(int) ((hauteur - hauteurTexte) / 2));• On a besoin d’un Graphics pour obtenir la taille d’untexte (ici celui du Component) car la taille d’uncaractère dépend de l’épaisseur du trait pour le tracerRichard Grin Interface graphique 257Taille utilisable d'un Graphics• Il arrive qu'une méthode reçoive en paramètreune instance de Graphics qui provient d'uncomposant graphique extérieur à la classe de laméthode• On peut récupérer é le rectangle affiché par cetteinstance par la méthode getClipBounds() :public void dessine(Graphics g) {Rectangle r = g.getClipBounds();g.drawLine(0, 0, r.width, r.height);}Trace une diagonaleRichard Grin Interface graphique 25843


ImagesImages• Les images sont des objets complexes(pixels, modèle de représentation des pixels, enparticulier pour les couleurs)• De base, Java sait travailler avec les imagesGIF, JPEG ou PNG, et les GIF animésRichard Grin Interface graphique 259Richard Grin Interface graphique 260Classes pour les images• Elles sont représentées en Java par 3 classes– java.awt.Image : une image de base– java.awt.image.BufferedImage :permet de manipuler les pixels de limage l'image– javax.swing.ImageIcon : correspond àune image de taille fixe utilisée pour décorerun composantRichard Grin Interface graphique 261ObjectImageBufferedImageRelations entre les classesIconImageIconSerializableLa méthode getImage() de la classe ImageIcon fournit uneinstance de ImageUn des (nombreux) constructeurs de ImageIcon prend uneImage en paramètreRichard Grin Interface graphique 262Traitement standard avec une image• On va commencer par des généralités surles images et on étudiera ensuite les 3classes d’images plus en détails1. Récupérer l’image désignée par un nom defichier local ou par un URL local ou distant2. Afficher l’image dans un composant (on nepeut afficher directement t une image dansun container)Richard Grin Interface graphique 263Richard Grin Interface graphique 26444


Récupérer une image• Le chargement d’une image peut être long• Le plus souvent ce chargement doit êtreeffectué par un thread en parallèle pourlibérer le thread de distribution desévénements et ne pas figer l’interfacegraphiqueAffichage d’une image• Lorsque le chargement s’effectue enparallèle, l’image s’affiche petit à petit,comme sur un navigateur Web• Si ce comportement ne convient pas, onpeut utiliser un MediaTraker pour attendrele chargement complet de l’image par lethread ; on n’affiche l’image qu’ensuiteRichard Grin Interface graphique 265Richard Grin Interface graphique 266Afficher une image• Les classes d'images ne dérivent pas de laclasse JComponent ; si on veut afficher uneimage, il faut l'inclure dans un composant•En Swing, on peut l’inclure– dans un JLabel (le plus simple)– mais aussi dans un JPanel, unJComponent ou un JButton• Avec AWT, on inclut les images dans un CanvasRichard Grin Interface graphique 267Désigner une image• Désigner une image locale par l’emplacementde son fichier dans l’arborescence ne marchepas si l’image est dans un fichier jar• C’est une des erreurs les plus fréquemmentrencontrées dans les projets d’étudiants• On pourra récupérer les images qu’elles soientdans un fichier jar ou non, si on désignel’image comme une ressource en utilisant laméthode getResource de la classe ClassRichard Grin Interface graphique 268Exemple : obtenir une ImageIconcomme une ressource• On délègue au chargeur de classes la recherche dufichier qui contient l’image :URL url = getClass().getResource(nomFichier);ImageIcon icone = new ImageIcon(url);• Le chargeur de classe recherche l’image selon sonalgorithme de recherche, typiquement, à partir duclasspath si nomFichier est un nom absolu, ou àpartir de l’emplacement du fichier classe (.class) sile nom est relatifRichard Grin Interface graphique 269Quelle classe choisir ?• Si on veut seulement afficher une image, sansla retoucher, le plus simple est d'utiliserImageIcon• Si lon l'on veut effectuer le chargement d'uneimage en tâche de fond, ou pouvoir modifier lataille de l'image, il faudra utiliser Image• Si on veut faire des traitements sur une image,il est nécessaire d'utiliser BufferedImageRichard Grin Interface graphique 27045


Obtenir une ImageIconImageIcon• Il existe des constructeurs de ImageIcon pourcréer une instance à partir de :– un nom de fichier absolu ou relatif ; leséparateur est « / » quel que soit le système– un URL (adresse Internet ; URL)– une image (Image)– un tableau de byte (byte[])Richard Grin Interface graphique 271Richard Grin Interface graphique 272Afficher une ImageIcondans un JLabelURL url =getClass().getResource("/img/image.gif");Icon icone = new ImageIcon(url);JLabel label = new JLabel(icone);JLabel label2 =new JLabel("Texte label", icone);Obtenir une ImageIcondepuis une appletString nomFichier = "images/image.gif";try {URL url = new URL(getCodeBase(),nomFichier);ImageIcon imageIcon = new ImageIcon(url);}catch (MalformedURLException e) { . . . }Richard Grin Interface graphique 273Richard Grin Interface graphique 274ImageChargement en tâche de fond• Les méthodes getImage des classesApplet ou Toolkit permettent de lier uneinstance de Image à une image ; elles nechargent pas l’image en mémoire• Ces méthodes retournent immédiatement ;elles ne renvoient pas d’erreur si l’imagen’existe pas• L’image sera ensuite chargée petit à petitpar un thread de chargement lorsqu’ondemandera son affichageRichard Grin Interface graphique 275Richard Grin Interface graphique 27646


Savoir si une image est chargée• Pour savoir si une image est déjà chargée, le plus simpleest d’utiliser la méthode checkID de la classeMediaTracker ; on passe le composant qui va utiliser l’imageau mediaTracker (ou un autre composant) :MediaTracker md = new MediaTracker(composant);md.addImage(image, age( age, 0);if (md.checkID(0)) {System.out.println("Chargement terminé");if (md.isErrorID(0))System.err.println("Erreur pendant chargement");}elseSystem.out.println("Pas encore chargée");Richard Grin Interface graphique 283java.awt.MediaTracker• On peut aussi utiliser un MediaTracker pourattendre le chargement complet d’une image•Un mediaTracker peut surveiller le chargement deplusieurs images (chacune a un numéro) :// L’image sera tracée dans le composant compMediaTracker md = new MediaTracker(comp);Image image = Toolkit.getDefaultToolkit().getImage(image);md.addImage(image, 0);try {md.waitForID(0); // ou waitForAll()}catch(InterruptedException e) {}Richard Grin Interface graphique 284Chargement d’une IMageIcon•Une ImageIcon est automatiquement chargéepar un mediatracker quand l’image est créée àpartir d’un nom de fichier ou d’une URL• La méthode getImageLoadStatus() permetde savoir si l’image a pu être chargée• setImageObserver(ImageObserver)permet d’observer le chargement de l’image (peutêtre utile pour les images gif animées)Taille d’une image• Les méthodesgetHeight(ImageObserver observer)getWidth(ImageObserver observer)de la classe Image renvoient les dimensionsde l’image (-1 si l’information i n’est pasencore disponible)• Les méthodes getIconWidth() etgetIconHeight() peuvent être utiliséespour les icônesRichard Grin Interface graphique 285Richard Grin Interface graphique 286Mettre à jour une image• Pour des raisons de performances lesimages sont gardées dans des caches par lesystème d’affichage• Si l’image est modifiée entre 2 affichages, ilpeut être nécessaire d’appeler la méthodeflush :image.flush();BufferedImageRichard Grin Interface graphique 287Richard Grin Interface graphique 28848


Créer et dessiner uneBufferedImage• Classe fille de Image, elle permetd’effectuer des traitements complexes surles images• Elle ne sera pas traitée en détails dans cecours// Créer une BufferedImage (avec un composant)bufferedImage =(BufferedImage)composant.createImage(w, h);// Créer une BufferedImage (sans composant)bufferedImage =new BufferedImage(w, h,BufferedImage.TYPE_INT_RGB);// Dessiner sur l'imageGraphics2D g2d = bufferedImage.createGraphics();dessiner(g2d); // fait des dessins sur g2dg2d.dispose();Richard Grin Interface graphique 289Richard Grin Interface graphique 290Passer de Image à BufferedImageImage img = …;BufferedImage bi = new BufferedImage(img.getWidth(null),img.getHeight(null),i BufferedImage.TYPE_INT_RGB);Graphics g = bi.getGraphics();g.drawImage(img, 0, 0, null);javax.imageioRichard Grin Interface graphique 291Richard Grin Interface graphique 292Paquetage javax.imageio• Ce paquetage permet de charger et sauvegarderfacilement n’importe quel type d’image• Il utilise la notion de plugin pour supporter lesdifférents types d’image• Dans l’API Java de base depuis le JDK 1.4Classe ImageIO• Classe du paquetage javax.imageio• Méthodes static read pour lire depuis unfichier local (File), un flot ou un URL (lecturedirecte sans utilisation d’un thread enparallèle)• Méthodes write pour écrire unBufferedImage dans un fichier ou un flot, enchoisissant le codage d’écritureRichard Grin Interface graphique 293Richard Grin Interface graphique 29449


Codages supportés par ImageIO• En lecture les formats gif, png et jpeg sontsupportés par l’API• En écriture, seuls les formats png et jpegsont supportés• On peut trouver des paquetages sur le Webpour ajouter d’autres formats• Obtenir les formats supportés en lecture :String[] formatNames =ImageIO.getReaderFormatNames();Exemples pour ImageIOURL url =getClass().getResource("/img/image.gif");BufferedImage bi = ImageIO.read(url);. . .String nomFichier = "http://.../truc.gif";bi = ImageIO.read(new URL(nomFichier));Richard Grin Interface graphique 295Richard Grin Interface graphique 296Représenter une actionInterface Action• Il arrive souvent qu’une même action (parexemple, quitter l’application, imprimer, ouvrir unfichier, obtenir de l’aide) puisse être déclenchéepar différents moyens :– choix hi d’un menu– clic sur un bouton de la souris– frappe d'une combinaison de touches au clavier(Ctrl-A)–etc.Richard Grin Interface graphique 297Richard Grin Interface graphique 298Informations centralisées• L’interface Action permet de centraliser– un texte (qui s’affiche sur les boutons ou les menus)– une icône– un traitement à exécuter– le fait que ce traitement est permis ou pas– un texte « actionCommand »– un mnémonique– un raccourci clavier– un texte qui décrit l’action (version longue ou courteutilisée par les bulles d’aide)Richard Grin Interface graphique 299Utilisation des actions• Cette action peut être utilisée par plusieurscomposants de types éventuellement différents• Certains attributs de ces composants sont alorsfixés par l’action– le texte des boutons ou des menus– l'action leur est ajoutée comme ActionListener–…Richard Grin Interface graphique 30050


Classe et interface pour les actions• L’interface javax.swing.Action (hérite deActionListener) permet de représenter une telleaction• On héritera le plus souvent de la classe abstraiteAbstractAction• Des constructeurs de cette classe prennent enparamètres (optionnels) un texte et une icôneRichard Grin Interface graphique 301Interface Action• L’interface Action a les méthodes suivantes– actionPerformed (héritée de ActionListener)– setEnabled et isEnabled indiquent si l’action peutêtre lancée ou non– putValue et getValue permettent d’ajouter desattributs (paire « nom-valeur ») à l’action ; 2 attributsprédéfinis : Action.NAME et Action.SMALL_ICON(utilisés si l’action est associée à un menu ou à une barre d'outils)–{add|remove}PropertyChangeListener pour, parexemple, notifier un menu associé à une action quel’action est invalidéeRichard Grin Interface graphique 302Classes qui peuvent utiliser une action• Ce sont des sous-classes de AbstractButton ;elles ont en particulier un constructeur qui prenden paramètre une action :– Boutons (JButton)– Boutons radio (JRadioButton), y compris dans unmenu (JRadioButtonMenuItem)– Boîtes à cocher (JCheckBox), y compris dans unmenu (JCheckBoxMenuItem)– Menu (JMenu)– Choix de menu (JMenuItem)Richard Grin Interface graphique 303Utilisation des actionsImageIcon image = new ImageIcon("gauche.gif");actionPrecedent =new AbstractAction("Question précédente", image) {public void actionPerformed(ActionEvent e) {. . .}};Définition de l’action. . .JButton bPrecedent = new JButton(actionPrecedent);panel.add(bPrecedent);Utilisation de l’actionbPrecedent.setText(""); // bouton "image" avec tooltipRichard Grin Interface graphique 304Utilisation des actions• On peut associer une action à un bouton (en faità un AbstractButton, c’est-à-dire JButton,JMenuItem, JToggleButton) par la méthodesetAction(Action)• Cette méthode fait tout ce qu’on peut enattendre ; elle ajoute en particulier l’actioncomme écouteur du bouton, met le nom(propriété NAME), l’icône (SMALL_ICON), le textede la bulle d’aide du bouton(SHORT_DESCRIPTION)• La méthode getAction() récupère l’actionRichard Grin Interface graphique 305<strong>Interfaces</strong> <strong>graphiques</strong> dynamiquesRichard Grin Interface graphique 30651


Moyens à utiliser• Il s’agit d’interfaces <strong>graphiques</strong> qui sontmodifiées après un premier affichage• Ces modifications sont le plus souventengendrées par les actions de l’utilisateur• Elles peuvent aussi être provoquées par lechargement d’un certain type de fichier oupar le passage à une nouvelle étape dutraitement• Modifier l’interface graphique à l’aide desméthodes add, remove ou removeAll de laclasse Container• Ensuite, faire afficher la nouvelle interfacepar repaint• Le plus souvent on devra envoyer desrevalidate aux composants qui ont changéd’aspect et des validate sur les containers àqui on a ajouté ou retiré des composantsRichard Grin Interface graphique 307Richard Grin Interface graphique 308Démo• On récupère une démo (avec le code source)des tous les composants quand on récupèrele JDK ; elle est à l’adressejdk1.6.0\demo\jfc\SwingSet2\SwingSet2.jarJava Look and Feel Repository• Oracle fournit un ensemble d’icônes qu’onpeut utiliser dans ses propres interfaces<strong>graphiques</strong> à l’adresse suivante :http://java.sun.com/developer/techDocs/hi/repository/• De nombreuses autres icônes sontdisponibles sur le WebRichard Grin Interface graphique 309Richard Grin Interface graphique 31052

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!