Introduction à JAVA

 Les composants graphiques (1.0)

    Tous les systèmes d'exploitation modernes sont maintenant dotés d'interfaces graphiques. Au lieu de concevoir des composants spécifiques à  JAVA, les concepteurs du langage ont préféré réutiliser les composants de la machine client au moyen d'interfaces implantés dans la machine virtuelle du navigateur ce qui assure l'indépendance de JAVA par rapport à la plate-forme.  Le positionnement de ces composants dans la fenêtre de l'applet est déterminé par les protocoles de mise en page déjà étudiés au chapitre 10. La majorité des composants répondent aux actions des utilisateurs (click souris, touche [Enter] ...) par l'envoi d'un événement au conteneur du composant. Dans les versions 1.0.x ces événements peuvent être interceptés et traités par une méthode handleEvent(Event evt ) de la classe Component. Si l'événement n'est pas traité par ce conteneur, il remonte au conteneur qui le contient : en général pour les applets,  il suffit d'implémenter la méthode handleEvent( ) de l'applet puisque c'est elle qui est le conteneur final de tous les composants. Pour les événements simples la méthode handleEvent( ) appelle la méthode action(Event evt, Object arg) . La variable evt.target permet de récupérer la nature du composant à l'origine de l'événement.
      Pour tous les composants, il est possible de préciser lors de la création la fonte associée au texte et  la couleur du pinceau au moyen de la méthode setForeground( ) . Il est possible de cacher un composant de nom cp par la méthode cp.hide( ) , de le réafficher avec cp.show( ) , de le désactiver avec cp.disable( ) et de le réactiver avec cp.enable( ) .

11.1 Classe Label

    Les libellés sont des objets de la classe Label . Ils permettent d'afficher un texte dans un conteneur. Le texte d'un Label peut être modifié librement mais la longueur du nouveau texte ne doit pas excéder celle définie lors de la mise en place par la méthode add( )  dans le gestionnaire de mise en page.
Ce composant ne génère pas d'action .
Les paramètres d'alignement sont Label.LEFT = 0,  Label.CENTER = 1 et Label.RIGTH = 2.
Les constructeurs sont Label( ) , (Label sans texte),  Label(String s) , (Label de texte s) et Label(String s, int aligne) qui précise l'alignement dans le conteneur.
Les méthodes d'instances d'un Label de nom "lb" sont lb.getText( ) qui permet de récupérer le libellé du Label et lb.setText(String s) qui permet de le modifier.

11.2 Classe Button

