PROGRAMMATION SWING ET XML

I. CONCEPTS ET CARACTERISTIQUES DE SWING

Premier TP : Etude d'une application Swing simple.

Nous allons dans ce premier TP étudier quelques un des concepts et des caractéristiques de base de Swing, au travers d'un petit exemple. Nous allons créer une petite application Swing permettant de compter le nombre de "clic" de souris effectuer par l'utilisateur sur un bouton.Voici l'interface que nous désirons obtenir :


Figure 1 : Interface graphique de notre petite application.

Pour réaliser cette interface, nous allons suivre les différentes étapes suivantes :

Importation des paquetages Swing nécessaires :

La ligne suivante permet d'importer le paquetage Swing principal :
import javax.swing.*;
Remarque: Les versions plus anciennes de Swing utilisent des noms de paquetage différents (i.e. com.sun.java.swing.*),
a priori, si vous utilisez un JDK supérieur ou égal à la version 1.2, vous ne rencontrerez pas ce problème.

La plupart des programmes utilisant Swing nécessite aussi l'importation de deux importants paquetages de AWT :*
 

import java.awt.*;
import java.awt.event.*;

Choisir le Look & Feel de notre application :

Swing vous permet de spécifier le Look & Feel, c'est-à-dire l'apparence que peuvent prendre vos applications. Ils existent différents Look & Feel standard : le Java L & F, le Windows L & F, le CDE/Motif L & F, etc ... Le code en gras dans l'extrait de code suivant, vous montre comment procéder pour régler le Look & Feel :
public static void main(String[] args) {
    try {
        UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
    } catch (Exception e) { }
    ...//Create and show the GUI...
}

Le code précédant signifie : "Quelque soit le Look & Feel choisit par l'utilisateur, utilise le Look & Feel indépendant de la plate-forme (le Java L & F). Un exemple de Java L & F est la figure 1. Vous pouvez ne pas vous préoccuper du L & F de votre application dans un premier temps, nous reviendrons dessus plus en détail.

Fixer notre conteneur racine (top-level container) :

Chaque programme fournissant une interface graphique contient au moins un conteneru racine. Pour la plupart des programmes, ce conteneur est une instance de JFrame, JDialog or JApplet (pour les applets :-). Chaque objet JFrame implémente une seule fenetre principale, et chaque objet JDialog implémente une fenetre secondaire. Pour sa part, chaque objet JApplet implémente une zone d'affichage à l'intérieur d'un navigateur. Un conteneur racine (top-level container) fournit les fonctionnalités nécessaires au composant Swing qui ont besoin de s'afficher et de traiter des événements.

Notre exemple d'ApplicationSwing ne possède qu'un conteneur racine : une JFrame. Quand l'utilisateur ferme cette fenetre, l'application s'arrete. Voici le code permettant de régler et d'afficher notre fenetre :
 

public class ApplicationSwing {
    ...
    public static void main(String[] args) {
        ...
        JFrame frame = new JFrame("ApplicationSwing");
        //...creation des composants qui s'intégreront dans la fenetre...
        //...on les ajoute dans un container nommé "contents"...
        frame.getContentPane().add(contents, BorderLayout.CENTER);
        //Achever les réglages et procéder à l'affichage.
        frame.addWindowListener(...);
        frame.pack();
        frame.setVisible(true);
    }
}

Régler les composants atomiques : le bouton et l'étiquette.

Comme la plupart des interfaces graphiques, notre ApplicationSwing contient un bouton et une étiquette (contrairement aux autres applications, c'est tout ce qu'elle possède !-). Voici le code qui initialise le bouton :
 
JButton button = new JButton("Je suis un bouton Swing !");
button.setMnemonic(KeyEvent.VK_I);
button.addActionListener(...creation d'un gestionnaire d'événements de type "action"...);

La première ligne crée un objet bouton, tandis que la seconde fixe la touche mnémonique associée à ce bouton (dans notre cas la combinaison ALT-i provoquera un "clic" de plus). Enfin, la troisième ligne abonne un gestionnaire d'événement pour traiter le "clic" sur le bouton. Nous verrons plus en détail dans la section "Prendre en compte le traitement des événements", comment traiter cet événement.

Voici maintenant le code qui initialise et manipule l'étiquette :
 

...
//On déclare nos variables d'instances :
private static String labelPrefix = "Nombre de "clics" de bouton : ";
private int numClicks = 0;
...
//On initialise notre étiquette :
final JLabel label = new JLabel(labelPrefix + "0    ");
...
label.setLabelFor(button);
...
//dans le gestionnaire d'événemenent des "clics" de souris :
label.setText(labelPrefix + numClicks);

Les lignes de code précédantes sont assez explicites, sauf peut-etre celle invoquant la méthode setLabelFor. Cet appel n'est qu'une indiquation que l'on donne pour signifier que ce label est rattaché au bouton. Cette information pourra etre utilisé dans le cadre des technologies d'accessibilité (i.e. les technologies facilitant l'utilisation de l'ordinateur pour les personnes handicapées).

Ajouter nos composants à nos conteneurs :

Notre ApplicationSwing regroupe le label et le bouton dans un conteneur (un JPanel) avant de l'ajouter dans la fenetre. Voici le code effectuant l'initialisation du panneau :
JPanel pane = new JPanel();
pane.setBorder(BorderFactory.createEmptyBorder(30, 30, 10, 30));
pane.setLayout(new GridLayout(0, 1));
pane.add(button);
pane.add(label);

