LECON 304


Votre premier bouton
Voici l'un des moments que vous attendiez avec impatience !
Vous allez enfin pouvoir mettre un bouton dans votre application...
Mais ne chantez pas trop vite. Vous allez effectivement utiliser un bouton, mais voir aussi que les choses se compliquent dès que vous utilisez ce genre de composants... Et encore plus lorsque vous en utilisez plusieurs !

Toujours prêts ?
Go, alors.
Sommaire du chapitre :

  • Utiliser la classe JButton
  • Les layout managers
  • Continuons dans notre lancée
  • Une classe Bouton personnalisée
  • Interaction avec la souris : l'interface MouseListener
  • Ce qu'il faut retenir
Utiliser la classe JButton
Comme le titre l'indique, nous allons utiliser un objet JButton. Celui-ci se trouve aussi dans le package javax.swing.

Au cours de ce chapitre, nous allons un peu mettre de côté notre objet Panneau : en fait, notre projet précédent dans sa globalité sera mis à l'écart...

Créez un nouveau projet avec :
  • une classe contenant une méthode main : appelons-la Test ;
  • une classe héritée de JFrame (avec tout ce qu'on a déjà mis avant, sauf la méthode go()) : appelons-la Fenetre.

Je sais, j'aime donner dans l'originalité...

Dans votre classe Fenetre, nous allons créer une variable d'instance de type JPanel et une de type JButton.
Faites de votre JPanel le contentPane de votre Fenetre. Ce qui doit nous donner ceci :

Classe Fenetre


Code : Java -

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
 
 
public class Fenetre extends JFrame{
 
        private JPanel pan = new JPanel();
        private JButton bouton = new JButton();
        
        public Fenetre(){
                
                this.setTitle("Animation");
                this.setSize(300, 300);
                this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                this.setLocationRelativeTo(null);
                this.setContentPane(pan);
                this.setVisible(true);
                
        }       
}

Classe Test

Code : Java -

1
2
3
4
5
6
7
public class Test {
 
        public static void main(String[] args){
                Fenetre fen = new Fenetre();
        }
        
}


Jusque-là, rien de bien nouveau mis à part l'instance de JButton, mais ce genre de choses ne doit plus vous épater, maintenant !

Définissons à présent un libellé à notre bouton et mettons-le sur... Sur... Ce qui nous sert de contentPane ! Notre JPanel, en l'occurrence !

Pour donner un libellé à un bouton, il existe deux méthodes, les voici :
Code : Java -
1
2
3
4
5
//Méthode 1 : Instanciation avec le libellé
JButton bouton = new JButton("Mon bouton");
//Méthode 2 : Instanciation puis définition du libellé
JButton bouton2 = new JButton();
bouton2.setText("Mon deuxième bouton");


Personnellement, je préfère la première... Mais là n'est pas la question.
Il ne nous reste plus qu'à ajouter notre bouton sur notre contentPane et ceci, grâce à la méthode add() de l'objet JPanel.

Voici donc notre code :
Code : Java -
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
 
 
public class Fenetre extends JFrame{
 
        private JPanel pan = new JPanel();
        private JButton bouton = new JButton("Mon bouton");
        
        public Fenetre(){
                
                this.setTitle("Animation");
                this.setSize(300, 300);
                this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                this.setLocationRelativeTo(null);
                
                //Ajout du bouton à notre contentPane
                pan.add(bouton);
                
                this.setContentPane(pan);
                this.setVisible(true);
                
        }       
}


Et le résultat :

Alors ! Heureux ?
Je ne sais pas si vous avez remarqué mais... votre bouton est centré sur votre conteneur !
Ceci vous semble normal ? Sachez tout de même que votre JPanel fait de la mise en page par défaut.
Qu'est-ce que tu entends par là ?

En fait, en java, il existe des objets qui servent à agencer vos composants, et les objets JPanel en ont un par défaut !
Pour vous le prouver, je vais vous faire travailler sur le contentPane de votre JFrame.
Quoi ? On peut faire ça ?

Bien sûr, mais il était de bon ton de vous faire découvrir l'objet Graphics avant de vous jeter dans la fosse aux lions...
Vous allez voir que pour obtenir le même rendu que précédemment sur le contentPane de votre JFrame, nous allons être obligés d'utiliser un de ces fameux objets d'agencements !

Tout d'abord, pour utiliser le contentPane d'une JFrame, on doit appeler la méthode getContentPane(), qui retourne ce dernier, auquel nous ajoutons nos composants. Voici le nouveau code :

Code : Java -
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
 
 
public class Fenetre extends JFrame{
 
        private JPanel pan = new JPanel();
        private JButton bouton = new JButton("Mon bouton");
       
        public Fenetre(){
               
                this.setTitle("Bouton");
                this.setSize(300, 300);
                this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                this.setLocationRelativeTo(null);
               
                //On ajoute le bouton au contentPane de la JFrame
                this.getContentPane().add(bouton);
               
                this.setVisible(true);
               
        }      
}


Voyez que le résultat n'est pas du tout le même :


Votre bouton est ÉNORME ! En fait, il prend toute la place disponible, et ceci parce que le contentPane de votre JFrame ne possède pas d'objet d'agencement...
Ces fameux objets, dont vous ne pourrez plus vous passer, s'appellent des layout managers.

Faisons un petit tour d'horizon de ces objets et voyons comment ils fonctionnent !

Les layout managers

Bon, vous allez voir qu'il existe plusieurs sortes de layout managers et que selon votre choix, il sera plus ou moins facile à utiliser... Je vais être méchant. Nous verrons le plus simple en dernier !
Vous devez aussi savoir que tous ces layout managers se trouvent dans la package : java.awt.

L'objet

BorderLayout

Le premier que nous aborderons est le BorderLayout. Celui-ci est très pratique si vous voulez placer vos composants de façon simple par rapport à une position cardinale de votre conteneur. Si je parle de positionnement cardinal, c'est parce que vous allez utiliser les positions :
  • NORTH
  • SOUTH
  • EAST
  • WEST
  • CENTER.


Mais je sais qu'un aperçu vaut mieux qu'un exposé sur le sujet. Alors voilà un exemple mettant en oeuvre un BorderLayout.


Cette fenêtre est composée de cinq JButton positionnés aux cinq endroits différents qu'offre un BorderLayout.

Voici le code de cette fenêtre :
Code : Java -
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import java.awt.BorderLayout;
 
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
 
 
public class Fenetre extends JFrame{
 
        private JButton bouton = new JButton("BorderLayout.NORTH");
       
        public Fenetre(){
               
                this.setTitle("Bouton");
                this.setSize(300, 300);
                this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                this.setLocationRelativeTo(null);
               
                //On définit le layout à utiliser sur le contentPane
                this.setLayout(new BorderLayout());
                
                //On ajoute le bouton au contentPane de la JFrame
                //Au centre
                this.getContentPane().add(new JButton("CENTER"), BorderLayout.CENTER);
                //Au nord
                this.getContentPane().add(new JButton("NORTH"), BorderLayout.NORTH);
                //Au sud
                this.getContentPane().add(new JButton("SOUTH"), BorderLayout.SOUTH);
                //À l'ouest
                this.getContentPane().add(new JButton("WEST"), BorderLayout.WEST);
                //À l'est
                this.getContentPane().add(new JButton("EAST"), BorderLayout.EAST);
                
               
                this.setVisible(true);
               
        }      
}


Ce n'est pas très difficile à comprendre. Vous définissez le layout à utiliser avec la méthode setLayout() de l'objet JFrame ; ensuite, pour chaque composant que vous souhaitez positionner avec add(), vous utilisez un attribut static de la classe BorderLayout en deuxième paramètre.
La liste des ces paramètres est celle citée plus haut...
Avec l'objet BorderLayout, vos composants sont soumis à des contraintes. Pour une position NORTH ou SOUTH, votre composant prendra la taille nécessaire en hauteur mais prendra toute la largeur ! Pour WEST et EAST, celui-ci prendra la largeur nécessaire mais toute la hauteur ! Et bien sûr pour CENTER, tout l'espace est utilisé !

L'objet

GridLayout

Celui-ci permet d'ajouter des composants suivant une grille définie par un nombre de lignes et de colonnes. Les éléments sont disposés depuis la case située en haut à gauche. Dès qu'une ligne est remplie, on passe à la suivante.
Si nous définissons une grille de 3 lignes et de 2 colonnes, voici ce que nous aurions :


Voici le code de cet exemple :

Code : Java -
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.awt.GridLayout;
 
import javax.swing.JButton;
import javax.swing.JFrame;
 
 
public class Fenetre extends JFrame{
 
        private JButton bouton = new JButton("BorderLayout.NORTH");
       
        public Fenetre(){
               
                this.setTitle("Bouton");
                this.setSize(300, 300);
                this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                this.setLocationRelativeTo(null);
               
                //On définit le layout à utiliser sur le contentPane
                //3 lignes sur 2 colonnes
                this.setLayout(new GridLayout(3, 2));
                
                //On ajoute le bouton au contentPane de la JFrame
                this.getContentPane().add(new JButton("1"));
                this.getContentPane().add(new JButton("2"));
                this.getContentPane().add(new JButton("3"));
                this.getContentPane().add(new JButton("4"));
                this.getContentPane().add(new JButton("5"));
               
                this.setVisible(true);
               
        }      
}


Ce code ne diffère pas beaucoup du précédent. Les seules différences résident dans le fait que nous utilisons un autre layout manager et qu'il n'y a pas besoin de définir le positionnement lors de l'ajout du composant avec la méthode add().

Vous devez aussi savoir que vous pouvez définir le nombre de lignes et le nombre de colonnes avec des méthodes de l'objet :
Code : Java -
1
2
3
4
5
6
GridLayout gl = new GridLayout();
gl.setColumns(2);
gl.setRows(3);
this.setLayout(gl);
 
// ou en abrégé : GridLayout gl = new GridLayout(3, 2);


Vous pouvez aussi spécifier des espaces entre les colonnes et entre les lignes.
Code : Java -
1
2
3
4
5
GridLayout gl = new GridLayout(3, 2);
gl.setHgap(5); // 5 pixels d'espace entre les colonnes (H comme Horizontal)
gl.setVgap(5); // 5 pixels d'espace entre les lignes (V comme Vertical)
 
//ou en abrégé : GridLayout gl = new GridLayout(3, 2, 5, 5);

Ce qui donnerait :

L'objet

FlowLayout

Celui-ci est certainement le plus facile à utiliser ! Il ne fait que centrer les composants dans le conteneur. Regardez plutôt :


Ah ! On dirait bien que nous venons de trouver le layout manager défini par défaut dans les objets JPanel.
Si vous mettez plusieurs composants avec ce gestionnaire, dès que la place est devenu trop étroite, il passe à la ligne inférieure. Voyez plutôt :


Il existe encore deux types de layout managers :
  • le CardLayout
  • le GridBagLayout.


Je suis dans l'incapacité de vous en parler car je ne les ai encore jamais utilisés... Sachez seulement que le dernier cité est le plus précis, mais aussi le plus compliqué à utiliser... Si vous êtes intéressés par ces deux gestionnaires, vous pouvez trouver des renseignements ici pour le CardLayout et pour le GridBagLayout.

Il faut que vous sachiez que les IHM ne sont en fait qu'une imbrication de composants les uns dans les autres, positionnés avec des layout managers. Vous allez voir tout de suite de quoi je veux parler...

Continuons dans notre lancée

Vous avez vu comment utiliser les layout managers ; nous allons donc continuer à jouer avec nos composants.
Vous savez :
  • créer une fenêtre
  • faire un conteneur personnalisé
  • placer celui-ci dans votre fenêtre
  • créer un bouton et le placer sur votre fenêtre.


Nous allons maintenant utiliser notre conteneur personnalisé et un bouton.
Vous pouvez revenir dans votre projet contenant notre animation créée dans les chapitres précédents.

Le but est de mettre notre animation au centre de notre fenêtre et un bouton en bas de celle-ci. Comme ceci :


Je vous laisse réfléchir quelques minutes, vous avez tous les outils pour y arriver !

Voilà le nouveau code de notre classe Fenetre :

Code : Java
import java.awt.BorderLayout;
import java.awt.Color;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;


public class Fenetre extends JFrame{

        private Panneau pan = new Panneau();
        private JButton bouton = new JButton("mon bouton");
        private JPanel container = new JPanel();
        
        public Fenetre(){
                
                this.setTitle("Animation");
                this.setSize(300, 300);
                this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                this.setLocationRelativeTo(null);
                
                container.setBackground(Color.white);
                container.setLayout(new BorderLayout());
                container.add(pan, BorderLayout.CENTER);
                container.add(bouton, BorderLayout.SOUTH);
                
                this.setContentPane(container);
                this.setVisible(true);
                
                go();
        }
        
        
        private void go(){
                
                //Les coordonnées de départ de notre rond
                int x = pan.getPosX(), y = pan.getPosY();
                //Le booléen pour savoir si on recule ou non sur l'axe X
                boolean backX = false;
                //Le booléen pour savoir si on recule ou non sur l'axe Y
                boolean backY = false;
                
                //Pour cet exemple, j'utilise une boucle while
                //Vous verrez qu'elle fonctionne très bien
                while(true){
                        
                        //Si la coordonnée x est inférieure à 1, on avance
                        if(x < 1)backX = false;
                        //Si la coordonnée x est supérieure à la taille du panneau 
                        //moins la taille du rond, on avance
                        if(x > pan.getWidth()-50)backX = true;
                        
                        //idem pour l'axe Y
                        if(y < 1)backY = false;
                        if(y > pan.getHeight()-50)backY = true;
                        
                        //Si on avance, on incrémente la coordonnée
                        if(!backX)
                                pan.setPosX(++x);
                        //Sinon, on décrémente
                        else
                                pan.setPosX(--x);
                        
                        //Idem pour l'axe Y
                        if(!backY)
                                pan.setPosY(++y);
                        else
                                pan.setPosY(--y);
                                
                        //On redessine notre panneau
                        pan.repaint();
                        
                        //Comme on dit : la pause s'impose ! Ici, 3 centièmes de secondes
                        try {
                                Thread.sleep(3);
                        } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                        }
                }
                
        }
        
}