Les boutons appartiennent à la classe Button . Ce sont des rectangles portant un titre et dont l'aspect se modifie lorsque l'on clique sur eux (inversion de l'effet de relief). Ils émettent alors l'événement ACTION_EVENT.
Les constructeurs sont Button( ) , (Bouton sans texte) et   Button(String label) (Bouton avec le texte label ).
Ce sont les dimensions de la chaîne label initiale qui définissent les dimensions finales du bouton : il est donc important de commencer par préciser la fonte utilisée avant de placer le bouton.
Les méthodes d'instances d'un bouton de nom "bt" sont bt.getLabel( ) qui permet de récupérer le libellé du bouton et bt.setLabel(String s) qui permet de le modifier avec les mêmes restrictions pour les dimensions que pour les Labels. 

L'exemple suivant met en oeuvre les méthodes des libellés et de boutons. Pour ne pas surcharger le listing, il n'est pas précisé de protocole de mise en page : c'est donc le gestionnaire par défaut (FlowLayout en mode centré) qui est utilisé. On trouvera aussi un exemple d'utilisation de la méthode action( ) pour le contrôle des événements. Les composants sont identifiés à partir des événements mais il est aussi possible d'utiliser le paramètre arg qui retourne ici le libellé. On modifie le libellé du troisième bouton à chaque click. Il faut prévoir pour son texte initial une longueur suffisante afin que le texte de remplacement puisse être affiché en totalité. L'exemple indique également comment utiliser les méthodes show( ) , hide( ) , enable( ) et disable( ) .

import java.applet.*;
import java.awt.*;

 public class boutons extends Applet
{  Font font = new Font("Helvetica",0,12);
    Font bold = new Font("Helvetica",1,12);
    Label lb = new Label("Test"); //déclaration et création
    Button bt1,bt2,bt3;
    boolean gras;   String s;

public void init()  
{  setBackground(Color.lightGray);
    setFont(font); //fonte par défaut pour les composants et l'applet
    add(lb);        //ajout du Label par le gestionnaire
    bt1 = new Button("Gras"); //création
bt1.setForeground(Color.red);    //couleur du texte du bouton
    bt1.setFont(bold);    add(bt1); //fonte spécifique et ajout
    bt2 = new Button("Normal");
    add(bt2);
    bt3 = new Button(" Ancien ");
    add(bt3);}

public boolean action(Event evt, Object arg)
{ s = arg.toString();
   if (evt.target.equals(bt1)) gras = true;
   else if (evt.target==bt2) gras = false; //autre syntaxe possible
   else if (evt.target.equals(bt3)){
      if (bt3.getLabel()==" Ancien "){
         bt3.setLabel("Nouveau"); //modification du libellé à chaque click
         bt1.hide();       //cacher bt1
         bt2.disable();}   //désactiver bt2
      else {
         bt3.setLabel(" Ancien ");}
bt1.show();
         bt2.enable();}}
   else return super.action(evt,arg); //pour événements non traités ici
   repaint();
   return true;}

public void paint(Graphics g)
{ if (gras) g.setFont(bold); else g.setFont(font);
   g.drawString("Test des boutons",20,50);
   g.drawString(s,20,65);}
}

  Tester les 3 boutons

 11.3 Classe Checkbox

    Il existe deux types de cases à cocher : les cases à choix multiples qui sont indépendantes les unes des autres et les cases à choix exclusif nommées aussi boutons radio. Dans un groupe à choix exclusif, une seule case peut être cochée à la fois. Il alors faut inclure les cases à cocher dans un groupe grace à un objet CheckboxGroup.
Les cases à cocher possèdent deux états : cochées ou non cochées. Un événement ACTION_EVENT est généré à chaque changement d'état.
Les constructeurs des cases à choix multiple sont Checkbox( ) (case sans label) et   Checkbox(String label) (case avec un libellé);
Pour les boutons radio, le constructeur est Checkbox(String label, CheckboxGroup nom_groupe, boolean etat ); il  associe la case au groupe nom_groupe et précise son état (cochée ou non).
On récupère l'état (booléen) de la case de nom cb avec la méthode cb.getState( ) et on l'impose avec la méthode cb.setState(boolean e) .

Dans l'exemple suivant les cases cb1 et cb2 sont des cases à choix multiple (les deux peuvent être cochées ou décochées en même temps). Les autres cases forment un groupe exclusif : une seule case est cochée. La méthode init( ) indique comment ajouter les composants dans le protocole de mise en page (Ici  le gestionnaire. par défaut : FlowLayout en mode centré). La méthode action( ) donne une façon possible de traiter les événements.

import java.applet.*;
   import java.awt.*;

public class boxcheck extends Applet
{  Font font = new Font("Helvetica",0,12);
    Checkbox cb1,cb2,cb3,cb4,cb5;   //déclarations des cases
    CheckboxGroup cg = new CheckboxGroup(); //déclarations du groupe
    boolean gras,ital;
    int index=1;        String s,s1;

 public void init()
  { setBackground(Color.lightGray);
    setFont(font);
    cb1 = new Checkbox("Gras");     add(cb1); //création et ajout
    cb2 = new Checkbox("Italique"); add(cb2);
    cb3 = new Checkbox("Case 1",cg,true);   //création bouton radio
    add(cb3);
    cb4 = new Checkbox("Case 2",cg,false);    add(cb4);
    cb5 = new Checkbox("Case 3",cg,false);    add(cb5);}

