Construction pas à pas d'une application graphique en Java

Par Arnaud Venet (Arnaud.Venet@trusted-logic.fr)
pour le cours ENSTA in103 (initiation java) -- avril 2001


L'objectif est de détailler les étapes de la construction d'une petite application graphique composée des éléments suivants :

L'utilisateur peut rentrer librement du texte dans les champs A et B. Lorsqu'il appuie sur le bouton "+" l'application concatène le texte contenu dans les champs A et B et affiche le résultat de l'opération dans la zone d'affichage. L'utilisateur peut quitter l'application en cliquant sur le bouton "quitter".

Les sources complets des exemples décrits dans ce document sont accessibles en téléchargeant les fichiers Exemple1.tar.gz et Exemple2.tar.gz


Etape N°1 : Le programme principal

Notre application sera définie par une classe Application qui contiendra la description tous les composants graphiques. Nous allons définir une classe Main qui contient une variable globale statique application de type Application. La fonction main se contente simplement de créer une instance de la classe Application et de la stocker dans la variable application.

Fichier " Main.java " :

    class Main { 
        private static Application application; 
    
        public static void main(String[] args) { 
            application = new Application(); 
        } 
    } 
Nous rappelons qu'une fois compilé, le programme se lance de la manière suivante :
         java Main

Etape N°2 : La classe Application

Les composants graphiques que l'on utilisera sont ceux définis dans le paquetage javax.swing de la bibliothèque standard Java. Nous aurons besoin des composants définis par les classes suivantes : La classe application contient autant de champs que d'objets graphiques, c'est à dire que le fichier " Application.java " a la forme suivante :
    import javax.swing.*; 
    
    class Application { 
        private JFrame fenetre; 
        private JLabel affichage; 
        private JTextField champA; 
        private JTextField champB; 
        private JButton boutonPlus; 
        private JButton boutonQuitter; 
    
        public Application() { 
            ... 
        } 
    } 

Les objets graphiques sont créés dans le constructeur de la classe. Cela se fait comme suit :

    this.fenetre = new JFrame("Une application graphique"); 
    
    // Le constructeur de JFrame prend en argument le nom de 
    // la fenêtre 
    this.affichage = new JLabel(); 
    this.champA = new JTextField(20); 
    this.champB = new JTextField(20); 
    
    // Le constructeur de JTextField prend en argument la taille 
    // en caractères du champ de saisie 
    this.boutonPlus = new JButton("+"); 
    
    // Le constructeur de JButton prend en argument le nom du bouton 
    this.boutonQuitter = new JButton("Quitter"); 

Une fois ces composants créés, il faut indiquer la façon dont ils sont disposés à l'écran. La seule chose que nous avons besoin d'indiquer pour l'instant est que l'objet fenetre contient tous les autres composants graphiques. Pour cela il faut récupérer le "conteneur" de la fenêtre, c'est-à-dire la zone graphique associée à la fenêtre dans laquelle on peut disposer d'autres objets graphiques. La méthode getContentPane de la classe JFrame renvoie le "conteneur" associé à la fenêtre. C'est un objet de la classe Container définie dans le paquetage java.awt. Ajouter des objets au "conteneur" de la fenêtre se fait simplement à l'aide de la méthode add de la classe Container qui prend en argument l'objet graphique à insérer dans le "conteneur".

Cela n'est cependant pas suffisant. En effet il ne suffit pas de dire quels sont les objets qui seront affichés dans la fenêtre, encore faut il dire comment ces objets seront disposés dans cette fenêtre (en colonnes, les uns à la suite des autres, en cercle, ...). La disposition de composants graphiques dans un objet de type Container est effectuée par des objets particuliers, qu'on appelle des gestionnaires de disposition ("layout managers"). Il faut associer un gestionnaire de disposition au conteneur de la fenêtre par l'intermédiaire de la méthode setLayout de la classe Container. Nous n'avons heureusement pas à écrire les gestionnaires de disposition (même si cela est possible)  car la bibliothèque standard Java fournit déjà un certain nombre de classes de gestionnaires de disposition qui correspondent aux situations les plus courantes. Nous utiliserons essentiellement deux types de gestionnaires de disposition :