Assez facile... N'est-ce pas ?
Avant de voir comment les boutons interagissent avec l'application, je vous propose de voir comment personnaliser ceux-ci !

Une classe Bouton personnalisée

Tout comme dans le deuxième chapitre, nous allons faire une classe : appelons-la Bouton, héritée (dans notre cas, nous allons faire hériter notre classe de javax.swing.JButton), et nous allons redéfinir la méthode paintComponent. Vous devriez y arriver tous seuls.

Pour cet exemple j'ai obtenu ceci :


Voici la classe Bouton de cette application :

Code : Java -
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
 
import javax.swing.JButton;
 
 
public class Bouton extends JButton {
 
        private String name;
        
        public Bouton(String str){
                super(str);
                this.name = str;
        }
        
        public void paintComponent(Graphics g){
                
                Graphics2D g2d = (Graphics2D)g;
                
                GradientPaint gp = new GradientPaint(0, 0, Color.blue, 0, 20, Color.cyan, true);
                g2d.setPaint(gp);
                g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
                
                g2d.setColor(Color.white);
                g2d.drawString(this.name, this.getWidth() / 2 - (this.getWidth() / 2 /4), (this.getHeight() / 2) + 5);
                
        }
        
        
}


J'ai aussi fait un bouton personnalisé avec une image de fond (un png...) que voici :