 public boolean action(Event evt, Object arg)
  { s1=arg.toString();
    if (evt.target==cb1) {
       gras = (cb1.getState()) ? true : false;} //test de l'état de la case
    else if (evt.target==cb2) {
       ital = (cb2.getState()) ? true : false;}
    else if (evt.target==cb3) index=1; //choix exclusif
    else if (evt.target==cb4) index=2; 
    else if (evt.target==cb5) index=3;
    else return super.action(evt,arg);
    repaint();
    return true;}

 public void paint(Graphics g)
  {  int mode=0;
     if (gras) mode+=1;
     if (ital) mode+=2; //ajout des valeurs si les deux cases sont cochées
     font = new Font("Helvetica",mode,12);
     g.setFont(font);
     g.drawString("Exemples de cases à cocher",20,60);
     g.drawString("Case cochée : "+index,250,60);
     g.drawString("Arg = "+s1,20,80);}
}

 

11.4 Classes Choice et List

    La classe Choice correspond à des menus déroulants. Au repos, ce composant affiche l'item sélectionné. Après un click, il affiche l'ensemble les items du menu et il devient alors possible de faire une nouvelle sélection par un click qui génère un événement ACTION_EVENT. Après création de la liste ch par le constructeur Choice( ) , il faut ajouter chaque item de la liste avec la méthode ch.addItem(String label) . La position de l'item sélectionné (le premier index vaut 0) est donnée par la méthode ch.getSelectedIndex( ) . La méthode ch.getSelectedItem( ) retourne le libellé de l'item sélectionné. Il est possible de supprimer l'item d'index p avec ch.delItem( int p) ou de le remplacer par ch.addItem(String label, int p) .
Attention : Le déroulement d'un menu Choice masque une partie de l'applet. Dans certains navigateurs la fermeture du menu déclenche le rafraîchissement de l'affichage par un appel à repaint( ).

    La classe Liste correspond à des listes de choix. Au repos, ce composant affiche plusieurs item. Un ascenseur permet de visualiser (si nécessaire) la totalité de la liste. Comme la liste peut autoriser des choix multiples la sélection d'un item par un simple click génère un événement LIST_SELECT qui doit être traité par la méthode handleEvent( ) . De même la dessélection d'un item génère un événement LIST_DESELECT. Par contre la sélection d'un item par un double click génère un événement ACTION_EVENT.
Les constructeurs sont List( ) (liste simple) et List(int nb, boolean chMul) qui définit une liste affichant nb items et qui autorise le choix multiple si le booléen chMul est vrai. Les méthodes de création des items et récupération de l'index sont les mêmes que pour la classe Choice. La méthode getSelectedItem s ( ) retourne un tableau de chaînes qui contient les éléments sélectionnés. Ne pas oublier le s à la fin du nom de la méthode.
L'exemple suivant montre comment utiliser les listes. Le compteur ct indique le nombre de passage dans la méthode paint( ). Ce compteur permet de vérifier si l'utilisation du menu déroulant provoque un repaint( ) non demandé par le programmeur. (fonction du navigateur utilisé)

import java.applet.*;
import java.awt.*;