Si nous utilisons un gestionnaire de disposition de type FlowLayout cela peut s'écrire :
    Container conteneur = fenetre.getContentPane(); 
    FlowLayout disposition = new FlowLayout(); 

    conteneur.setLayout(disposition); 
    conteneur.add(this.affichage); 
    conteneur.add(this.champA); 
    conteneur.add(this.champB); 
    conteneur.add(this.boutonPlus); 
    conteneur.add(this.boutonQuitter); 

Cela sous-entend naturellement que nous ayons ajouté la ligne :
    import java.awt.*; 

au début du fichier " Application.java " afin de pouvoir utiliser la classe Container sans la préfixer par le nom de son paquetage.

Si nous avions voulu organiser les éléments suivant un quadrillage de trois lignes et deux colonnes, nous aurions écrit :

    GridLayout disposition = new GridLayout(3, 2); 

les autres commandes restant inchangées.

Une fois les objets graphiques disposés à l'intérieur de la fenêtre, il reste deux dernières choses à faire :

Cela s'écrit donc de la façon suivante :
    this.fenetre.pack(); 
    this.fenetre.setVisible(true); 

Ce qui termine l'écriture du constructeur de la classe Application.

NOTE : dès lors que l'on a un programme Java composé de plusieurs fichiers il vaut mieux utiliser la commande suivante :

    javac *.java

pour compiler tous les fichiers constituant le programme.

Etape N°3 : Association d'actions aux clics sur les boutons

Telle quelle cette application graphique est sans intérêt. On peut certes entrer du texte dans les champs de saisie, mais rien ne se passe si l'on clique sur les boutons. Il faut pour cela associer des actions aux clics sur les boutons. De la même façon qu'on affecte un gestionnaire de disposition au conteneur d'une fenêtre, nous devons associer un gestionnaire d'actions ("action listener") aux boutons. Ces gestionnaires d'événements déclencheront une action que nous aurons définie, et ce, à chaque fois que l'utilisateur cliquera sur le bouton correspondant. Un gestionnaire d'action en Java est un objet qui implémente l'interface nommée ActionListener qui se trouve dans le paquetage java.awt.event. Un gestionnaire d'action est associé à un bouton à l'aide de la méthode addActionListener de la classe JButton. On peut tout à fait associer plusieurs gestionnaires d'action à un même bouton qui seront activés en séquence lors d'un clic sur le bouton, mais nous n'aurons pas besoin de ce genre de choses. L'interface ActionListener décrit une unique fonctionnalité définie par la méthode suivante :
    public void actionPerformed(ActionEvent contexte); 

L'environnement d'exécution Java scrute constamment les événements souris et clavier et lorsqu'il détecte un clic sur un bouton il invoque la méthode actionPerformed de tous les gestionnaires d'action qui ont été attachés au bouton. L'argument passé à cette méthode contient des informations sur le contexte dans lequel l'action a eu lieu (si par exemple une touche du clavier était enfoncée au moment du clic) qui peuvent être utilisées par le programme. En pratique nous n'en aurons pas besoin et nous n'utiliserons donc jamais cet argument.

Un gestionnaire d'action est une classe qui implémente l'interface ActionListener et qui gère le comportement d'un bouton. Etant donné que nous avons deux boutons nous devons définir deux classes GestionQuitter et GestionPlus implémentant cette interface dans deux fichiers " GestionQuitter.java " et " GestionPlus.java ".

Le gestionnaire d'action le plus simple à définir est celui qui correspond au bouton "quitter". Terminer l'exécution d'un programme se fait en appelant la fonction statique exit de la classe System du paquetage java.lang (importé par défaut par tout programme Java). Cette fonction est en tout point similaire à la fonction exit de C (un code de retour de 0 signifie une sortie normale du programme).

Le fichier " GestionQuitter.java " contient donc :

    import java.awt.event.*; 
    
    class GestionQuitter implements ActionListener { 
        public void actionPerformed(ActionEvent contexte) { 
            System.exit(0); 
        } 
    } 