Et voilà le résultat :


C'est sympa aussi !
J'ai appliqué l'image sur l'intégralité du fond comme je l'ai montré lorsque nous nous amusions avec notre Panneau. Voici le code de la classe Bouton :

Code : Java -
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;
 
import javax.imageio.ImageIO;
import javax.swing.JButton;
 
 
public class Bouton extends JButton{
 
         private String name;
     private Image img;
         
     public Bouton(String str){
             super(str);
             this.name = str;
             
             try {
                     img = ImageIO.read(new File("fondBouton.png"));
                } catch (IOException e) {
                        e.printStackTrace();
                }
                        
 
     }
    
     public void paintComponent(Graphics g){
                 
             Graphics2D g2d = (Graphics2D)g;
            
             GradientPaint gp = new GradientPaint(0, 0, Color.blue, 0, 20, Color.cyan, true);
             g2d.setPaint(gp);
            // g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
            g2d.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
             
             g2d.setColor(Color.black);
             g2d.drawString(this.name, this.getWidth() / 2 - (this.getWidth() / 2 /4), (this.getHeight() / 2) + 5);
            
     }
 
}

Bien sûr, ladite image est à la racine de mon projet !


Rien de bien compliqué jusqu'à maintenant... Et c'est à partir de là que les choses vont devenir intéressantes !
Que diriez-vous si je vous proposais de pouvoir changer l'aspect de votre objet au passage de la souris, lorsque vous cliquez dessus, et même lorsque vous relâchez le clic ?
On peut faire ça ?

