La deuxième partie de cette présentation de Java porte sur les notions d'interfaces et de paquetages, pour ensuite décrire les applets, le modèle graphique et la gestion des événements. Elle repose sur le jdk 1.1.
Une interface définit un type en déclarant un ensemble d'entêtes de méthodes (comme les méthodes abstraites) et de constantes. On peut alors déclarer des variables de ce type.
Une classe peut indiquer dans sa définition qu'elle implante une ou plusieurs interfaces. Elle doit alors à fournir une définition pour les méthodes de l'interface. L'intérêt des interfaces est de pouvoir rendre des classes, compatibles au niveau des types, sans forcément effectuer une liaison d'héritage. D'autre part le fait de pouvoir implanter plusieurs interfaces permet de simuler l'héritage multiple. Néanmoins les interfaces ne peuvent pas implanter de méthodes, ce qui rendra plus difficile la réutilisation de code que peut offrir un véritable héritage multiple.
Les paquetages (package
) regroupent des classes
et des interfaces. Ce sont les modules de Java. Ils permettent d'avoir
accès à un nom sans ambiguité. Par exemple la classe java.lang.Applet
indique la classe applet
du sous-paquetage lang
du paquetage
java
. On indique son appartenance à un paquetage par la déclaration
package TD5.exemples;
. Pour éviter d'avoir à écrire le nom complet
d'une classe, il est possible d'importer un paquetage (import TD5.*
indique d'importer tous les paquetages de racine TD5
). Enfin sans
déclaration spéciale une classe est disponible au niveau du paquetage.
Si on désire la rendre publique, il faut l'indiquer explicitement.
Une évolution, à partir de la version 1.1 du langage Java, est la possibilité de déclarer des classes internes (locales). Une classe interne peut être définie comme membre d'une classe (comme les champs de données ou de méthodes) ou bien à l'intérieur d'un bloc d'instructions (à l'intérieur d'une méthode).
Si la classe interne est déclarée comme champs de sa classe englobante, elle pourra être accessible par la notation qualifiée "point" indiquant le chemin d'accès. Par contre si elle est déclarée à l'intérieur d'un bloc d'instruction, elle ne sera visible qu'à l'intérieur de ce bloc. Néanmoins une valeur de cette classe interne pourra être retourné comme résultat, et surtout utilisé par la suite, comme valeur d'un certain type, si cette classe implantait l'interface correspondante.
Comme le nom de la classe n'a que peu d'importance dans ce dernier cas, il est autorisé de déclarer localement des classes anonymes.
L'intérêt des classes internes provient principalement du nouveau mécanisme de gestion des évènements des applets (que nous verrons par la suite).
On présente les principales classes pour définir des applets (classe Applet
), dessiner (classe Graphics
) et pour interagir (classe Event
). Une applet est une petite application graphique. Le paquetage principale pour gérer le graphisme et l'interaction s'appelle AWT (Abstract Window Toolkit). Ce paquetage a été fortement modifié entre la version 1.0 et la version 1.1, principalement pour la gestion des évènements.
cycle de vie d'une applet : init()start()stop()destroy() où :
Une applet se lance soit avec appletviewer
, soit avec netscape
,
suivi d'un fichier HTML indiquant le fichier .class
de la manière suivante :
<html> <head> Exercices en Java </head> <body> <H1> Test </H1> <P> <applet code="Test1" height=400 width=400> <P><EM> Not a java-powered browser! </EM> </applet> </body> </html>
Ce fichier correspond à l'exemple Test1 développé par la suite.
La feuille de dessin d'une applet est un objet de la classe Graphics
.
Elle est représentée par une matrice de points (class Points
)
dont les coordonnées sont (0,0) en haut à gauche et vont jusqu'à (size.width()-1,size().height-1)
en bas à droite. La classe Applet
utilise principalement la méthode paint(Graphics g) correspondant
à son affichage graphique. L'exemple suivant :
import java.awt.*;
import java.applet.*;
public class Test1 extends Applet {
public void paint(Graphics g) {
g.drawString("Salut tout le monde",100,10);
}
}
est une applet affichant un texte aux coordonnées (100,10).
Les méthodes principales de dessins sont : drawLine, drawRect, fillRect, drawOval, fillOval, setColor, drawString, setFont ...
On s'intéresse aux interactions d'une applet. Elles sont gérées comme des événements (dérivés de EventObject) produits par un composant graphique (dérivé de Component) de l'applet, que l'on appellera la source, et traités par un délégué qui a indiqué qu'il s'intéressait à cet évènement en s'enregistrant prealablment auprès dè la source. On retrouve ici le modèle de gestion des évènements du système NextStep.
La source doit donc définir des méthodes d'enregistrement des délégués qui doivent respecter la forme suivante :
public <TypeEvt>Listener set<TypeEvt>Listener(<TypeEvt>Listener monDelegue) public <TypeEvt>Listener add<TypeEvt>Listener(<TypeEvt>Listener monDelegue) public <TypeEvt>Listener remove<TypeEvt>Listener(<TypeEvt>Listener monDelegue)
comme par exemple la méthode prédéfinie addMouseListener utilisée de la
manière suivante : addMouseListener(MouseListener monDelegue)
. La première forme indique une diffusion simple (vers un seul délégué), et la deuxième permet une diffusion multiple.
L'exemple suivant montre une applet qui affiche une chaîne à l'endroit cliqué par
la souris :
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class Test3 extends Applet {
public Test3() {
MouseListener clic = new MouseAdapter () {
public void mousePressed (MouseEvent e)
{
int x = e.getX();
int y = e.getY();
System.out.println("x = " + x + " y = "+y);
getGraphics().drawString("salut tout le monde",x,y);
}
};
addMouseListener(clic);
}
public void init () {
this.setBackground(Color.white);
}
}
<TypeEvt>
correspond à un type d'évènement, par exemple MouseListener pour les évènements souris ou plus généralement ActionListener pour les évènements de type action.
Le délégué clic est une instance de la classe anonyme étendant la classe
MouseAdapter. Il traite les les évènements souris MouseListener,
respectant l'interface MouseListener et redéfinit la méthode mousePressed.
Il n'est pas nécessaire de définir une classe anonyme. Par exemple le texte suivant :
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class Test3 extends Applet {
class AdaptateurSouris extends MouseAdapter implements MouseListener {
public void mousePressed (MouseEvent e)
{
int x = e.getX();
int y = e.getY();
System.out.println("x = " + x + " y = "+y);
getGraphics().drawString("salut tout le monde",x,y);
}
}
public Test3() {
MouseListener clic = new AdaptateurSouris()
addMouseListener(clic);
}
public void init () {
this.setBackground(Color.white);
}
}
Enfin il est aussi possible d'implanter directement une interface Listener dans l'applet que l'on construit. Dans ce dernier cas, il est donc nécessaire d'écrire toutes les méthodes de l'interface implantée.
Il est fréquent d'utiliser une classe intermédiaire entre la source d'un évènement et le délegué qui traitera l'évènement. On appelle cet intermédiaire un adaptateur. Cela permet de rendre plus indépendant l'interface utilisateur du code de traitement. De plus l'adaptateur permet des opérations sur les évènements, de gérer une queue d'évènements et d'utiliser un seul délégué pour plusieurs sources d'évènements.
la diffusion d'un évènement à un ou des délégués, ou adaptateurs, dépend uniquement des objets, respectant l'interface Listener et abonnés à cette source. L'ordre de diffusion n'est pas garanti. Pour le garantir il est nécessaire de coder une chaine de délégués partant d'un adaptateur unique.
Le paquetage java.awt
définit des composants d'interaction fort
pratiques. On peut citer les labels, les boutons, les boites à choix multiple
ou exclusif, les menus déroulants, les listes de choix, les champs de texte
en entrée, les ascenseurs, les fonds, ...
L'exemple suivant reprend l'exemple du login et des mots de passe du TD4 :
Fichier : passwdTest.java
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class passwdTest extends Applet {
String monlogin ="tartempi";
String monpasswd ="itaparit";
TextField login;
TextField passwd;
boolean OK = false;
ActionListener RC = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if ((e.getSource() == login) || (e.getSource() == passwd))
{ if ((login.getText().equals(monlogin)) &&
(passwd.getText().equals(monpasswd)))
{OK=true; good();}
else {nogood();}
}
}
};
public void init() {
login = new TextField(8);
passwd = new TextField(8);
add(new Label("Login : "));
add(login);
add(new Label("Password : "));
passwd.setEchoChar('*');
add(passwd);
login.addActionListener(RC);
passwd.addActionListener(RC);
}
public void good() {
resize(120,180);
this.getGraphics().drawString("c'est parti...",10,150);
}
public void nogood() {
this.getGraphics().drawString("identification incorrecte",10,100);
}
}
Les composants login et passwd engendrent des évènements (comme par exemple un retour chariot) vers le délégué RC qui s'est enregistré auprès de ces deux composants par les messages addActionListener. La méthode actionPerformed traitera l'évènement action e en vérifiant bien que la source de l'évènement est un de ces deux composants.
On ajoute des composants par la méthode add envoyée à un conteneur, par exemple celui de la page graphique d'une applet, comme dans les exemples sur la saisie d'un mot de passe et la lecture d'une expression. Des protocoles de mise en page permettent de placer de manière précise les composants dans un conteneur.
Un conteneur (classe Container
) est un espace graphique permettant de recevoir plusieurs composants graphiques. Cette classe se découpe en deux sous-classes : les panneaux (Panel) dont la classe applet
hérite
et les fenêtres (Window). L'exemple du mot de passe montrait différents
composants graphiques dans l'espace graphique de l'applet.
La mise en page des conteneurs peut suivre différentes disciplines :
Flowlayout
: de gauche à droite, avec changement de ligne;
BorderLayout
: en indiquant cinq zones (North, South, East, West et Center);
GridLayout
: sous forme de grille avec changement de ligne;
CardLayout
: où un seul composant est affiché à la fois avec navigation;
GridBagLayout
: quadrillage
GridBagConstraint
: et contraintes.