Nous devons également affecter un gestionnaire d'action de cette classe au bouton "quitter" de la classe Application. Nous devons donc rajouter dans le constructeur, juste après la construction des objets graphiques, les lignes suivantes :

    GestionQuitter gestionQuitter = new GestionQuitter(); 

    this.boutonQuitter.addActionListener(gestionQuitter); 

Après recompilation de tous les fichiers et lancement du programme on s'aperçoit que le bouton "quitter" est bien actif.

Le gestionnaire d'action du bouton "+" pose problème car il doit interagir avec les autres composants graphiques de l'application : il doit récupérer le contenu des champs de saisie et modifier la zone d'affichage. Un moyen de résoudre ce problème est de créer dans la classe GestionPlus un champ application qui contient un pointeur sur l'objet Application qui utilise ce gestionnaire d'action. Ainsi le bouton peut avoir accès aux informations contenues dans les composants graphiques de l'application. Comme ceux-ci sont privés il y a deux solutions possibles :

La seconde solution est la meilleure du point de vue de la programmation objet car elle est favorise le masquage de la structure interne de la classe Application.

Les méthodes getText et setText définies dans les classes JLabel et JTextField permettent de lire et modifier le contenu de ces objets textuels. Nous ajoutons donc les méthodes suivantes à la classe Application :

    public String contenuChampA() { 
        return this.champA.getText(); 
    } 

    public String contenuChampB() { 
        return this.champB.getText(); 
    } 

    public void afficher(String texte) { 
        this.affichage.setText(texte); 
    } 

La classe GestionPlus s'écrit alors :

    import java.awt.event.*; 

    class GestionPlus implements ActionListener { 
        private Application application; 
    
        public GestionPlus(Application appli) { 
            this.application = appli; 
        } 
    
        public void actionPerformed(ActionEvent e) { 
            this.application.afficher(
                    this.application.contenuChampA()
                 +  this.application.contenuChampB()
            ); 
        } 
    } 

Le constructeur permet à l'application d'envoyer un pointeur sur elle-même au gestionnaire d'action du bouton. On ajoute donc au constructeur de la classe Application, juste après la construction des objets graphiques, les lignes suivantes :

    GestionPlus gestionPlus = new GestionPlus(this); 

    this.boutonPlus.addActionListener(gestionPlus); 

Nous obtenons ainsi une application entièrement opérationnelle. Les sources complets de l'application se trouvent dans l'archive Exemple1.tar.gz "Exemple1" au même niveau que ce document HTML.
 

Extension : amélioration de la disposition des composants graphiques

Les gestionnaires de disposition que nous avons vus précédemment ne permettent qu'un agencement assez limité des objets graphiques. Il est par exemple impossible de disposer les composants comme suit : Pour cela il faut utiliser un composant particulier de type JPanel qui joue le même rôle que le "conteneur" d'une fenêtre, à la seule différence que ce "conteneur" est un composant graphique et peut être inséré à l'intérieur d'un autre "conteneur". On peut affecter un gestionnaire de disposition à un objet de type JPanel, ce qui permet d'avoir des zones d'objets avec différentes disposition au sein du même "conteneur" d'une fenêtre. Les objets graphiques s'insèrent dans un JPanel grâce à la méthode add de la même façon que pour un Container. La disposition précédente peut donc s'écrire de la manière suivante, en modifiant légèrement le constructeur de la classe Application :
    Container conteneur = fenetre.getContentPane(); 
    GridLayout disposition = new GridLayout(4, 1); 
    JPanel boutons = new JPanel(); 
    FlowLayout dispositionBoutons = new FlowLayout(); 

    boutons.setLayout(dispositionBoutons); 
    boutons.add(this.boutonPlus); 
    boutons.add(this.boutonQuitter); 

    conteneur.setLayout(disposition); 
    conteneur.add(this.affichage); 
    conteneur.add(this.champA); 
    conteneur.add(this.champB); 
    conteneur.add(boutons); 

Les sources de l'application ainsi modifiée sont disponible dans l'archive Exemple2.tar.gz


retour page java           modif 18/04/02 par Maurice Diamantini