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( ).
    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.
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
    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);}
}
    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 getSelectedItems( ) 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
    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);}
}
    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);}
} 
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.