Bien entendu !
Il existe des interfaces à implémenter qui permettent de gérer toutes sortes d'événements sur votre IHM.
Le principe de fonctionnement est un peu déroutant au premier abord, mais il est assez simple lorsqu'on a un peu pratiqué. N'attendons pas plus et voyons ça de plus près !

Interaction avec la souris : l'interface MouseListener

Avant de nous lancer dans l'implémentation de cette dernière, nous allons voir ce que nous allons obtenir à la fin :


Vous avez vu ce que nous allons obtenir, mais vous allez tout de même passer par un peu de théorie avant d'arriver à ce résultat.
Je ne me suis pas encore attardé sur le sujet (et je ne le ferai toujours pas), mais, pour arriver à détecter les événements qui surviennent à votre composant, Java utilise ce qu'on appelle le design pattern Observer.

Je ne vous l'expliquerai pas dans le détail tout de suite, une partie concernant les design patterns sera rédigée...
En fait, un dp est un modèle de conception, une idée directrice afin d'avoir des programmes stables, réutilisables à souhait et paramétrables au possible !

Le design pattern Observer consiste en un modèle qui permet à des objets de se tenir au courant automatiquement. Ceci se fait par le biais d'interfaces que les objets devant se tenir au courant doivent implémenter.
Pour le moment, retenez qu'un objet observable implémente une interface communément appelée Observable et qu'un observateur va, lui, implémenter une interface communément appelée Observer (ceci dans le cas où nous utilisons le pattern Observer fourni avec Java ; oui, ces interfaces existent...). Le principe est simple :
  • l'objet observable va avoir un ou plusieurs objets observateurs
  • lorsqu'une donnée change dans un objet observable, il met tous ses objets observateurs abonnés à sa "news letter" au courant !

