CNAM de Nice – « algo-prog TPA1 » F. Besnard interfaces graphiques en java Dans un programme, on appelle "interface graphique" l'ensemble des composants graphiques (fenêtres, boutons, icônes, menus, …) permettant d’avoir une présentation et un dialogue plus agréable pour l'utilisateur que les anciens modes purement textuels. Il existe en java deux packages de gestion d'interface graphique: l'Abstract Window Toolkit (java.awt), et SWING (javax.swing). Le deuxième est plus récent que le premier (et s'appuie d'ailleurs sur lui); aussi est-ce SWING que nous utiliserons. Les concepts de base sont partagés, et on peut utiliser simultanément les deux packages; pour qu'il n'y ait pas de confusion au niveau des noms, chaque classe de SWING est préfixée par la lettre "J". Par exemple, la classe gérant une fenêtre principale est java.awt.Frame dans l'AWT, et javax.swing.JFrame dans SWING. A. Fenêtre minimale On crée une instance de la classe JFrame, que l'on rend visible par l'appel des méthodes "pack" et "setVisible": package TP7; Dans le dossier TP7: import javax.swing.JFrame; Compilation: javac –classpath .. Main.java class Main { public static void main(String[] args) { JFrame frame = new JFrame(); frame.pack(); frame.setVisible(true); } } Exécution: java -classpath .. TP7.Main Résultat: il n'apparaît que la barre de titre, car nous n'avons rien mis à l'intérieur. De plus, on ne peut fermer en cliquant sur la croix: il faut taper "ctrl-c" dans la fenêtre DOS. B. On ajoute un contenu On veut rajouter des objets graphiques, tel qu'un texte (JLabel) ou un bouton (JButton). Les objets graphiques doivent être placés dans un conteneur (JPanel). Enfin, les différents conteneurs doivent être placés dans le conteneur principal de la fenêtre, que l'on obtient à partir de sa méthode "getContentPane". On préfèrera l'approche de droite, orientée objet (on crée une classe Frame1 dérivant de JFrame) à celle de gauche plus classique, basée uniquement sur la composition: sujet-tp7-intro-swing.sxw -1- 04/04/23 CNAM de Nice – « algo-prog TPA1 » F. Besnard package TP7; Import javax.swing.*; Import java.awt.Container; Package TP7; Import javax.swing.*; Import java.awt.Container; class Main { public static void main(String[] args) { Public class Frame1 JFrame frame = new JFrame(); extends javax.swing.JFrame { JPanel pane = new JPanel(); Public Frame1() { pane.add( new JLabel("un") ); Container container = getContentPane(); pane.add( new JButton("deux") ); JPanel pane = new JPanel(); frame.pack(); pane.add( new JLabel("un") ); frame.setVisible(true); pane.add( new JButton("deux") ); frame.getContentPane().add( pane ); container.add( pane ); } } } } package TP7; class Main { public static void main(String[] args) { TP7.Frame1 frame = new TP7.Frame1(); frame.pack(); frame.setVisible(true); } } Résultat: C. Classes internes En poussant le concept de réutilisation par dérivation jusqu'au bout, on crée aussi une classe pour "pane", dérivant de JPanel. Pour éviter la multiplication de (petites) classes et donc de fichiers, on peut utiliser le principe de classes internes (inner classes), permettant à une classe d'être définie à l'intérieur d'une autre (et donc ne créant pas un nouveau fichier): sujet-tp7-intro-swing.sxw package TP7; import javax.swing.*; public class Frame2 extends JFrame { // inner class (panel) // ------------------class Pane2 extends JPanel { public Pane2() { add( new JLabel("un") ); add( new JButton("deux") ); } } } // now, the Frame2 constructor // --------------------------public Frame2() { getContentPane().add( new Pane2() ); } -2- 04/04/23 CNAM de Nice – « algo-prog TPA1 » F. Besnard D. Gestion des évènements La gestion des évènements en java se fait depuis la version 1.1 à l’aide d’interfaces « écouteuses » (listener), où il faut implémenter certaines méthodes, invoquées lors d’un évènement particulier. L’objet instance d’un listener doit être lié à l’objet graphique origine de l’évènement par la méthode « addListener ». Pour chaque type de composant graphique il existe des listeners prédéfinis. Par exemple, si l’on veut écrire « ouch » sur la sortie standard lorsqu’on clique sur un bouton, il faut d’abord créer une classe implémentant la méthode « actionPerformed » de l’interface « ActionListener » : class ButtonAction implements ActionListener { public void actionPerformed(ActionEvent e) { System.out.println("ouch") ; } } … puis enregistrer cette classe comme « écouteuse » des évènements générés par le bouton : Jbutton b = new Jbutton("click me !") ; b.addActionListener( new ButtonAction() ) ; note : certaines interfaces listener comportent de nombreuses méthodes à implémenter. Si l’on veut n’en implémenter qu’une, on utilisera une classe « adapter » prédéfinie, proposant des implémentations par défaut. Ainsi, pour pouvoir fermer la fenêtre en cliquant sur l’icône « X » en haut à droite, on dérive une classe de WindowAdapter, en ne réécrivant que la méthode « windowClosing » : class WinCloser extends WindowAdapter { public void windowClosing(WindowEvent e) { System.exit(0); } } puis frame.addWindowListener(new WinCloser()); sujet-tp7-intro-swing.sxw -3- 04/04/23 CNAM de Nice – « algo-prog TPA1 » F. Besnard E. Exemple : intervertir les textes On veut intervertir les deux textes "un" et deux" lorsqu'on clique sur le bouton. On utilise pour cela les méthodes « getText » et « setText » disponibles dans les classes JLabel et JButton. package TP7; import javax.swing.*; import java.awt.event.*; public class Frame3 extends JFrame { private JLabel _label; private JButton _button; // inner class (panel) // ------------------class Pane3 extends JPanel { Pane3() { _label = new JLabel("un"); _button = new JButton("deux"); _button.addActionListener( new ButtonAction() ); add( _label ); add( _button ); } } // code to swap the two texts // -------------------------class ButtonAction implements ActionListener { public void actionPerformed(ActionEvent e) { String s = _label.getText(); _label.setText( _button.getText() ); _button.setText( s ); } } // code to close the window // -----------------------class WinCloser extends WindowAdapter { public void windowClosing(WindowEvent e) { System.exit(0); } } // now, the Frame3 constructor // --------------------------public Frame3() { getContentPane().add( new Pane3() ); addWindowListener(new WinCloser()); } } // end class Frame3 F. Exercice écrire une classe ayant les fonctionnalités suivantes: - il y a deux boutons "+", "-", et un label affichant un entier; - lorsqu'on clique sur ces boutons, l'entier est incrémenté ou décrémenté; - l'entier doit rester dans un intervalle, dont les bornes sont données au constructeur; - l'entier est initialisé à la moyenne des deux bornes. sujet-tp7-intro-swing.sxw -4- 04/04/23