La première ligne crée le panneau, tandis que la seconde lui règle sa bordure. La troisième crée un gestionnaire d'agencement (layout manager) qui force le contenu du panneau à etre affiché en une seule colonne. La dernière ligne ajoute le bouton et l'étiquette à notre panneau. Le fait d'ajouter le bouton et l'étiquette signifie qu'ils sont controllés par le gestionnaire d'agencement de leur père. En particulier, le gestionnaire d'agencement d'un conteneur détermine la taille et la position de chaque composant ayant été ajouté à ce conteneur.

Ajouter une bordure à nos composants :

Voici, le bout de code permettant de gérer une bordure autour de nos composants :
 
 pane.setBorder(BorderFactory.createEmptyBorder(30, 30, 10, 30));

Cette bordure fournit simplement un peu d'espace autour du contenu de notre panneau --  30 pixels en haut, à droite et à gauche, et 10 pixels en bas. La gestion des bordures est héritée par Jpanel de la classe JComponent.

Prendre en compte le traitement des événements :

Notre ApplicationSwing contient deux gestionnaires d'événements.Le premier s'occupe des "clics" de souris, et le second gère la fermeture de la fenetre principale. Voici le code associé aux traitements d'événements dans notre ApplicationSwing :
 
button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        numClicks++;
        label.setText(labelPrefix + numClicks);
    }
});
...
frame.addWindowListener(new WindowAdapter() {
    public void windowClosing(WindowEvent e) {
        System.exit(0);
    }
});

Le premier extrait de code est associé au traitement concernant les "clics" de souris. Lorsqu'un événement "clic" de souris est créé, il est passé à la méthode actionPerformed (héritée de l'interface ActionListener), qui peut effectuer son traitement. On incrémente donc notre compteur de "clic" et on met à jour le contenu de l'étiquette. Dans le deuxième extrait de code, on utilise une classe utile WindowAdapter, qui implémente l'ensemble des méthodes devant etre implémentées lorsque l'on veut traiter les événements concernant les fenetres (WindowEvent). Toutes les méthodes de cette classe sont vides, et il ne nous reste qu'à implémenter la méthode nous intéressant, dans notre cas windowClosing(WindowEvent e).

Prendre garde aux problèmes liés aux Threads :

 Notre ApplicationSwing est sure vis-a-vis de la concurrence (i.e. thread-safe). Une fois que l'interface graphique est visible, les seules modifications la concernant ont lieu dans des processus de gestion d'événements. Comme le gestionnaire d'événement tourne dans le meme thread que celui qui effectue tous les traitements d'événements et les affichages de l'application, il n'est pas possible que deux threads essayent de manipuler l'interface graphique en meme temps. Cependant, il peut etre aussi facile d'introduire des problèmes liés au threading , pensez donc bien à effectuer les modifications concernant l'interface graphique dans le processus de traitement d'événement.

Supporter les technologies d'accessibilité :

Les technologies d'accessibilités permettent aux personnes ayant un handicap d'accéder à l'ordinateur et à ses applications. Des interfaces tels que des lecteurs d'écran ou des claviers en braille permettent une interaction avec l'utilisateur, sans passer uniquement par l'interface graphique. Chaque composant Swing contient un certain nombre de caractéristisques permettant de prendre un compte cette ouverture vers les technologies d'accessibilité. Dans notre code, un exemple illustrant cela est l'utilisation de la méthode setLabelFor.

Il faut savoir que les technologies d'accessibilité peuvent tirer parti des informations contenus dans les composants de l'interface graphique pour interagir avec l'utilisateur. Ainsi, dans le code suivant, toutes les informations que l'on donne en tant que programmeur sont accessibles à ces technologies (sans que l'on ai d'ailleurs à introduire de code particulier!) :
 

JButton button = new JButton("I'm a Swing button!");
label = new JLabel(labelPrefix + "0    ");
label.setText(labelPrefix + numClicks);
JFrame frame = new JFrame("SwingApplication");

Nous n'étudierons pas plus en détail ces possibilités, mais vous pouvez toujours vous reporter à l'Accessibility API pour en savoir plus.

Pour ceux qui auraient tout finit en moins d'une demi-heure ...

Pour ceux qui auraient finit rapidement cette petite application, nous allons maintenant rajouter une zone de saisie à notre ApplicationSwing. Cette zone de saisie devra etre éditable lorsque le nombre de "clics" sera pair, et non-éditable sinon. Voici deux photos d'écran illustrant cela :
 

Cette petite modification ne vous demandera pas trop de temps normalement, nous allons donc compliquer encore un peu plus notre exemple. Pour cela, tout en gardant les memes composants de base, on voudrait obtenir le comportement suivant :

  1. Meme comportement de la zone de saisie que précédemment, mais
  2. inversion de la zone de saisie et du label à chaque "clic" de souris.
Ce qui nous donnera visuellement les résultats suivants :
 

Voila, cela sera tout pour cette fois, mais rien ne vous empeche de rajouter des petites fonctionnalités comme la modification du Look & Feel lors de chaque "clic", ou mieux : rajouter un JComboBox permettant d'activer ou de désactiver la zone de saisie ...

Pour résumer : expérimentez !!!


Pour ceux qui abandonnent, le code des applications demandées est là : ApplicationSwing, ApplicationSwing2 et ApplicationSwing3.