Ce principe, adapté aux composants swing, permet aux objets composants d'être écoutés et donc de pouvoir faire réagir votre application en conséquence !

Ne nous attardons pas sur le sujet, nous y reviendrons.
Maintenant que vous avez une très vague idée de la façon dont Java gère les événements, nous allons commencer à gérer les passages de notre souris sur notre objet !

Comme vous l'avez sûrement deviné, vous allez devoir implémenter l'interface MouseListener dans votre classe Bouton. Utilisez l'astuce d'Eclipse vue lors des implémentations d'interfaces, afin de générer automatiquement les méthodes à implémenter.

Nous aurons aussi à dire à notre classe Bouton qu'elle va devoir tenir quelqu'un au courant de ses changements d'état par rapport à la souris. Ce quelqu'un n'est autre... qu'elle-même !
Eh oui... Notre classe va s'écouter elle-même, donc ; dès que notre objet observable, notre bouton, va avoir des informations concernant les actions de la souris, il va dire à l'objet qui l'observe, lui-même, ce qu'il doit faire !

Ceci se fait grâce à la méthode addMouseListener(MouseListener obj) qui prend un objet MouseListener en paramètre ; ici, elle prendra this. Rappelez-vous que vous pouvez utiliser le type d'une interface comme super-type : ici, notre classe implémente l'interface MouseListener , nous pouvons donc utiliser cet objet comme référence de cette interface !