public class liste extends Applet
{ Font font = new Font("Helvetica",0,12);
   Font bold = new Font("Helvetica",1,12);
   Choice choix;
   List liste;
   int nch,nliste,ct;
   String s,s1,sel[]=new String[5];

public void init()
{ setBackground(Color.lightGray);
   setFont(font);
   choix = new Choice();
   choix.addItem("Choix 1");       choix.addItem("Choix 2");
   choix.addItem("Choix 3"); //création des items
   add(choix); //ajout par le protocole de mise en page
   liste = new List(3,true); //affichage sur 3 lignes, multiple autorisé
   liste.addItem("Element 1");     liste.addItem("Element 2");
   liste.addItem("Element 3");     liste.addItem("Element 4");
   liste.addItem("Element 5");
   add(liste);}

public boolean action(Event evt, Object arg)
{ s1=arg.toString(); //retour de la chaîne de l'item sélectionné
   if (evt.target==choix) {
       nch=choix.getSelectedIndex(); //index de l'item sélectionné
s=choix.getSelectedItem();
       if (nch==2) ct=0;} //RAZ du compteur de passage dans paint()
    else if (evt.target.equals(liste))
       sel=liste.getSelectedItems();
   else return super.action(evt,arg);
   repaint();
   return true;}

public void paint(Graphics g)
{  ct++;    g.drawString("Ct = "+ct,10,70);
    g.drawString("Choix "+(nch+1),10,90);
    g.drawString("Item : "+s,10,110);
    g.drawString("Sélection : ",120,70);
    for (int i=0; i<sel.length; i++)
       g.drawString(sel[i],120,85+15*i);
    g.drawString("Arg = "+s1,90,110);}
}

   Double click sur la liste

 11.5 Classe TextField

    C'est une zone de texte sur une ligne. Le contenu du champ est éditable par l'utilisateur.  La touche "retour chariot" [Enter] génère un événement ACTION_EVENT.
Les constructeurs sont TextField( ) qui retourne un champ vide, TextField( int n) qui crée un champ vide de n caractères, TextField( String s) qui crée un champ initialisé avec la chaîne s et TextField( String s, int n) . Les principales méthodes que l'on peut appliquer sur une zone de texte de nom tf sont tf.getText( ) qui retourne la chaîne affichée et tf.setText(String ns) qui affiche la chaîne ns dans le champ. Pour faire des saisies avec un masque d'écriture, on peut aussi utiliser la méthode tf.setEchoCharacter(char c) .  La classe TextArea (zone de texte sur plusieurs lignes) possède des méthodes analogues.

L'exemple montre comment utiliser les zone de texte. Il précise comment effectuer les conversions des chaînes de caractères récupérées vers des entiers ou des réels. Avec ce code, il y a générations d'exceptions si la chaîne tapée dans la zone ne représente pas un nombre valide.
Note : par suite d'un bug (?), le compilateur utilisé génère un code non fonctionnel si les zones de texte sont créees dans la procédure init( ) et pas lors de la déclaration.

import java.applet.*;
import java.awt.*;

public class zonetext extends Applet
{ int i=10;  double d=5.25;
   String s,s1,sm="*****";
   Font font = new Font("Helvetica",0,12);
   Label lb1,lb2,lb3;
   TextField tf1=new TextField("10",5);
   TextField tf2=new TextField(""+d,5);
   TextField tf3=new TextField(sm,6);

public void init()
{  setBackground(Color.lightGray);
    setFont(font);
    lb1=new Label("Entier",0);
    add(lb1);   add(tf1);
    lb2=new Label("Réel",0);
    add(lb2);   add(tf2);
    lb3=new Label("Masqué",0);
    add(lb3);   add(tf3);
    tf3.setEchoCharacter('*');

public boolean action(Event evt, Object arg)
{ s1=arg.toString();  
   if (evt.target==tf1) {
     s=tf1.getText();
     i=Integer.parseInt(s);} //conversion chaîne en entier
   else if (evt.target==tf2) {
     s=tf2.getText();
     d=Double.valueOf(s).doubleValue();} //conversion chaîne en double
   else if (evt.target==tf3){
     sm=tf3.getText();}
   else return super.action(evt,arg);
   repaint();   return true;}

public void paint(Graphics g)
{ g.drawString("Entier = "+i,20,60);
   g.drawString("Double = "+d,120,60);
   g.drawString("Masqué = "+sm,250,60);
   g.drawString("Arg = "+s1,20,80);}
}

11.6 Classe Scrollbar

