C'est la deuxième partie du mon tutoriel sur la création d'interface graphique en Swing.
Voici la deuxième partie du mon tutoriel consacré à Swing. Dans la première partie, nous avons vu comment ouvrir des fenêtres et y placer des composants. C'était un bon début mais il manque un peu de réactivité! En effet, on va voir comment gérer les événements, c'est à dire comment réagir aux différentes interactions avec l'interface. Par exemple, comment traiter le fait que l'utilisateur clique sur un bouton ? etc...
Concepts abordés :
- les listeners
- la programmation événementielle
- ActionListener, WindowListener etc...
Swing utilise une architecture par événements. En effet, lorsque un composant de l'interface graphique le désire (enfin, lorsque une interaction se produit), il peut provoquer un événement. Un événement est ensuite transmis aux objets qui désirent être informés de l'événement et réagir en conséquence. Ces objets sont des listeners (des "écouteurs"). Pour définir un listener, il suffit de créer une classe qui implémente une interface de type EventListener (http://java.sun.com/j2se/1.5.0/docs/api/java/util/EventListener.html pour voir la liste des listeners disponibles). Ensuite, ce listener devra être enregistré dans le composant de l'interface graphique dans lequel le listener veut "écouter". Le composant dispatche l'événement à tout ses listeners enregistrés.
Concrètement, si on veut être avertit que l'utilisateur clique sur un bouton (JButton), on devra implanter une classe implantant un ActionListener http://java.sun.com/j2se/1.5.0/docs/api/java/awt/event/ActionListener.html) (un ActionListener est un EventListener pour les JButton) et ajouter ce listener au JButton.
En pratique :
//création du bouton JButton but=new JButton("ici"); //Ajout de l'action listener but.addActionListener(listener);
L'objet listener devra implémenter l'interface ActionListener : il devra donc obligatoirement contenir la fonction actionPerformed. Cette fonction sera la fonction qui sera appellée lorsqu'un événement se produit sur le bouton
Voici un exemple complet :
import javax.swing.*; import java.awt.*; import java.awt.event.*; //le listener est la classe test public class test extends JFrame implements ActionListener { /** Constructeur de test */ public test() { //titre de la fenetre super("Test"); //panel JPanel pan=new JPanel(); pan.setLayout(new BorderLayout()); //bouton ici JButton but=new JButton("Ici !"); //ajoute un listener : ici le listener est cette classe but.addActionListener(this); //ajoute le boutton dans le panel pan.add(but,BorderLayout.CENTER); // setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setContentPane(pan); pack(); //permet de mettre une bonne dimension a la fenetre setVisible(true); } /** * obligatoire car test implémente l'interface ActionListener */ public void actionPerformed(ActionEvent e) { //quand on a cliqué sur le bouton ici System.out.println("Ici !"); } public static void main(String args[]) { new test(); } }
Copiez ce code dans un fichier test.java puis compilez en faisant javac test.java -classpath ".;"
Pour lancez le programme, faites java test.
Dans cet exemple, le listener est la classe elle-même. On respecte bien l'interface ActionListener puisque la fonction actionPerformed est définie. Quand on clique sur le bouton "ici", un message apparaît sur la console.
Ici dans notre exemple, il y a qu'un seul bouton. Si on met plusieurs boutons, comment les différencier si tous les listeners sont la même classe ? En effet si on fait le programme suivant :
import javax.swing.*; import java.awt.*; import java.awt.event.*; public class test extends JFrame implements ActionListener { private JButton but1,but2; //les boutons /** Constructeur de test */ public test() { //titre de la fenetre super("Test"); //panel JPanel pan=new JPanel(); pan.setLayout(new BorderLayout()); //bouton ici but1=new JButton("Ici !"); //ajoute un listener : ici le listener est cette classe but1.addActionListener(this); //ajoute le boutton dans le panel pan.add(but1,BorderLayout.CENTER); //2eme Bouton but2=new JButton("2eme !"); but2.addActionListener(this); pan.add(but2,BorderLayout.SOUTH); // setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setContentPane(pan); pack(); //permet de mettre une bonne dimension a la fenetre setVisible(true); } /** * obligatoire car test implémente l'interface ActionListener */ public void actionPerformed(ActionEvent e) { //quand on a cliqué sur le bouton ici System.out.println("Ici !"); } public static void main(String args[]) { new test(); } }
Lorsque vous cliquez sur le bouton "ici" et "2eme", on obtient le même message. C'est normal car ces 2 boutons partagent le même listener donc le même gestionnaire d'événements.
2 solutions s'offrent à nous pour pouvoir faire un traitement différents selon les boutons :
- soit on créé deux classes distinctes implémentant un ActionListener qui seront associées à chacun des boutons
- soit en utilisant l'objet ActionEvent passé en parametre de actionPerformed pour différencier les 2 composants.
Nous allons bien sur voir ces 2 solutions !
Solution : Utilisation de ActionEvent
A la page http://java.sun.com/j2se/1.5.0/docs/api/java/awt/event/ActionEvent.html on peut voir les méthodes d'un ActionEvent. La fonction getSource() va nous etre utile.
getSource() renvoi le composant qui a provoqué l'événement.
Ainsi, pour différencier le traitement selon le bouton, on réagit en fonction de l'objet retourné par getSource() :
Voici la nouvelle fonction actionPerformed() :
public void actionPerformed(ActionEvent e) { Object source=e.getSource(); if (source==but1) System.out.println("Ici !"); else if (source==but2) System.out.println("2eme !"); }
Maintenant, quand on clique sur le bouton "2eme !", le message affiché est "2eme !". On a différencié les 2 traitements.
La solution de réunir tous les traitements dans le même listener peut se révéler parfois assez pratique et simple (quoique) mais en mon sens, elle n'est pas très propre. La solution que nous allons voir après est à mon sens bien mieux.
Solution : Une classe par bouton
Pour différencier le traitement pour nos 2 boutons, nous pouvons créé 2 classes distinctes :
import javax.swing.*; import java.awt.*; import java.awt.event.*; public class test extends JFrame { private JButton but1,but2; //les boutons /** Constructeur de test */ public test() { //titre de la fenetre super("Test"); //panel JPanel pan=new JPanel(); pan.setLayout(new BorderLayout()); //bouton ici but1=new JButton("Ici !"); //ajoute un listener : ici le listener est cette classe but1.addActionListener(new TraitementBut1()); //ajoute le boutton dans le panel pan.add(but1,BorderLayout.CENTER); //2eme Bouton but2=new JButton("2eme !"); but2.addActionListener(new TraitementBut2()); pan.add(but2,BorderLayout.SOUTH); // setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setContentPane(pan); pack(); //permet de mettre une bonne dimension a la fenetre setVisible(true); } public static void main(String args[]) { new test(); } public class TraitementBut1 implements ActionListener { /** * obligatoire car test implémente l'interface ActionListener */ public void actionPerformed(ActionEvent e) { System.out.println("Ici !"); } } public class TraitementBut2 implements ActionListener { /** * obligatoire car test implémente l'interface ActionListener */ public void actionPerformed(ActionEvent e) { System.out.println("2eme !"); } } }
La classe TraitementBut1 gère les événements sur le bouton "ici" et la classe TraitementBut2 sur le bouton "2eme !".
Il existe une énorme variété de lListeners pour tout type d'événements. Chaque composant de l'interface graphique a ses événements.
Voici une petite liste des listeners :
...
Toutes les classes contenant le mot Listener est un listener, regardez la page http://java.sun.com/j2se/1.5.0/docs/api/ !
Cette deuxième partie est finie. Vous pouvez m'envoyer vos commentaires/remarques.