9. INTERFACES GRAPHIQUES
AVEC SWING
 
 
Précédent Suivant
 
Interfaces graphiques
avec Swing
AWT et Swing
* AWT (Abstract Window Toolkit)
* Package "java.awt"
* Figé depuis 1.1
* Structure relativement simple
* Swing
* Package "javax.swing"
* Toujours en évolution
* Structure plus complexe qu'AWT
* Surcouche d'AWT (héritage des composants AWT)
Types de composants graphiques
* Composants de haut-niveau
* JFrame: fenêtre
* JDialog: boîte de dialogue
* JApplet: "applet" (application embarquée dans HTML)
* Composants de niveau intermédiaire
* Destinés à recevoir d'autres composants
* JPanel: "panneau", zone où placer des composants
* JScrollPane: zone avec ascenseur
* JSplitPane: zone partagée en deux
* ...
* Composants élémentaires
* JButton: bouton
* JLabel: texte
* JComboBox: menu déroulant
* ...
Composants de niveau intermédiaire
Composants élémentaires
Classe "JComponent"
* Composants intermédiaire et bas niveaux héritent de "JComponent"
* Tout composant Swing
* Est susceptible d'en contenir d'autres
* Possède les propriétés d'un composant conteneur AWT
* Documentation
* Fournie par Sun (dans Google: "java api 1.5")
* Consulter les classes: Component, Container, JComponent
Ouverture d'une fenêtre
* Exemple
import javax.swing.JFrame;
import java.awt.Dimension;
public class Fenetre extends JFrame {
public Fenetre(String titre,int x,int y,
int largeur,int hauteur) {
setTitle(titre);
setPreferredSize(new Dimension(largeur,hauteur));
setLocation(new java.awt.Point(x,y));
pack();
}
public static void main(String args[]) {
JFrame fenetre = new Fenetre("Nawouak",100,100,200,200);
fenetre.setVisible(true);
}
}
* "pack" dimensionne la fenêtre
* En fonction de sa taille préférée
* Et de la taille et du placement de chaque composant qu'elle contient
Ajout de composants
* Les composants de haut-niveau ont un conteneur principal
* Container cp = this.getContentPane();
* Exemple
public Fenetre(...) {
...
Container cp = getContentPane();
JLabel label = new JLabel("Bonjour !");
JButton bouton = new JButton("Cliquer ici");
cp.add(label,BorderLayout.NORTH);
cp.add(bouton,BorderLayout.SOUTH);
pack();
}
* Placement des composants
* Les positions précises ne sont pas données
* Seulement des indications: "NORTH" et "SOUTH"
* Un gestionnaire de placement détermine les positions précises
Gestionnaires de placement (1/2)
* L'utilisateur peut changer la taille d'une fenêtre
==> les composants doivent être repositionnés
* A la programmation, éviter un ordre absolu
* Seules des indications sont fournies
* Sur la taille et le positionnement des composants
* Chaque conteneur possède un gestionnaire de placement
* Aussi appelé "layout manager"
* Objet chargé de repositionner les composants
* En fonction de règles propres
* En tentant de satisfaire les indications fournies
* Plusieurs types de gestionnaires de placement
* BorderLayout: placement en bordure
* FlowLayout: placement en flot
* GridLayout: placement en grille
* BoxLayout: placement en pile
* ...
* Combinaison de gestionnaires ==> imbrication de conteneurs
Gestionnaires de placement (2/2)
* Critères de placement d'un gestionnaire
* Algorithme de placement propre au gestionnaire
* Indications de positionnement des composants
* Contraintes de taille du conteneur
* Contraintes de taille des composants
* Expression des contraintes de taille
* Taille minimale: composant.setMinimumSize(dimensions);
* Taille maximale: composant.setMaximumSize(dimensions);
* Taille préférée: composant.setPreferredSize(dimensions);
* Dimensions modélisées par la classe "java.awt.Dimension"
* Attributs: width, height
* Constructeur: Dimension(largeur,hauteur)
* Définir le gestionnaire
* conteneur.setLayout(new classe_gestionnaire(paramètres));
* Placement des composants
* Automatique au redimensionnement de la fenêtre
* En appelant la méthode "pack" de la fenêtre
Placement en bordure (1/2)
* Gestionnaire: BorderLayout
* Permet de placer au maximum 5 composants
* 4 en bordure (nord, sud, est, ouest)
* 1 au centre
* Tente de respecter
* La hauteur préférée du nord et du sud
* La largeur préférée de l'est et de l'ouest
* Le centre occupe le reste
* Gestionnaire par défaut de "JFrame"
Placement en bordure (2/2)
* Exemple
cp.setLayout(new BorderLayout());
JButton bouton1 = new JButton("B1");
...
JButton bouton5 = new JButton("B5");
bouton1.setPreferredSize(new Dimension(100,100));
cp.add(bouton1,BorderLayout.NORTH);
cp.add(bouton2,BorderLayout.SOUTH);
cp.add(bouton3,BorderLayout.EAST);
cp.add(bouton4,BorderLayout.WEST);
cp.add(bouton5,BorderLayout.CENTER);
pack();
Placement en flot (1/2)
* Gestionnaire: FlowLayout
* Permet un placement comme dans un texte
* Selon l'ordre d'ajout
* De gauche à droite
* Puis, de haut en bas
* Alignement précisé à la construction
* FlowLayout.LEFT
* FlowLayout.RIGHT
* FlowLayout.CENTER
* Composants affichés à leur taille préférée
* Gestionnaire par défaut de "JPanel" et "JApplet"
Placement en flot (2/2)
* Exemple
cp.setLayout(new FlowLayout(FlowLayout.RIGHT));
JButton bouton1 = new JButton("B1");
...
JButton bouton4 = new JButton("B4");
bouton1.setPreferredSize(new Dimension(80,30));
bouton2.setPreferredSize(new Dimension(80,40));
bouton3.setPreferredSize(new Dimension(80,30));
bouton4.setPreferredSize(new Dimension(80,30));
cp.add(bouton1);
cp.add(bouton2);
cp.add(bouton3);
cp.add(bouton4);
pack();
Placement en grille (1/2)
* Gestionnaire: GridLayout
* Permet un placement sur une grille
* Selon l'ordre d'ajout
* De gauche à droite
* Puis, de haut en bas
* Nombre de lignes ou de colonnes fixé
* Indications fournies au constructeur
* new GridLayout(lignes,colonnes)
* lignes > 0 ==> colonnes ignoré
* lignes = 0 ==> lignes ignoré
* La dimension ignorée est adaptée automatiquement
* Les composants ont tous la même taille
* Ils occupent toute la place de leur cellule dans la grille
Placement en grille (2/2)
* Exemple
cp.setLayout(new GridLayout(3,3));
JButton bouton1 = new JButton("B1");
...
JButton bouton4 = new JButton("B4");
bouton1.setPreferredSize(new Dimension(80,10));
bouton2.setPreferredSize(new Dimension(80,10));
bouton3.setPreferredSize(new Dimension(80,10));
bouton4.setPreferredSize(new Dimension(80,10));
cp.add(bouton1);
cp.add(bouton2);
cp.add(bouton3);
cp.add(bouton4);
pack();
Placement en pile (1/2)
* Gestionnaire: BoxLayout
* Permet d'empiler des composants
* Soit sur une ligne: BoxLayout.Y_AXIS
* Soit sur une colonne: BoxLayout.X_AXIS
* Direction d'empilement fournie au constructeur
* new BoxLayout(conteneur,direction)
* Attention: conteneur fourni à la construction
* Alignement des composants
* Fixé pour chaque composant: setAlignmentX
* Doit être le même pour tous
* Composants affichés à leur taille maximale
* Gestionnaire par défaut de "JToolBar"
Placement en pile (2/2)
* Exemple
cp.setLayout(new BoxLayout(cp,BoxLayout.Y_AXIS));
JButton bouton1 = new JButton("B1");
JButton bouton2 = new JButton("B2");
JButton bouton3 = new JButton("B3");
bouton1.setMaximumSize(new Dimension(50,20));
bouton2.setMaximumSize(new Dimension(70,20));
bouton3.setMaximumSize(new Dimension(60,20));
bouton1.setAlignmentX(Component.RIGHT_ALIGNMENT);
bouton2.setAlignmentX(Component.RIGHT_ALIGNMENT);
bouton3.setAlignmentX(Component.RIGHT_ALIGNMENT);
cp.add(bouton1);
cp.add(bouton2);
cp.add(bouton3);
pack();
Les événements
* Evénements élémentaires
* Souris
* Appui d'un bouton
* Déplacement du curseur
* Clavier
* Appui d'une touche
* Saisie d'un caractère
* Evénements avancés
* Actions
* Sélection d'un item de menu
* Clic sur un composant bouton
* Evénements système
* Fermeture fenêtre
* Redimensionnement composant
Principe de l'écouteur
* Capter un événement ==> observer, "écouter"
* Objet chargé d'écouter: "écouteur" (ou "listener")
* Ecouter ==> s'enregistrer auprès de l'objet surveillé
* Evénement e déclenché sur l'objet ==> tous les écouteurs informés
* Informations sur l'événement encapsulées dans un objet o
* Objet o transmis à tous les objets écouteurs
* Méthode e() appelée pour tous les écouteurs
* Objet o passé en paramètre
* Les écouteurs doivent donc respecter une interface commune
Interface "Listener"
* A chaque type d'événement = une interface "Listener"
* MouseListener, KeyListener, WindowListener...
* Définition d'une classe qui implémente l'interface
class MonEcouteur implements MouseListener {
void mouseClicked(MouseEvent e) { ... }
void mouseEntered(MouseEvent e) { ... }
void mouseExited(MouseEvent e) { ... }
void mousePressed(MouseEvent e) { ... }
void mouseReleased(MouseEvent e) { ... }
}
* L'écouteur appartient à cette classe
* MouseListener ecouteur = new MonEcouteur();
* Enregistrement de l'écouteur auprès du composant
* composant.addMouseListener(ecouteur);
* Inconvénient: toutes les méthodes doivent être redéfinies
Classe "Adapter" (1/2)
Classe "Adapter" (2/2)
* Chaque interface "Listener" ==> une classe "Adapter" (ou adapteur)
* Interface "MouseListener" ==> classe "MouseAdapter"
* Classe adapteur = classe vide implémentant l'interface associée
* class MouseAdapter implements MouseListener
* Toutes les méthodes sont vides
* Il suffit de dériver la classe adapteur
* Redéfinition des méthodes nécessaires uniquement
* class MonEcouteur extends MouseAdapter {
public void mousePressed(MouseEvent e) { ... }
}
* Conseil: déclarer l'adapteur en classe "interne"
* A l'intérieur de la classe concernée
* Il accède ainsi aux membres cachés de la classe
* Si usage unique ==> classe "anonyme"
Classes imbriquées (ou "nested classes")
* Classe imbriquée = classe déclarée dans une autre
class A {
class B { ... }
...
}
* Accès à la classe imbriquée B par la classe englobante A
* Dans la classe A, B est accessible directement
* A l'extérieur, il faut écrire "A.B"
* Mêmes règles de visibilité que les attributs et les méthodes
* Relation d'amitié forte
* A voit les membres privés de B
* B voit les membres privés de A
* Deux types de classes imbriquées en Java
* Classe imbriquée statique ("static nested class")
* Par abus de langage, "nested class" peut être employé
* C++ ne fournit que ce type d'imbrication
* Classe interne ("inner class")
Classe imbriquée statique (1/2)
* Utilisation du mot-clé "static"
class A {
static class B { ... }
...
}
* Classe "normale" placée dans une autre classe
* Les différences
* Classe B dans l'espace de nommage de A
* Amitié forte entre A et B
* Les instances de A et B n'ont aucun lien ==> pas d'agrégation
* Accès réciproque aux membres d'instance impossible
* Accès réciproque aux membres de classe naturellement possible
Classe imbriquée statique (2/2)
public class ClasseA {
private int a;
private static int b;
public static class ClasseB {
private int x;
private static int y;
public void f() { System.out.println(a); } ==> interdit, aucune instance
public void g() { System.out.println(b); } ==> ok, membre de classe
public void h() {
ClasseA m = new ClasseA();
System.out.println(m.a); ==> ok, passage par une instance + relation d'amitié
System.out.println(m.b); ==> ok, membre de classe + relation d'amitié
}
}
public static void test() {
ClasseB e = new ClasseB();
System.out.println(e.x); ==> ok, passage par une instance + relation d'amitié
System.out.println(e.y); ==> ok, membre de classe + relation d'amitié
}
}
Classe interne (1/3)
* Sans mot-clé "static"
class A {
class B { ... }
}
* Caractéristiques similaires à la classe imbriquée statique
* Classe B dans l'espace de nommage de A
* Amitié forte entre A et B
* Accès réciproque aux membres de classe
* Différence: toute instance de B est liée à une instance de A
* Seule une instance de A peut créer une instance de B
* Agrégation: chaque instance de B fait partie d'une instance de A
* Agrégation ==> accès réciproque aux membres d'instance
* Une classe interne ne peut pas avoir de membres de classe
Classe interne (2/3)
public class ClasseA {
private int a;
private static int b;
public class ClasseB {
private int x;
private static int y; ==> interdit
public void f() { System.out.println(a); } ==> ok, accès par une instance
public void g() { System.out.println(b); }
}
public static void test1() {
ClasseB e = new ClasseB(); ==> interdit, aucune instance
ClasseA m = new ClasseA();
ClasseB e = m.new ClasseB(); ==> ok, création par une instance
}
public void test2() {
ClasseB e = new ClasseB(); ==> ok, création par une instance (~ this.new)
System.out.println(e.x);
}
}
Classe interne (3/3)
* Classe interne ==> problème de distinction des membres
* Classe A possède une classe interne B
* A et B possèdent toutes les deux un attribut x
* Comment faire la distinction dans B ?
* this.x ==> attribut de l'instance de classe B
* A.x ==> attribut de l'instance de classe A
* Même question avec "this"
* this ==> instance de classe B
* A.this ==> instance de classe A
* Classe anonyme = classe interne sans nom
* Classe à usage unique
* Spécification / héritage à la construction
* monAdapteur = new MouseAdapter() {
public void mousePressed(MouseEvent e)
{ /* Redéfinition */ }
};
Evénements souris (1/2)
* Ecouteur: MouseListener
* void mousePressed(MouseEvent)
* Bouton enfoncé
* void mouseReleased(MouseEvent)
* Bouton relâché
* void mouseClicked(MouseEvent)
* Clic de souris = bouton enfoncé puis relâché
* void mouseEntered(MouseEvent)
* Arrivée sur un composant
* void mouseExited(MouseEvent)
* Sortie d'un composant
* Adapteur: MouseAdapter
* Ajout: addMouseListener(...)
Evénements souris (2/2)
* Ecouteur: MouseMotionListener
* void mouseDragged(MouseEvent)
* Déplacement de la souris avec bouton enfoncé
* void mouseMoved(MouseEvent)
* Déplacement de la souris sans bouton enfoncé
* Adapteur: MouseMotionAdapter
* Ajout: addMouseMotionListener(...)
* Evénement: MouseEvent
* int getX() / int getY()
* Coordonnées du curseur (relatives au composant qui capte)
* int getButton()
* Bouton qui a changé d'état
Evénements clavier
* Ecouteur: KeyListener
* void keyPressed(KeyEvent): Touche enfoncée
* void keyReleased(KeyEvent): Touche relâchée
* void keyTyped(KeyEvent): Saisie d'un caractère
* Adapteur: KeyAdapter
* Ajout: addKeyListener(...)
* Evénement: KeyEvent
* char getKeyChar()
* Retourne le caractère saisi
* int getKeyCode()
* Retourne le code de la touche
Evénements fenêtre
* Ecouteur: WindowListener
* void windowClosing(WindowEvent)
* Tentative de fermeture de la fenêtre
* void windowOpened(WindowEvent)
* Première apparition de la fenêtre
* Adapteur: WindowAdapter
* Exemple: fermeture de la fenêtre
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
dispose();
System.exit(0);
}
});
Actions
* Plusieurs événements peuvent déclencher la même action
* Exemple: un bouton et une option de menu
* Possibilité de définir un objet "action"
* Héritage de la classe "AbstractAction"
* Redéfinition de la méthode "actionPerformed"
* actionQuitter = new AbstractAction("Quitter") {
public void actionPerformed(ActionEvent e) {
dispose();
System.exit(0);
}
};
* L'action peut ensuite être associée à un ou plusieurs événements
* Pour certains composants, un événement déclenche une action
* Clic sur un composant bouton ==> exécution de l'action associée
* Association de l'action: bouton.setAction(actionQuitter);
* Evénement ==> appel de la méthode "actionPerformed" de l'action
Dessiner sur un composant (1/2)
* De préférence, on dessine sur un objet de la classe "JPanel"
* Dessiner ==> redéfinition de "paintComponent"
* void paintComponent(Graphics g)
* Méthode appelée dès que le composant doit être redessiné
* Ne doit pas être appelée directement
* Pour forcer le réaffichage: appeler "repaint"
* Méthode asynchrone
* N'est pas forcément exécutée tout de suite
* Plusieurs appels dans un instant très court ==> une seule exécution
* Lors de la redéfinition, toujours appeler la super-classe
* Première ligne: super.paintComponent(g);
Dessiner sur un composant (2/2)
* Pour dessiner, il faut un contexte graphique
* Classe "Graphics"
* Objet passé en paramètre à la méthode "paintComponent"
* Possibilité aussi de le demander à un composant
* composant.getGraphics();
* Permet d'effectuer des opérations de dessin
* Couleur: setColor(Color)
* Code RGB: Color(int r,int g,int b)
* Couleur prédéfinie: Color.YELLOW
* Fonte: setFont(Font)
* Tracé
* drawLine(x1,y1,x2,y2): ligne
* drawRect(x,y,l,h) / fillRect(x,y,l,h): rectangle vide / plein
* drawOval(x,y,l,h) / fillOval(x,y,l,h): ellipse vide / pleine
* drawString(texte,x,y): texte
* drawImage(image,x,y,this): image
Manipulation des images
* Charger une image
* Image i = Toolkit.getDefaultToolkit().getImage(url);
* Localiser l'image ==> obtenir une URL
* Fournir un chemin relatif
* Recherche dans les chemins du "classpath"
* Application autonome
* URL url = getClass().getResource(chemin_fichier);
* Applet
* URL url = new URL(getCodeBase(),chemin_fichier);
* Attendre le chargement d'une image
MediaTracker tracker = new MediaTracker(this);
tracker.addImage(image,id);
try { tracker.waitForID(id); }
catch(InterruptedException e) {}
 
 
Copyright (c) 1999-2016 - Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
La permission est accordée de copier, distribuer et/ou modifier ce document sous les termes de la licence GNU Free Documentation License, Version 1.1 ou toute version ultérieure publiée par la fondation Free Software Foundation. Voir cette licence pour plus de détails (http://www.gnu.org).