    A cause des problèmes de mise en page, c'est un composant délicat à mettre en oeuvre car il veut occuper le maximum de place mis à sa disposition. Il faut, surtout si on utilise des ascenseurs verticaux,  empiler des panneaux et des gestionnaires pour obtenir un aspect à peu près correct. La mise en page sans protocole est toujours beaucoup plus facile à mettre en application dans le cas des ascenseurs.
Le constantes Scrollbar.HORIZONTAL =0 et Scrollbar.VERTICAL = 1 définissent l'orientation de l'ascenseur.
Le constructeur d'un ascenseur est Scrollbar(int orient, int valeur, int delta, int mini, int maxi) . valeur est la valeur initiale associée à la position du curseur, delta est l'increment dont varie la valeur quand on clique de part et d'autre du curseur (un click sur les boutons des extrémités fait varier la valeur de une unité), mini et maxi sont les valeurs extrêmes entre lesquelles peut varie valeur . Un ascenseur ne génère pas d'événement ACTION_EVENT. Il faut utiliser la méthode handleEvent( ) pour récupérer les événements de ce composant. La méthode la plus utile est getValue( ) qui retourne la valeur liée à la position du curseur.

L'exemple suivant montre comment utiliser les ascenseurs. L'ascenseur vertical est placé à gauche. Il s'étale jusqu'en bas de l'applet. Les deux ascenseurs horizontaux sont placés dans des panneaux imbriqués dans un autre panneau (le gestionnaire BorderLayout bl admet un seul composant dans la case Nord). On peut constater que leur longueur est liée à celle du libellé placé juste au-dessus.
J'ai constaté que comme valeur maximale, il faut prendre la valeur souhaitée augmentée de la valeur de l'incrément delta. (avec un delta de 5, il faut prendre mini = 0, maxi = 105 pour que valeur varie entre 0 et 100.)

import java.applet.*;
import java.awt.*;

public class ascenseur extends Applet
{ int v1=10,v2=20,v3=150; //valeurs initiales
   Font font = new Font("Helvetica",0,12);
   Panel panN = new Panel();
   Panel pan0=new Panel(),pan1 = new Panel();
   BorderLayout bl= new BorderLayout(20,20);
   Scrollbar sc1=new Scrollbar(1,v1,5,0,105);   //vertical
   Scrollbar sc2=new Scrollbar(0,v2,10,0,110); //horizontal
   Scrollbar sc3=new Scrollbar(0,v3,10,100,210);
   Label lb2=new Label("Ascenseur 2");
   Label lb3=new Label("Autre ascenseur");

public void init( )     
{  setBackground(Color.lightGray);
    setFont(font);
    setLayout(bl);
    add("North",panN);   add("West",sc1);
    panN.setLayout(new FlowLayout(0,30,0)); //panneau Nord global
    panN.add(pan0); panN.add(pan1);         //sous-panneaux Nord
    pan0.setLayout(new BorderLayout(5,0)); //pour placer lb2 et sc2
    pan0.add("North",lb2); //la longueur du Label détermine celle
    pan0.add("South",sc2); //de l'ascenseur
    pan1.setLayout(new BorderLayout(5,0)); //pour lb3 et sc3
    pan1.add("North",lb3);
    pan1.add("South",sc3);}

public boolean handleEvent(Event evt)
{  if (evt.target==sc1)  v1=sc1.getValue();
    else if (evt.target==sc2) v2=sc2.getValue();
    else if (evt.target==sc3)  v3=sc3.getValue();
    else return super.handleEvent(evt);
    repaint();      return true;}

public void paint(Graphics g)
{  g.drawString("A1 = "+v1,100,60);
    g.drawString("A2 = "+v2,100,80);
    g.drawString("A3 = "+v3,100,100);}
}

11.7 Autres composants

On peut aussi considérer les panneaux ( Panel ) et les canevas ( Canvas ) comme des composants : ils sont également mis en place par les protocoles de mises en page. La classe Panel a déjà été examinée lors de l'étude des gestionnaires de mise en page. Les canevas sont des zones de dessins pour lesquelles il faut obligatoirement redéfinir une méthode paint(Graphics g) spécifique. Il faut donc définir une classe propre à chaque canevas que l'on veut mettre en place. Le constructeur de cette classe est vide mais la méthode resize( ) permet de préciser ses dimensions.