2014-2015 Programmation d’interfaces avec TM* Swing de Java * de ORACLE (http://www.oracle.com/technetwork/java/index.html) 2I007 & LI357 / Université Paris VI 1 Choun Tong LIEU 2014-2015 Plan I. II. Généralité Conteneurs 1. Top-Level 2. Et quelques autres III. Agencement (Layout) IV. Quelques composantes simples V. Listener (écouteur / réflexe) 2I007 & LI357 / Université Paris VI 2 Choun Tong LIEU 2014-2015 I. Généralité Les composantes Swing font partie de JFC (Java TM Foundation Classes) qui regroupe les éléments permettant la création d'interfaces graphiques. Des simples boutons, menus, listes, curseurs, … aux composantes plus sophistiquées file chooser split pane table 2I007 & LI357 / Université Paris VI tree 3 Choun Tong LIEU 2014-2015 Les noms des composantes AWT sont repris en Swing avec le préfixe "J". Par exemple : Button (en AWT) et JButton (en Swing). packages différents : java.awt et javax.swing Contrairement au AWT (Abstract Window Toolkit), Swing est construit avec des codes non natifs look and feel indépendant de la couche inférieure d'interface graphique (X Window System, Windows, Mac OS, etc, …). Plus de fonctionnalité dans une composante Swing que dans la même composante AWT : ● une image dans un bouton Swing, ● une composante Swing n'est pas nécessairement rectangulaire, ● modifier le dessin d'une bordure d'une composante Swing, ● modifier le comportement et l'apparence d'une composante Swing, ● … La classe Component offre aux sous-classes la possibilité de ne pas posséder sa propre fenêtre pour les affichages. Chaque instance qui n'a pas de fenêtre propre affichera alors dans la fenêtre de l'instance parent. 2I007 & LI357 / Université Paris VI 4 Choun Tong LIEU 2014-2015 Généralement, chaque composante AWT (Frame, Panel, Button, etc, …) a sa propre fenêtre. Le JFrame de Swing a une fenêtre, les composantes (JPanel, JButton, etc, …) n'en ont pas. Chaque instance de ces dernières emprunte pour son affichage la fenêtre de l'instance mère (si elle existe, sinon celle de grand-mère, etc, …). 2I007 & LI357 / Université Paris VI 5 Choun Tong LIEU 2014-2015 import java.awt.*; class Exemple_1_1 { public static void main (String argvs[]) { Frame f = new Frame("Exemple_1_1"); Panel p = new Panel(); Button b = new Button("Bouton 1_1"); arbre des widgets arbre des fenêtres f fenêtre de f p fenêtre de p f.setVisible(true); b f.add(p); p.add(b); ... xwininfo: Window id: 0x260003e "Exemple_1_1" } } f p b fenêtre de b Root window id: 0x3f (the root window) (has no name) Parent window id: 0x1001455 (has no name) ... 1 child: 0x2600041 (has no name): () 175x55+-4+-20 +0+0 1 child: 0x2600042 (has no name): () 167x31+4+20 +4+20 1 child: 0x2600047 (has no name): () 85x24+41+5 +45+25 2I007 & LI357 / Université Paris VI 6 Choun Tong LIEU 2014-2015 import javax.swing.*; class Exemple_1_2 { public static JFrame f JPanel p JButton b void main (String argvs[]) { = new JFrame("Exemple_1_2"); = new JPanel(); = new JButton("Bouton 1_2"); f.setVisible(true); f.getContentPane().add(p); p.add(b); ... arbre des widgets arbre des fenêtres f fenêtre de f p b } } xwininfo: Window id: 0x2e00041 "Exemple_1_2" fenêtre de f empruntée par p et par b pour l'affichage Root window id: 0x3f (the root window) (has no name) Parent window id: 0x1001539 (has no name) ... 1 child: 0x2e00044 (has no name): () 175x54+-4+-20 +0+0 2I007 & LI357 / Université Paris VI 7 Choun Tong LIEU 2014-2015 Pas de fenêtre physique pour JPanel et JButton. ● économie de ressources et de gestion pour le serveur; toute la gestion de ces composantes se fait localement côté client. ● plus de possibilités dans le graphisme pour dessiner ces composantes sans être contraint à utiliser le cadre rectangulaire et opaque d'une fenêtre. ● par exemple, pour avoir un bouton transparent : import java.awt.*; import javax.swing.*; class Exemple_1_3 { public static void main (String argvs[]) { JFrame f = new JFrame("Exemple_1_3"); JButton a = new JButton("AAAAAAAAAA"); JButton b = new JButton("BBBBB"); JButton c = new JButton("CCCCC"); ... b.setOpaque(false); ... } } 2I007 & LI357 / Université Paris VI 8 Choun Tong LIEU 2014-2015 II. Conteneurs Top-Level : ● a une fenêtre; elle est en contact avec le gestionnaire de fenêtre (JFrame, JDialog, etc, …) ou en contact avec la fenêtre du navigateur (JApplet, etc, …). JFrame ● f = new JFrame("Exemple_2_1"); contient une seule composante fille instance de la classe JRootPane (voir l'arbre ci-dessous). JRootPane r = f.getRootPane(); ● on peut ajouter directement dans f des composantes par add(). Elles seront ajoutées dans le conteneur contentPane de f. On récupère sa composante contentPane par la méthode Container c = f.getContentPane (); 2I007 & LI357 / Université Paris VI 9 Choun Tong LIEU 2014-2015 avant d'y ajouter une composante par c.add(new JButton("Bouton 2_1")); ● JRootPane ● ● ● ● JRootPane r = f.getRootPane(); instance sans fenêtre, utilisée uniquement pour être fille d'un conteneur Top-Level. à droite, son arbre d'instance et ses filles. contient un glassPane et un layeredPane. 2I007 & LI357 / Université Paris VI 10 absent à la création d’une instance de JFrame Choun Tong LIEU 2014-2015 ● instance glassPane Component g = f.getGlassPane(); sans fenêtre et non affichée par défaut, ● lorsqu'elle est affichée (visible), elle est transparente (glass) en se mettant devant tous les autres composantes et intercepte tous les évènements souris, ● utilisée pour dessiner par-dessus tous les autres composantes, instance layeredPane ● ● JLayeredPane l = f.getLayeredPane(); sans fenêtre et affichée par défaut, ● contient un contentPane et une barre de menu, et les place (voir dessin ci-dessus). instance contentPane ● ● Container c = f.getContentPane(); ● ● ● sans fenêtre et affichée par défaut, sert à contenir tous les composantes ajoutées au conteneur Top-Level, par défaut, l'outil de placement est un BorderLayout permettant de placer les composantes au centre et aux 4 points cardinaux, 2I007 & LI357 / Université Paris VI 11 Choun Tong LIEU 2014-2015 ● Un exemple avec un JFrame : import java.awt.*; import javax.swing.*; class Exemple_2_1 { public static void main (String argvs[]) { JFrame f = new JFrame("Exemple_2_1");; JRootPane r = f.getRootPane(); Component g = f.getGlassPane(); JLayeredPane l = f.getLayeredPane(); Container c = f.getContentPane(); JMenuBar m = f.getJMenuBar(); // == null f.setVisible(true); c.add(new JButton("Bouton 2_1")); ... } } 2I007 & LI357 / Université Paris VI 12 Choun Tong LIEU 2014-2015 ● Un exemple avec un JApplet contenu dans la fenêtre principale d'un navigateur : Exemple_2_2.html <HTML> <HEAD> <META NAME="GENERATOR" Content="Microsoft Visual Studio"> <META HTTP-EQUIV="Content-Type" content="text/html"> <TITLE>Exemple 2_2 avec une Applet</TITLE> </HEAD> <BODY> <!-- Insert HTML here --> <applet code=Exemple_2_2.class name=Exemple_2_2 width=120 height=60 > <param name=background value="008080"> <param name=foreground value="FFFFFF"> </applet> </BODY> </HTML> 2I007 & LI357 / Université Paris VI 13 Choun Tong LIEU 2014-2015 Exemple_2_2.java import javax.swing.*; public class Exemple_2_2 extends JApplet { public void init () { JPanel p = new JPanel(); JTree t = new JTree(); setVisible(true); getContentPane().add(p); p.add(t); } } 2I007 & LI357 / Université Paris VI 14 Choun Tong LIEU 2014-2015 Quelques autres conteneurs : ● JLayeredPane ● par défaut, sans gestionnaire d’agencement (voir ci-dessous), ● place ses filles dans des couches (layers) numérotées (petit numéro derrière grand numéro devant) : profondeur z. ● par position dans les 3 dimensions : x, y et profondeur z. ● ses numéros prédéfinis : ● public static final Integer DEFAULT_LAYER; // 0 ● public static final Integer PALETTE_LAYER; // 100 ● public static final Integer MODAL_LAYER; // 200 ● public static final Integer POPUP_LAYER; // 300 ● public static final Integer DRAG_LAYER; // 400 ● public static final Integer FRAME_CONTENT_LAYER; ● valeur -30000, ● couche utilisée pour mettre le contentPane et le menuBar de JFrame (voir ci-dessus). ● à l’intérieur d’une même couche, les filles sont placées par position (0 devant (nb_filles – 1) derrière). 2I007 & LI357 / Université Paris VI 15 Choun Tong LIEU 2014-2015 import java.awt.*; import javax.swing.*; class Exemple_2_3 { public static void JFrame f JLayeredPane l Container c JMenuBar m JButton b main (String argvs[]) { = new JFrame("Exemple_2_3");; = f.getLayeredPane(); = f.getContentPane(); = new JMenuBar(); = new JButton("Bouton 2_3 Bis"); m.add(new JMenu("ABC")); f.setJMenuBar(m); f.setVisible(true); c.add(new JButton("Bouton 2_3")); b.setSize(b.getPreferredSize()); b.setLocation(50, 50); l.add(b, new Integer(0)); ... } } 2I007 & LI357 / Université Paris VI 16 Choun Tong LIEU 2014-2015 ● JPanel ● sans fenêtre propre, ● ajout des composantes filles par sa méthode add(), ● contient par défaut un gestionnaire d’agencement (layout manager) de la classe FlowLayout pour placer ses filles : placement en lignes comme dans un éditeur de texte, (voir le chapitre III. Agencement ci-dessous) 2I007 & LI357 / Université Paris VI 17 Choun Tong LIEU 2014-2015 III. Agencement (Layout) Pour un conteneur, on change son gestionnaire d’agencement (layout manager) par sa méthode void setLayout (LayoutManager mgr); Par exemple : Container c; SpringLayout l; ... c.setLayout(l); Quelques différentes classes de gestionnaire d’agencement (implémentant l’interface LayoutManager) : 2I007 & LI357 / Université Paris VI 18 Choun Tong LIEU 2014-2015 ● FlowLayout ● comme un éditeur de texte, il remplit horizontalement une ligne de composantes de la gauche vers la droite ou de la droite vers la gauche ou au centre et passe à la ligne suivante s’il n’y a pas assez de place, ● Le choix peut être fait à sa création avec des constantes : ● CENTER (valeur par défaut) ● LEADING alignement par la gauche (resp. droite) si le conteneur adopte pour son contenu l’orientation gauche-droite (resp. droite-gauche) (voir les classes Component et ComponentOrientation avec ses constantes LEFT_TO_RIGHT, RIGHT_TO_LEFT, etc, …) ● LEFT ● RIGHT ● TRAILING, l’inverse de LEADING ou plus tard par sa méthode 2I007 & LI357 / Université Paris VI 19 void setAlignment(int align); Choun Tong LIEU 2014-2015 ● ● Avec des espacements horizontaux (hgap) et verticaux (vgap) possibles entre les éléments BoxLayout ● place les filles de son conteneur en ligne (ne passe pas à la ligne suivante) ou en colonne (ne passe à la colonne suivante), ● avec les choix : ● LINE_AXIS en ligne (et reste en ligne) selon l’orientation gauche-droite, droite-gauche, haut-bas ou bas-haut adoptée par le conteneur, ● PAGE_AXIS même chose que précédemment, mais passe à la ligne ou colonne suivante pour rester dans la page, ● X_AXIS en ligne et de la gauche vers la droite, ● Y_AXIS en colonne et du haut vers le bas. 2I007 & LI357 / Université Paris VI 20 Choun Tong LIEU 2014-2015 ● GridLayout ● comme un tableau (lignes x colonnes), ● le conteneur est divisé en rectangles de même taille, ● ● selon l’orientation gauche-droite, droite-gauche, haut-bas ou bas-haut adoptée par le conteneur, BorderLayout ● place au centre et aux 4 coins cardinaux, ● on spécifie le coin au moment où on ajoute une composante : Panel p; ... p.setLayout(new BorderLayout()); p.add(new Button("South"), BorderLayout.SOUTH); 2I007 & LI357 / Université Paris VI 21 Choun Tong LIEU 2014-2015 ● CardLayout ● Comme un jeu de cartes, une seule composante visible à la fois, ● GridBagLayout ● Toutes les cellules de la même colonne ont la même largeur. ● Toutes les cellules de la même ligne ont la même hauteur. ● On y place une composante avec une contrainte de la classe GridBagConstraints. ● Quelques membres de GridBagConstraints : ● gridx et gridy : indiquent les coordonnées de la cellule en commençant par 0, ● gridwidth et gridheight : indiquent combien de cellules la composante occupe, ● weightx et weighty : indiquent la taille en proportion que la cellule va prendre par rapport à toutes les composantes de la même ligne (de la même colonne). Il y a respect d’une taille minimale : par exemple, pour un label, tout le texte devrait être visible si possible. La valeur 0 indique que la cellule doit suivre les autres. 2I007 & LI357 / Université Paris VI 22 Choun Tong LIEU 2014-2015 ● ● fill peut prendre comme valeur ● NONE : indique que la composante n’est pas obligée de s’agrandir pour remplir toute la surface de la cellule, ● HORIZONTAL : la composante occupe toute la largeur de la cellule, ● VERTICAL : la composante occupe toute la hauteur de la cellule, ● BOTH : la composante occupe toute la surface de la cellule. anchor : dans le cas où la composante est plus petite que la cellule, elle peut prendre comme valeur ● CENTER : la composante reste au milieu (par défaut), ● NORTH : au nord, ● SOUTHWEST ● ... 2I007 & LI357 / Université Paris VI 23 Choun Tong LIEU 2014-2015 Panel p; GridBagLayout gridbag; GridBagConstraints c; Button button; ... p.setLayout(gridbag); ... c.fill = GridBagConstraints.BOTH; c.weightx = 1.0; ... button = new Button("Button1"); gridbag.setConstraints(button, c); p.add(button); button = new Button("Button2"); gridbag.setConstraints(button, c); p.add(button); ... 2I007 & LI357 / Université Paris VI 24 Choun Tong LIEU 2014-2015 ● SpringLayout ● contrairement aux autres classes de layout qui viennent de AWT, SpringLayout vient du Swing. ● ça commence avec l’arithmétique sur les Spring : ● une instance de Spring comporte 3 valeurs : minimum, préférée et maximum, ● si on le note par un triplet [a,b,c] avec a b c, alors on peut définir l’arithmétique suivante : [a1, b1, c1] + [a2, b2, c2] -[a, b, c] max([a1, b1, c1], [a2, b2, c2]) [a1, b1, c1] - [a2, b2, c2] min([a1, b1, c1], [a2, b2, c2]) ● = = = = = [a1 + a2, b1 + b2, c1 + c2] [-c, -b, -a] [max(a1, a2), max(b1, b2), max(c1, c2)] [a1, b1, c1] + (-[a2, b2, c2]) -max(-[a1, b1, c1], -[a2, b2, c2]) et les méthodes de la classe Spring pour ces opérations : 2I007 & LI357 / Université Paris VI 25 Choun Tong LIEU 2014-2015 // Pour définir une constante Spring avec ces trois valeurs static Spring constant (int min, int pref, int max); // Même chose avec min = pref = max static Spring constant (int pref); static Spring minus (Spring s); static Spring max (Spring s1, Spring s2); static Spring sum (Spring s1, Spring s2); ● ● ● ● permet, entre autre, aux composantes sœurs de s'attacher bord contre bord entre elles ou au conteneur mère avec des contraintes, ● EAST, WEST, NORTH et SOUTH. peut donc forcer une composante à changer de taille, permet aussi les composantes de se placer par position (avec des "Spring" contantes, voir ci-dessous), peut remplacer la plus part des "layout" ci-dessus. 2I007 & LI357 / Université Paris VI 26 Choun Tong LIEU 2014-2015 ● quelques méthodes : // permet de récupérer le Spring qui contrôle le bord edgeName de c par rapport // au bord haut ou gauche de sa mère Spring getConstraint (String edgeName, Component c); // permet de récupérer l'instance contenant les contraintes affectée à c SpringLayout.Constraints getConstraints (Component c); // permet de lier le bord e1 de c1 au bord e2 de c2 avec un espace // constant pad void putConstraint (String e1, Component c1, int pad, String e2, Component c2); // même chose, mais avec un Spring void putConstraint (String e1, Component c1, Spring s, String e2, Component c2); 2I007 & LI357 / Université Paris VI 27 Choun Tong LIEU 2014-2015 ● SpringLayout.Constraints ● classe de constrainte utilisée par SpringLayout et interne à SpringLayout, ● comme un rectangle avec ses propriétés x, y, largeur et hauteur, ● à la place des entiers, ces propriétés ont comme valeur des Spring, ● ses quelques constructeurs et méthodes : // Constructeur créant un objet vide de contrainte avec positionnement à (0, 0) SpringLayout.Constraints (); // Constructeur créant des contraintes en x et y et 0 pour la largeur et hauteur SpringLayout.Constraints (Spring x, Spring y); // Constructeur créant des contraintes en x, y, largeur et hauteur SpringLayout.Constraints (Spring x, Spring y, Spring width, Spring height); 2I007 & LI357 / Université Paris VI 28 Choun Tong LIEU 2014-2015 // mettre une contrainte en x void setX (Spring x); // mettre une contrainte sur la hauteur void setHeight (Spring x); // mettre une contrainte sur un des bords de la composante avec edgeName pris // parmi les SpringLayout.NORTH, SpringLayout.SOUTH, // SpringLayout.EAST et SpringLayout.WEST void setConstraint (String edgeName, Spring s); ● Un exemple permettant de placer le Bouton 1 en haut à gauche de son conteneur, le Bouton 3 en bas à droite et le Bouton 2 en diagonale entre les deux avec la particularité suivante lors du changement de taille du conteneur : ● ● ● Bouton 1 reste en haut à gauche, Bouton 3 reste en bas à droite, Bouton 2 change de taille pour que ses bords haut et gauche (resp. bas et droit) restent collés à Bouton 1 (resp. Bouton 3). 2I007 & LI357 / Université Paris VI 29 Choun Tong LIEU 2014-2015 import java.awt.*; import javax.swing.*; class Exemple_3_1 { public static void main (String argvs[]) { JFrame f = new JFrame("Exemple_3_1");; Container c = f.getContentPane(); SpringLayout l = new SpringLayout(); JButton b1 = new JButton("Bouton 1"); JButton b2 = new JButton("Bouton 2"); JButton b3 = new JButton("Bouton 3"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); c.setLayout(l); c.add(b1); c.add(b2); c.add(b3); b2.setBackground(Color.RED); l.getConstraints(b1).setX(Spring.constant(0)); l.getConstraints(b1).setY(Spring.constant(0)); 2I007 & LI357 / Université Paris VI 30 Choun Tong LIEU 2014-2015 l.putConstraint(SpringLayout.SOUTH, b3, 0, SpringLayout.SOUTH, c); l.putConstraint(SpringLayout.EAST, b3, 0, SpringLayout.EAST, c); l.getConstraints(b2).setX(l.getConstraint(SpringLayout.EAST, b1)); l.getConstraints(b2).setY(l.getConstraint(SpringLayout.SOUTH, b1)); l.getConstraints(b2).setWidth( Spring.sum(l.getConstraints(b3).getX(), Spring.minus(l.getConstraints(b2).getX()))); l.getConstraints(b2).setHeight( Spring.sum(l.getConstraints(b3).getY(), Spring.minus(l.getConstraints(b2).getY()))); f.pack(); f.setVisible(true); ... } } 2I007 & LI357 / Université Paris VI 31 Choun Tong LIEU 2014-2015 IV. Quelques composantes simples JButton ● On peut utiliser une image à la place d’un texte. import java.awt.*; import javax.swing.*; class Exemple_4_1 { public static void main (String argvs[]) { JFrame f; JButton b; f = new JFrame("Exemple_4_1"); f.setVisible(true); b = new JButton(new ImageIcon("image_4_1.jpg")); f.getContentPane().add(b); ... } } 2I007 & LI357 / Université Paris VI 32 Choun Tong LIEU 2014-2015 ● Avec un texte en plus : b = new Jbutton("Exemple_4_1", new ImageIcon("image_4_1.jpg")); JSplitPane ● permet de partager son espace d’affichage en 2 (vertical ou horizontal) pour afficher 2 composantes, ● peut avoir une barre de séparation ajustable. 2I007 & LI357 / Université Paris VI 33 Choun Tong LIEU 2014-2015 import java.awt.*; import javax.swing.*; class Exemple_4_2 { public static void main (String argvs[]) { JFrame f; JSplitPane s; f = new JFrame("Exemple_4_2"); f.setVisible(true); s = new JSplitPane(); s.setLeftComponent(new JLabel(new ImageIcon("image_4_2.jpg"))); s.setRightComponent(new JLabel(new ImageIcon("image_4_3.jpg"))); s.setOneTouchExpandable(true); f.getContentPane().add(s); ... } } 2I007 & LI357 / Université Paris VI 34 Choun Tong LIEU 2014-2015 V. Listener (écouteur / réflexe) Réflexes de haut niveau en réaction à une succession d'événements de bas niveau, par exemple pour un curseur (JSlider) : ButtonPress + MotionNotify + ButtonRelease → ChangeListener (méthode stateChanged() ) Les Swing héritent de java.awt.Component → Comportements de base propres à un composant (Component) : ● ComponentListener : pour réagir aux changements de taille, position et visibilité. ● FocusListener : pour réagir aux changements de focus de l'entrée clavier. ● HierarchyBoundsListener : pour réagir quand un ancêtre change de taille ou de position. ● HierarchyListener : pour réagir aux changements d'ancêtres. ● InputMethodListener : pour réagir aux entrées texte, à ne pas confondre avec les touches clavier du KeyListener ci-dessous. 2I007 & LI357 / Université Paris VI 35 Choun Tong LIEU 2014-2015 ● ● ● ● KeyListener : pour réagir aux touches clavier. MouseListener : pour réagir aux boutons, aux entrées/sorties de la souris. MouseMotionListener : pour réagir aux déplacements de la souris. MouseWheelListener : pour réagir aux mouvements de la roulette de la souris. 2I007 & LI357 / Université Paris VI 36 Choun Tong LIEU 2014-2015 Et chaque composant Swing possède ses propres réflexes : 2I007 & LI357 / Université Paris VI 37 Choun Tong LIEU 2014-2015 2I007 & LI357 / Université Paris VI 38 Choun Tong LIEU 2014-2015 Quelques composants : • Pour chaque composant, il y a plusieurs types d'écouteurs/réflexes communs et spécifiques. • Pour tout composant, on peut y ajouter une ou plusieurs écouteurs/réflexes. • JButton : (composant simple) - hérite de javax.swing.AbstractButton (classe abstraite) • public void addActionListener(ActionListener l) • public void removeActionListener(ActionListener l) • public ActionListener[] getActionListeners() - avec l'écouteur/réflexe principal ActionListener (interface) • la seule méthode void actionPerformed(ActionEvent e) - rien n'empêche de réagir aux événements de bas niveau avec l'écouteur/réflexe MouseListener (interface) 2I007 & LI357 / Université Paris VI 39 Choun Tong LIEU 2014-2015 import java.awt.*; import java.awt.event.*; import javax.swing.*; class Exemple_5_1 { public static void main JFrame f JPanel p JButton b MonActionListener a MonMouseListener m (String argvs[]) { = new JFrame("Exemple_5_1"); = new JPanel();; = new JButton("Bouton 5_1"); = new MonActionListener(); = new MonMouseListener(); f.setVisible(true); f.getContentPane().add(p); p.add(b); b.addActionListener(a); b.addMouseListener(m); } } 2I007 & LI357 / Université Paris VI 40 Choun Tong LIEU 2014-2015 class MonActionListener implements ActionListener { public void actionPerformed (ActionEvent e) { System.out.println("Press + Release"); } } class MonMouseListener extends MouseAdapter { public void mousePressed (MouseEvent e) { System.out.println("Press"); } public void mouseReleased (MouseEvent e) { System.out.println("Release"); } public void mouseEntered (MouseEvent e) { System.out.println("Enter"); } public void mouseExited (MouseEvent e) { System.out.println("Exit"); } } 2I007 & LI357 / Université Paris VI 41 Choun Tong LIEU 2014-2015 • JFileChooser : (composant composé un peu plus complexe) - avec l'écouteur/réflexe principal ActionListener (interface) • la seule méthode void actionPerformed(ActionEvent e) - qui ne sera pas appelée systématiquement à chaque fois que l'on clique sur les boutons affichés. C'est selon le contexte. - Des comportements par défaut sans appeler cette méthode : en cliquant sur un répertoire, etc, ... - Par exemple, en cliquant sur le bouton « Ouvrir », la méthode n'est appelée que si le champs « Nom de fichier : » est rempli et on trouve dans l'événement « e » les informations « APPROVE_OPTION » et le nom absolu du fichier sélectionné. Par contre, la méthode est appelée à chaque fois que l'on clique dans le bouton « Annuler ». 2I007 & LI357 / Université Paris VI 42 Choun Tong LIEU 2014-2015 2I007 & LI357 / Université Paris VI 43 Choun Tong LIEU 2014-2015 • JTree : (composant complexe et sophistiqué) - des écouteurs/réflexes complexes : • public void addTreeSelectionListener ( TreeSelectionListener tsl) appelé si un nœud est sélectionné ou dé-sélectionné. • public void addTreeExpansionListener( TreeExpansionListener tel) appelé si l'arbre est changé. • public void addTreeWillExpandListener( TreeWillExpandListener tel) appelé juste avant le changement. 2I007 & LI357 / Université Paris VI 44 Choun Tong LIEU 2014-2015 Réagir aux changements des propriétés : • Pour chaque composant (instance), il existe des propriétés portant chacun un nom distinct (par exemple « font », « background », « foreground », etc, ...). • Toute modification de valeur de ces propriétés provoque l'exécution des réflexes associées. • Pour associer ces écouteurs/réflexes à toutes les propriétés (prédéfinies ou définies par l'utilisateur) public void addPropertyChangeListener( PropertyChangeListener listener) • Ou à une propriété nommée (prédéfinie ou définie par l'utilisateur) public void addPropertyChangeListener( String propertyName, PropertyChangeListener listener) 2I007 & LI357 / Université Paris VI 45 Choun Tong LIEU 2014-2015 import import import import java.awt.*; java.awt.event.*; javax.swing.*; java.beans.*; class Exemple_5_3 { public static void main (String argvs[]) { JFrame f = new JFrame("Exemple_5_3"); JPanel p = new JPanel();; JButton b = new JButton("Bouton 5_3"); MonActionListener a = new MonActionListener(); MonPropertyChangeListener prcl = new MonPropertyChangeListener(); f.setVisible(true); f.getContentPane().add(p); p.add(b); b.addActionListener(a); b.addPropertyChangeListener(prcl); } } 2I007 & LI357 / Université Paris VI 46 Choun Tong LIEU 2014-2015 class MonActionListener implements ActionListener { public void actionPerformed (ActionEvent e) { ((JButton)(e.getSource())).setBackground(Color.red); } } class MonPropertyChangeListener implements PropertyChangeListener { public void propertyChange (PropertyChangeEvent evt) { System.out.println(evt); } } 2I007 & LI357 / Université Paris VI 47 Choun Tong LIEU 2014-2015 • Il est possible de provoquer ces exécutions par la méthode de l'objet public void firePropertyChange( String propertyName, ...) • L'utilisateur peut définir de nouvelles propriétés liées à un composant (instance) public final void putClientProperty( Object key, Object value) 2I007 & LI357 / Université Paris VI 48 Choun Tong LIEU 2014-2015 import import import import java.awt.*; java.awt.event.*; javax.swing.*; java.beans.*; class Exemple_5_4 { public static void main (String argvs[]) { JFrame f = new JFrame("Exemple_5_4"); JPanel p = new JPanel();; JButton b = new JButton("Bouton 5_4"); MonActionListener a = new MonActionListener(); MonPropertyChangeListener prcl = new MonPropertyChangeListener(); f.setVisible(true); f.getContentPane().add(p); p.add(b); b.addActionListener(a); b.putClientProperty("koukou", "valeur de koukou"); b.addPropertyChangeListener("koukou", prcl); } } 2I007 & LI357 / Université Paris VI 49 Choun Tong LIEU 2014-2015 class MonActionListener implements ActionListener { public void actionPerformed (ActionEvent e) { ((JButton)(e.getSource())). firePropertyChange("koukou", 0, 1); } } class MonPropertyChangeListener implements PropertyChangeListener { public void propertyChange (PropertyChangeEvent evt) { System.out.println(evt); } } 2I007 & LI357 / Université Paris VI 50 Choun Tong LIEU 2014-2015 • Pour que les événements (asynchrones) soient (mieux) traités, il faut éviter de laisser exécuter ces composants (le principal) dans un thread normal (qui fonctionne mieux avec synchronisation). Il vaut mieux le confier à l'EDT (Event Dispatch Thread) : 2I007 & LI357 / Université Paris VI 51 Choun Tong LIEU 2014-2015 class Exemple_5_5 { public static void main (String argvs[]) { SwingUtilities.invokeLater(new Runnable() { public void run() { JFrame f = new JFrame("Exemple_5_5"); JPanel p = new JPanel();; JButton b = new JButton("Bouton 5_5"); MonActionListener a = new MonActionListener(); f.setVisible(true); f.getContentPane().add(p); p.add(b); b.addActionListener(a); } }); } } 2I007 & LI357 / Université Paris VI 52 Choun Tong LIEU 2014-2015 Les Swing héritent de java.awt.Component. Et dans Component, • à chaque événement reçu, la méthode protected void processEvent(AWTEvent evenement) est automatiquement appelée, • pour choisir les événements à traiter : protected final void enableEvents(long masques_evenements) • en général, on n'a pas à appeler enableEvents() et à choisir les masques d'événements, car à chaque fois que l'on ajoute un listener, le ou les masques d'événements correspondants sont automatiquement ajoutés, • elle est utile pour les classes héritées pour choisir des événements particuliers. Selon les événements (donc leur listener correspondant) à traiter et par défaut, processEvent() appelle automatiquement la méthode process???Event() appropriée : 2I007 & LI357 / Université Paris VI 53 Choun Tong LIEU 2014-2015 • protected void processComponentEvent(ComponentEvent e) • • • • • • • • protected protected protected protected protected protected protected protected void void void void void void void void processFocusEvent(FocusEvent e) processKeyEvent(KeyEvent e) processMouseEvent(MouseEvent e) processMouseMotionEvent(MouseEvent e) processMouseWheelEvent(MouseWheelEvent e) processInputMethodEvent(InputMethodEvent e) processHierarchyEvent(HierarchyEvent e) processHierarchyBoundsEvent(HierarchyEvent e) Pour cela, on ajoute le ou les listeners correspondants : • public void add???Listener(???Listener l) On peut aussi le ou les retirer : • public void remove???Listener(???Listener l) 2I007 & LI357 / Université Paris VI 54 Choun Tong LIEU 2014-2015 import import import import java.lang.*; javax.swing.*; java.awt.AWTEvent; java.awt.event.*; class Exemple_5_6 { public static void main (String argvs[]) { JFrame f = new JFrame("Exemple_5_6"); MonBouton b = new MonBouton("Mon Bouton"); b.addActionListener(new MonAction()); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.add(b); f.pack(); f.setVisible(true); } } 2I007 & LI357 / Université Paris VI 55 Choun Tong LIEU 2014-2015 class MonBouton extends JButton { MonBouton (String s) { super(s); } protected void processEvent (AWTEvent e) { super.processEvent(e); System.out.println("Evenement recu : " + e.getID()); } } class MonAction implements ActionListener { public void actionPerformed (ActionEvent e) { System.out.println("Bouton clic."); } } 2I007 & LI357 / Université Paris VI 56 Choun Tong LIEU 2014-2015 • Pour un bouton, on ajoute un actionListener qui réagit au click de la souris, mais aussi aux touches choisies pour ce bouton. • On ajoute pour cela ce listener par la méthode public void addActionListener(ActionListener l) • Si on enlève l'instruction super.processEvent(e); , non seulement la méthode actionPerformed(ActionEvent e) n'est pas appelée, mais en plus l'aspect graphique du bouton enfoncé n'est pas honoré. Rien ne fonctionne. 2I007 & LI357 / Université Paris VI 57 Choun Tong LIEU 2014-2015 • Un Listener est une interface : ⁃ Interface EventListener : racine de tout Listener, sans aucune méthode, c'est juste un tag (marquage). ⁃ Interface ActionListener : la seule méthode void actionPerformed(ActionEvent e) ⁃ Interface ChangeListener : la seule méthode public void stateChanged (ChangeEvent e) ⁃ ... ⁃ Interface WindowListener : avec plusieurs méthodes ➢ void windowActivated(WindowEvent e) ➢ ... ➢ void windowOpened(WindowEvent e) En général, pour un Listener avec une ou plusieurs méthodes, il y a un Adapter correspondant (classe implémentant ce Listener) où toutes ces méthodes sont définies avec un comportement par défaut. Cela permet au programmeur de créer une classe héritée de ce Adapter en réécrivant une ou plusieurs de ces méthodes pour des besoins particuliers. Par exemple : ➢ Class WindowAdapter 2I007 & LI357 / Université Paris VI 58 Choun Tong LIEU 2014-2015 ... ➢ Class ContainerAdapter • Attention : on peut trouver le même Listener utilisé dans différentes composantes avec bien sûr un comportement différent. • Par exemple : ⁃ public void addActionListener(ActionListener l) • JButton, JCheckBox, JComboBox, JfileChooser, etc, ... ⁃ public void addChangeListener(ChangeListener l) •JButton, JSlider, JSpinner, etc, ... • Interaction des composantes « top-level » avec le gestionnaire de fenêtre. Par exemple, avec un JFrame : JFrame f = new JFrame("Exemple_5_6"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); • On aura le même effet en utilisant la méthode de JFrame public void addWindowListener(WindowListener l); • Et on redéfinit pour WindowListener la ou les méthodes avec System.exit(0) ➢ void windowClosing(WindowEvent e) ➢ void windowClosed(WindowEvent e) ➢ 2I007 & LI357 / Université Paris VI 59 Choun Tong LIEU
© Copyright 2024