Voici notre classe Bouton à présent :

Code : Java -
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;
 
import javax.imageio.ImageIO;
import javax.swing.JButton;
 
 
public class Bouton extends JButton implements MouseListener{
 
         private String name;
     private Image img;
         
     public Bouton(String str){
             super(str);
             this.name = str;
             
             try {
                                 img = ImageIO.read(new File("fondBouton.png"));
                        } catch (IOException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                        }
                        
                        //Avec cette instruction, notre objet va s'écouter lui-même
                        //Dès qu'un événement de la souris sera intercepté, il sera au courant !
                        this.addMouseListener(this);
     }
    
     public void paintComponent(Graphics g){
                 
             Graphics2D g2d = (Graphics2D)g;
            
             GradientPaint gp = new GradientPaint(0, 0, Color.blue, 0, 20, Color.cyan, true);
             g2d.setPaint(gp);
            // g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
            g2d.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
             
             g2d.setColor(Color.black);
             g2d.drawString(this.name, this.getWidth() / 2 - (this.getWidth() / 2 /4), (this.getHeight() / 2) + 5);
            
     }
 
        /**
         * Méthode appelée lors du clic de souris
         */
        public void mouseClicked(MouseEvent event) {
        
        }
 
        /**
         * Méthode appelée lors du survol de la souris
         */
        public void mouseEntered(MouseEvent event) {
                
        }
 
        /**
         * Méthode appelée lorsque la souris sort de la zone du bouton
         */
        public void mouseExited(MouseEvent event) {
                
        }
 
        /**
         * Méthode appelée lorsque l'on presse le clic gauche de la souris
         */
        public void mousePressed(MouseEvent event) {
                
        }
 
        /**
         * Méthode appelée lorsque l'on relâche le clic de souris
         */
        public void mouseReleased(MouseEvent event) {
                        
        }       
}


C'est par le biais de ces différentes méthodes que nous allons gérer les différentes images à dessiner dans notre objet ! Mais rappelez-vous que : Même si vous n'utilisez pas toutes les méthodes d'une interface, vous DEVEZ tout de même mettre le squelette des méthodes non utilisées (avec les accolades), et ceci est aussi valable pour les classes abstraites.
Dans notre cas, la méthode repaint() est appelée de façon tacite. Lorsqu'un événement est déclenché, notre objet se redessine automatiquement ! Comme lorsque vous redimensionnez votre fenêtre dans les premiers chapitres...


Avec cette information, nous n'avons plus qu'à changer notre image selon la méthode invoquée :
  • notre objet aura une teinte jaune lors du survol de la souris
  • une teinte orangée lorsque l'on pressera le clic gauche
  • reviendra à la normale si on relâche le clic.

Pour ce faire, voici les fichiers .png dont je me suis servi (mais rien ne vous empêche de les faire vous-mêmes).

Je vous rappelle que dans le code qui suit, les fichiers images sont mis à la racine du projet ! !


Maintenant, voici le code de notre classe Bouton personnalisée :

Code : Java -
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;
 
import javax.imageio.ImageIO;
import javax.swing.JButton;
 
 
public class Bouton extends JButton implements MouseListener{
 
         private String name;
     private Image img;
         
     public Bouton(String str){
             super(str);
             this.name = str;
             
             try {
                                 img = ImageIO.read(new File("fondBouton.png"));
                        } catch (IOException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                        }
                        
                        this.addMouseListener(this);
     }
    
     public void paintComponent(Graphics g){
                 
             Graphics2D g2d = (Graphics2D)g;
            
             GradientPaint gp = new GradientPaint(0, 0, Color.blue, 0, 20, Color.cyan, true);
             g2d.setPaint(gp);
            // g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
            g2d.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
             
             g2d.setColor(Color.black);
             g2d.drawString(this.name, this.getWidth() / 2 - (this.getWidth() / 2 /4), (this.getHeight() / 2) + 5);
            
     }
 
