Swing - Partie 2 : ActionListener, Listener, JButton

Swing - Partie 2 : Action !

Description

C'est la deuxième partie du mon tutoriel sur la création d'interface graphique en Swing.

Introduction

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...

Les Listeners

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.

ActionListener : réagir aux clics sur un JButton

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 !".

Les autres Listeners

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 :

  • WindowListener: tous les évènements concernant une fenêtre (ouverture, fermeture, sélection etc)
  • MouseListener: tous les évènements concernant la souris (clic, etc)
  • MouseMotionListener: tous les évènements concernant le mouvement de la souris

...

Toutes les classes contenant le mot Listener est un listener, regardez la page http://java.sun.com/j2se/1.5.0/docs/api/ !

Fin

Cette deuxième partie est finie. Vous pouvez m'envoyer vos commentaires/remarques.

A voir également
Rejoignez-nous