        @Override
        public void mouseClicked(MouseEvent event) {
                 //Pas utile d'utiliser cette méthode ici                      
        }
 
        @Override
        public void mouseEntered(MouseEvent event) {
                
                //Nous changeons le fond en jaune pour notre image lors du survol
                //avec le fichier fondBoutonHover.png
                try {
                         img = ImageIO.read(new File("fondBoutonHover.png"));
                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
                
        }
 
        @Override
        public void mouseExited(MouseEvent event) {
 
                //Nous changeons le fond en vert pour notre image lorsqu'on quitte le bouton
                //avec le fichier fondBouton.png
                try {
                         img = ImageIO.read(new File("fondBouton.png"));
                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
                
        }
 
        @Override
        public void mousePressed(MouseEvent event) {
 
                //Nous changeons le fond en orangé pour notre image lors du clic gauche
                //avec le fichier fondBoutonClic.png
                try {
                         img = ImageIO.read(new File("fondBoutonClic.png"));
                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
                
        }
 
        @Override
        public void mouseReleased(MouseEvent event) {
                
                //Nous changeons le fond en orangé pour notre image 
                //lorsqu'on relâche le clic 
                //avec le fichier fondBoutonHover.png
                try {
                         img = ImageIO.read(new File("fondBoutonHover.png"));
                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }               
        }       
}


Et voilà le travail ! Il va de soi que si vous avez fait un clic-droit / Enregistrer-sous sur mes images, elles ne doivent pas avoir le même nom que sur mon morceau de code... Vous DEVEZ mettre le nom que vous leur avez donné... Je sais ça va de soi... Mais on ne sait jamais !

Et maintenant, vous avez un bouton personnalisé qui réagit aux passages de souris !
Mais un bémol se profile à l'horizon...
Lequel ? L'objet marche très bien !

Je sais qu'il va y avoir des p'tits malins qui vont cliquer sur le bouton et relâcher le clic en dehors du bouton !
Dans ce cas, vous devez voir que le fond du bouton est jaune, vu que c'est ce que nous avons demandé de faire à notre méthode mouseReleased ! Pour palier à ce problème, nous allons vérifier que, lorsque le clic est relâché, la souris est toujours sur le bouton.
Comment tu vas réussir à faire ça ? J'ai eu beau regarder dans les méthodes proposées par notre objet, mais il n'y a rien qui nous permette de faire ça !

Je sais... Mais vous n'avez pas regardé au bon endroit !
Maintenant que nous avons implémenté l'interface MouseListener, il y a un autre objet que nous n'avons pas encore utilisé... Vous ne le voyez pas ? C'est le paramètre présent dans toutes les méthodes de cette interface ! Oui, c'est MouseEvent.

Grâce à cet objet, nous pouvons avoir beaucoup de renseignements sur les événements. Nous ne détaillerons pas tout ici mais vous verrez quelques utilités de ces types d'objets tout au long de cette partie !
Dans notre cas, nous pouvons récupérer la position X et Y de notre souris par rapport à notre Bouton, tout ceci avec les méthodes getX() et getY(). Donc, si nous relâchons notre clic de souris en dehors de la zone où se trouve notre objet, la valeur retournée par l'une des méthodes getX() ou getY() sera négative ou supérieure aux dimensions du bouton !

Voici enfin le code final de notre classe Bouton :

Code : Java -
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;
 
import javax.imageio.ImageIO;
import javax.swing.JButton;
 
 
public class Bouton extends JButton implements MouseListener{
 
         private String name;
     private Image img;
         
     public Bouton(String str){
             super(str);
             this.name = str;
             
             try {
                                 img = ImageIO.read(new File("fondBouton.png"));
                        } catch (IOException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                        }
                        
                        this.addMouseListener(this);
     }
    
     public void paintComponent(Graphics g){
                 
             Graphics2D g2d = (Graphics2D)g;
            
             GradientPaint gp = new GradientPaint(0, 0, Color.blue, 0, 20, Color.cyan, true);
             g2d.setPaint(gp);
            // g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
            g2d.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
             
             g2d.setColor(Color.black);
             g2d.drawString(this.name, this.getWidth() / 2 - (this.getWidth() / 2 /4), (this.getHeight() / 2) + 5);
            
     }
 
        @Override
        public void mouseClicked(MouseEvent event) {
                 //Pas utile d'utiliser cette méthode ici                      
        }
 
        @Override
        public void mouseEntered(MouseEvent event) {
                
                //Nous changeons le fond en jaune pour notre image lors du survol
                //avec le fichier fondBoutonHover.png
                try {
                         img = ImageIO.read(new File("fondBoutonHover.png"));
                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
                
        }
 
        @Override
        public void mouseExited(MouseEvent event) {
 
                //Nous changeons le fond en vert pour notre image lorsqu'on quitte le bouton
                //avec le fichier fondBouton.png
                try {
                         img = ImageIO.read(new File("fondBouton.png"));
                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
                
        }
 
        @Override
        public void mousePressed(MouseEvent event) {
 
                //Nous changeons le fond en orangé pour notre image lors du clic gauche
                //avec le fichier fondBoutonClic.png
                try {
                         img = ImageIO.read(new File("fondBoutonClic.png"));
                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
                
        }
 
        @Override
        public void mouseReleased(MouseEvent event) {
                
                //Nous changeons le fond en orangé pour notre image 
                //lorsqu'on relâche le clic 
                //avec le fichier fondBoutonHover.png   
                
                //Si on est à l'extérieur de l'objet, on dessine le fond par défaut
                if(event.getY() < 0 || event.getX() < 0 || event.getY() > this.getHeight() || event.getX() > this.getWidth())
                {
                        try {
                                 img = ImageIO.read(new File("fondBouton.png"));
                        } catch (IOException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                        }
                }
                //Sinon on met le fond jaune, la souris est encore dessus...
                else
                {
                        try {
                                 img = ImageIO.read(new File("fondBoutonHover.png"));
                        } catch (IOException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                        }
                }               
        }
        
}

Vous allez voir tout au long des chapitres qui suivent qu'il y a différentes interfaces pour les différentes actions possibles sur une IHM ! Sachez seulement qu'il y a une convention aussi pour ces interfaces.
Leur nom commence par le type d'action ensuite suivi du mot Listener. Nous avons vu ici les actions de la souris, et voyez le nom de l'interface : MouseListener


Voilà ! Ce chapitre est clos !
Eh ! Attends... Notre bouton ne sais toujours rien faire !

C'est justement le sujet du prochain chapitre. Passez faire un tour sur le topo de cette partie et si vous avez un score raisonnable au QCM, vous pourrez y aller...

Ce qu'il faut retenir

  • Un bouton s'utilise avec la classe JButton présente dans le package javax.swing.
  • Pour ajouter un bouton à une fenêtre, vous devez utiliser la méthode add() de son contentPane.
  • Il existe des objets afin de pouvoir positionner nos composants sur un contentPane ou un conteneur : les layout managers.
  • Les layout managers se trouvent dans le package java.awt.
  • Le layout manager par défaut d'un objet JPanel est le FlowLayout.
  • Outre le FlowLayout, vous avez aussi le BorderLayout, le GridLayout, le CardLayout et le GridBagLayout. Il en existe sûrement d'autres sur le net... mais je ne les connais pas...
  • On définit un layout sur un conteneur grâce à la méthode setLayout().
  • Vous pouvez interagir sur un composant avec votre souris en implémentant l'interface MouseListener.
  • Lorsque vous implémentez une interface <...>Listener, vous dites à votre classe qu'elle doit se préparer à observer des événements du type de l'interface... Vous devez donc spécifier QUI doit observer et QUE doit-elle observer, avec les méthodes du type add<...>Listener(<...>Listener).
Toujours motivés pour continuer ?
Je ne vous ai pas trop fait peur ?

Très bien, alors voyons : Interaction bouton(s) - application.



0 comments to "LECON 304"

Post a Comment

Powered by Blogger.

About This Blog

Aller au debut de la page