Aller au menu - Aller au contenu

Icône Interaction bouton(s) - application

Mise à jour : 12/02/2010
Difficulté : Intermédiaire Intermédiaire Creative Commons BY-NC-SA
96 702 visites depuis 7 jours, dont 1 259 sur ce chapitre classé 4/786
Nous y voilà !
Dans ce chapitre, votre objet Bouton pourra enfin communiquer avec votre application !
Je pense tout de même que le chapitre précédent à dû vous plaire...

Nous allons voir comment faire, mais aussi qu'il y a plusieurs façons de faire...
Ensuite, il ne tiendra qu'à vous de bien choisir...

En avant, moussaillons. :pirate:
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

Déclencher une action : l'interface ActionListener

Tout est dans le titre ! :D
Afin de gérer les différentes actions à effectuer selon le bouton sur lequel on clique, nous allons utiliser l'interface ActionListener.

Cependant, nous n'allons pas implémenter cette dernière dans notre classe Bouton, mais dans notre classe Fenetre... Le but étant de faire en sorte que lorsque nous cliquons sur notre bouton, il se passe quelque chose dans notre application comme changer un état, une variable, faire une incrémentation... Enfin n'importe quelle action !

Comme je vous l'ai expliqué dans la partie précédente, lorsque nous faisons addMouseListener, nous prévenons l'objet observé qu'un objet doit être mis au courant ! Ici, nous voulons que ce soit notre application, notre Fenetre, qui écoute notre Bouton, le but final étant de pouvoir lancer ou d'arrêter l'animation de notre Panneau.

Avant d'en arriver là, nous allons faire plus simple. Nous allons voir dans un premier temps l'implémentation de l'interface ActionListener. Afin de vous montrer toute la puissance de cette interface, nous allons utiliser un nouvel objet présent dans le package javax.swing : le JLabel.
Cet objet est en fait comme une étiquette, il est spécialisé dans l'affichage de texte ou d'image... Il est donc parfait pour notre premier exemple !

Pour l'instanciation ou l'initialisation, il fonctionne un peu comme le JButton, voyez plutôt :

Code : Java
1
2
3
4
JLabel label1 = new JLabel();
label1.setText("mon premier JLabel");
//Ou encore
JLabel label2 = new JLabel("Mon deuxième JLabel");


Créez une variable d'instance de type JLabel - appelons-la label - initialisez-la avec le texte qui vous plaît, puis ajoutez-la avec votre contentPane en BorderLayout.NORTH.

Voici le résultat :

Image utilisateur


Et le 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
26
27
28
public class Fenetre extends JFrame {
 
    private Panneau pan = new Panneau();
    private Bouton bouton = new Bouton("mon bouton");
    private JPanel container = new JPanel();
    private JLabel label = new JLabel("Le JLabel");
   
    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);
            container.add(label, BorderLayout.NORTH);
           
            this.setContentPane(container);
            this.setVisible(true);
           
            go();
    }
        
    //...
}


Vous pouvez voir le texte en haut à gauche... L'alignement par défaut de cet objet est à gauche mais vous pouvez changer quelques paramètres, comme :
  • l'alignement
  • la police à utiliser
  • la couleur de police
  • ...


Voilà un code qui met tout ceci en pratique, et son aperçu.

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
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);
            
            //Définition d'une police d'écriture
            Font police = new Font("Tahoma", Font.BOLD, 16 );
            //On applique celle-ci aux JLabel
            label.setFont(police);
            //On change la couleur de police
            label.setForeground(Color.blue);
            //Et on change l'alignement du texte grâce aux attributs static de la classe JLabel
            label.setHorizontalAlignment(JLabel.CENTER);
            
            container.add(label, BorderLayout.NORTH);
           
            this.setContentPane(container);
            this.setVisible(true);
           
            go();
    }


Aperçu :

Image utilisateur


Maintenant que notre étiquette est exactement comme nous le voulons, nous allons pouvoir implémenter l'interface ActionListener.
Lorsque vous avez implémenté les méthodes de l'interface, vous vous apercevez que celle-ci n'en contient qu'une seule !

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
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
 
public class Fenetre extends JFrame implements ActionListener{
 
    private Panneau pan = new Panneau();
    private Bouton bouton = new Bouton("mon bouton");
    private JPanel container = new JPanel();
    private JLabel label = new JLabel("Le JLabel");
   
    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);
            
            //Définition d'une police d'écriture
            Font police = new Font("Tahoma", Font.BOLD, 16 );
            //On applique celle-ci aux JLabel
            label.setFont(police);
            //On change la couleur de police
            label.setForeground(Color.blue);
            //Et on change l'alignement du texte grâce aux attributs static de la classe JLabel
            label.setHorizontalAlignment(JLabel.CENTER);
            
            container.add(label, BorderLayout.NORTH);
           
            this.setContentPane(container);
            this.setVisible(true);
           
            go();
    }
        
 //...
 
//*******************************************************************************
//                             LA VOILAAAAAAAAAAAAAA
//*******************************************************************************
        /**
         * C'est la méthode qui sera appelée lors d'un clic sur notre bouton
         */
        public void actionPerformed(ActionEvent arg0) {
                                
        }
        
}


Nous allons maintenant prévenir notre objet Bouton que notre objet Fenetre l'écoute ! Vous l'avez deviné, nous ajoutons notre objet Fenetre à la liste des objets qui écoutent notre Bouton grâce à la méthode addActionListener(ActionListener obj) invoquée sur la variable bouton. Ajoutez cette instruction dans le constructeur, en passant this en paramètre (c'est notre Fenetre qui écoute notre bouton...).

Une fois ceci fait, nous allons changer le texte de notre JLabel dans la méthode actionPerformed ; en fait, nous allons compter combien de fois on clique sur notre bouton... Pour cela, nous ajoutons une variable d'instance de type int dans notre classe : appelons-la compteur. Dans la méthode actionPerformed, nous allons incrémenter ce compteur et afficher son contenu dans notre étiquette.

Voici le code de notre objet mis à jour :

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
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
 
public class Fenetre extends JFrame implements ActionListener{
 
    private Panneau pan = new Panneau();
    private Bouton bouton = new Bouton("mon bouton");
    private JPanel container = new JPanel();
    private JLabel label = new JLabel("Le JLabel");
    /**
     * Compteur de clics !
     */
    private int compteur = 0;
   
    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);
            
            //On ajoute notre Fenetre à la liste des auditeurs de notre Bouton
            bouton.addActionListener(this);
            
            container.add(bouton, BorderLayout.SOUTH);
            
            //Définition d'une police d'écriture
            Font police = new Font("Tahoma", Font.BOLD, 16 );
            //On applique celle-ci aux JLabel
            label.setFont(police);
            //On change la couleur de police
            label.setForeground(Color.blue);
            //Et on change l'alignement du texte grâce aux attributs static de la classe JLabel
            label.setHorizontalAlignment(JLabel.CENTER);
            
            container.add(label, BorderLayout.NORTH);
           
            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();
                }
        }
       
}
 
//*******************************************************************************
//                             LA VOILAAAAAAAAAAAAAA
//*******************************************************************************
        /**
         * C'est la méthode qui sera appelée lors d'un clic sur notre bouton
         */
        public void actionPerformed(ActionEvent arg0) {
                //Lorsque nous cliquons sur notre bouton, on met à jour le JLabel
                this.compteur++;
                label.setText("Vous avez cliqué " + this.compteur + " fois");
        }
        
}


Et le résultat :

Image utilisateur


On commence à faire du sérieux, là ! ! :D
Mais attendez, on ne fait que commencer... Eh oui ! Nous allons maintenant ajouter un deuxième bouton à notre Fenetre, à côté de notre premier bouton (vous êtes libres d'utiliser la classe personnalisée ou un JButton) ! Personnellement, je vais utiliser des boutons normaux maintenant ; en effet, la façon dont on écrit le nom de notre bouton, dans notre classe personnalisée, n'est pas assez souple et donc l'affichage peut être décevant... :-°

Bref, nous avons maintenant deux boutons écoutés par notre objet Fenetre.

Vous devez créer un deuxième JPanel qui va contenir nos deux boutons et insérer celui-ci dans le contentPane en BorderLayout.SOUTH.
Si vous tentez de mettre deux composants au même endroit avec un BorderLayout, seul le dernier composant ajouté apparaîtra ! Eh oui, le composant prend toute la place dans un BorderLayout !


Voilà notre 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
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
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
 
public class Fenetre extends JFrame implements ActionListener{
 
    private Panneau pan = new Panneau();
    private JButton bouton = new JButton("bouton 1");
    private JButton bouton2 = new JButton("bouton 2");
    private JPanel container = new JPanel();
    private JLabel label = new JLabel("Le JLabel");
    /**
     * Compteur de clics !
     */
    private int compteur = 0;
   
    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);
            
            //On ajoute notre Fenetre à la liste des auditeurs de notre Bouton
            bouton.addActionListener(this);
            bouton2.addActionListener(this);
            
            JPanel south = new JPanel();
            south.add(bouton);
            south.add(bouton2);
            container.add(south, BorderLayout.SOUTH);
            
            
            //Définition d'une police d'écriture
            Font police = new Font("Tahoma", Font.BOLD, 16 );
            //On applique celle-ci aux JLabel
            label.setFont(police);
            //On change la couleur de police
            label.setForeground(Color.blue);
            //Et on change l'alignement du texte grâce aux attributs static de la classe JLabel
            label.setHorizontalAlignment(JLabel.CENTER);
            
            container.add(label, BorderLayout.NORTH);
           
            this.setContentPane(container);
            this.setVisible(true);
           
            go();
    }
        
    //...
 
//*******************************************************************************
//                             LA VOILAAAAAAAAAAAAAA
//*******************************************************************************
        /**
         * C'est la méthode qui sera appelée lors d'un clic sur notre bouton
         */
        public void actionPerformed(ActionEvent arg0) {
                //Lorsque nous cliquons sur notre bouton, on met à jour le JLabel
                this.compteur++;
                label.setText("Vous avez cliqué " + this.compteur + " fois");
        }
        
}


Et le résultat :

Image utilisateur



Le problème maintenant est :
comment faire faire deux choses différentes dans la méthode actionPerformed ?

En effet ! Si nous laissons la méthode actionPerformed telle qu'elle est, les deux boutons auront la même action lorsque nous cliquerons dessus. Essayez, et vous verrez !

Il existe un moyen de savoir qui a déclenché l'événement, en utilisant l'objet passé en paramètre dans la méthode actionPerformed. Nous allons utiliser la méthode getSource() de cet objet pour connaître le nom de l'instance qui a généré l'événement.
Testez la méthode actionPerformed suivante, et voyez le résultat :

Code : Java
1
2
3
4
5
6
7
8
public void actionPerformed(ActionEvent arg0) {
                
                if(arg0.getSource() == bouton)
                        label.setText("Vous avez cliqué sur le bouton 1");
                
                if(arg0.getSource() == bouton2)
                        label.setText("Vous avez cliqué sur le bouton 2");
        }


Résultat :

Image utilisateur


Vous pouvez constater que notre code fonctionne très bien ! Mais cette approche n'est pas très orientée objet... Si vous avez une multitude de boutons sur votre IHM... vous allez avoir une méthode actionPerformed très chargée !
Nous pourrions créer deux objets à part, chacun écoutant un bouton, dont le rôle serait de faire un traitement précis par bouton... Cependant, si dans nos traitements nous avons besoin de modifier des données internes à la classe contenant nos boutons, il faudrait passer ces données (ou objets) à cet objet... Pas terrible non plus.

On commence à te connaître, maintenant ! Tu as une idée derrière la tête...

Je suis démasqué ! :p
Il existe en Java un type de classe particulière. Voyons ça tout de suite !

Parlez avec votre classe intérieure

En Java, vous pouvez faire ce qu'on appelle des classes internes.
Ceci consiste à déclarer une classe dans une classe ! Je sais, ça paraît tordu mais vous allez voir que c'est très pratique.

En effet, ces classes possèdent tous les avantages des classes normales, héritant d'une super-classe ou implémentant une interface, elles bénéficieront donc des bénéfices du polymorphisme et de la covariance des variables ! :D
En plus, elles ont l'avantage d'avoir accès aux attributs de la classe dans laquelle elles sont déclarées !

Dans le cas qui nous intéresse, ceci permet de faire une implémentation de l'interface ActionListener, détachée de notre classe Fenetre, mais pouvant utiliser ses attributs !
La déclaration d'une telle classe se fait exactement comme une classe normale, sauf qu'elle est dans une autre classe... Ce qui donne ceci :

Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class MaClasseExterne{
 
     public MaClasseExterne(){
          //....
     }
 
     class MaClassInterne{
 
          public MaClassInterne(){
               //...
          }
     }
}


Grâce à ceci, nous allons pouvoir faire une classe spécialisée dans l'écoute de composants et qui a un travail précis à faire ! Dans notre exemple, nous allons juste faire deux classes internes implémentant chacune l'interface ActionListener ; elles redéfiniront donc la méthode actionPerformed :
  • BoutonListener : qui écoutera le premier bouton
  • Bouton2Listener : qui écoutera le second !

Une fois que ceci est fait, il ne nous reste plus qu'à dire à chaque bouton : "qui l'écoute", avec la méthode addActionListener.

Voici la classe Fenetre mise à jour :

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
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
 
public class Fenetre extends JFrame{
 
    private Panneau pan = new Panneau();
    private JButton bouton = new JButton("bouton 1");
    private JButton bouton2 = new JButton("bouton 2");
    private JPanel container = new JPanel();
    private JLabel label = new JLabel("Le JLabel");
    private int compteur = 0;
   
    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);
            
            //Ce sont maintenant nos classes internes qui écoutent nos boutons 
            bouton.addActionListener(new BoutonListener());
            bouton2.addActionListener(new Bouton2Listener());
            
            JPanel south = new JPanel();
            south.add(bouton);
            south.add(bouton2);
            container.add(south, BorderLayout.SOUTH);
            
            Font police = new Font("Tahoma", Font.BOLD, 16 );
            label.setFont(police);
            label.setForeground(Color.blue);
            label.setHorizontalAlignment(JLabel.CENTER);
            
            container.add(label, BorderLayout.NORTH);
            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){
               
            if(x < 1)backX = false;
            if(x > pan.getWidth()-50)backX = true;               
            if(y < 1)backY = false;
            if(y > pan.getHeight()-50)backY = true;
            if(!backX)pan.setPosX(++x);
            else pan.setPosX(--x);
            if(!backY) pan.setPosY(++y);
            else pan.setPosY(--y);
            pan.repaint();
 
            try {
                    Thread.sleep(3);
            } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
            }
        }
       
        }
 
 
        /**
         * classe qui écoute notre bouton
         */
        class BoutonListener  implements ActionListener{
 
                /**
                 * Redéfinition de la méthode actionPerformed
                 */
                public void actionPerformed(ActionEvent arg0) {
                        label.setText("Vous avez cliqué sur le bouton 1");                     
                }
                
        }
        
        /**
         * classe qui écoute notre bouton2
         */
        class Bouton2Listener  implements ActionListener{
 
                /**
                 * Redéfinition de la méthode actionPerformed
                 */
                public void actionPerformed(ActionEvent e) {
                        label.setText("Vous avez cliqué sur le bouton 2");                     
                }
                
        }
                
        
        
}


Et le résultat est parfait :

Image utilisateur


Vous pouvez même constater que nos classes internes ont accès aux attributs déclarés private dans notre classe Fenetre !


Nous n'avons plus à nous soucier de qui a déclenché l'événement maintenant, car nous avons une classe qui écoute chaque bouton ! Nous pouvons souffler un peu, une grosse épine vient de nous être retirée du pied.

Vous le savez, mais vous pouvez faire écouter votre bouton par plusieurs classes... Il vous suffit d'ajouter les classes qui écoutent le boutons avec addActionListener.

Eh oui, faites le test...
Créez une troisième classe interne, peu importe son nom (moi, je l'appelle Bouton3Listener), implémentez l'interface ActionListener dans celle-ci et contentez-vous de faire un simple System.out.println dans la méthode actionPerformed. N'oubliez pas d'ajouter cette dernière à la liste des classes qui écoutent votre bouton (n'importe lequel des deux... Moi, j'ai choisi le premier).

Je ne vous donne que le code ajouté :

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
//Les imports
public class Fenetre extends JFrame{
    
     //Les variables d'instance...
      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);
            
            //Première classe écoutant mon bouton 
            bouton.addActionListener(new BoutonListener());
            //Deuxième classe écoutant mon bouton
            bouton.addActionListener(new Bouton3Listener());
            
            bouton2.addActionListener(new Bouton2Listener());
            
            JPanel south = new JPanel();
            south.add(bouton);
            south.add(bouton2);
            container.add(south, BorderLayout.SOUTH);
            
            Font police = new Font("Tahoma", Font.BOLD, 16 );
            label.setFont(police);
            label.setForeground(Color.blue);
            label.setHorizontalAlignment(JLabel.CENTER);
            
            container.add(label, BorderLayout.NORTH);
            this.setContentPane(container);
            this.setVisible(true);
           
            go();
    }
 
    //...
 
        class Bouton3Listener  implements ActionListener{
 
                /**
                 * Redéfinition de la méthode actionPerformed
                 */
                public void actionPerformed(ActionEvent e) {
                        System.out.println("Ma classe interne numéro 3 écoute bien !");               
                }
                
        }
}


Et le résultat :

Image utilisateur


Les classes internes sont vraiment des classes à part entière ! Elles peuvent aussi être héritées d'une super-classe... De ce fait, c'est presque comme si nous avions de l'héritage multiple, ça n'en est pas... Mais ça y ressemble. Donc, ce code est valide :

Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class MaClasseExterne extends JFrame{
 
     public MaClasseExterne(){
          //....
     }
 
     class MaClassInterne extends JPanel{
 
          public MaClassInterne(){
               //...
          }
     }
 
     class MaClassInterne2 extends JButton{
 
          public MaClassInterne(){
               //...
          }
     }
 
}


Rien ne vous empêche de faire de votre classe Panneau une classe interne ! :D


Vous voyez bien que ce genre de classes peuvent s'avérer très utile... :D
Bon. Nous avons réglé le problème d'implémentation, nous avons deux boutons qui sont écoutés, il ne nous reste plus qu'à lancer et arrêter notre animation avec ceux-ci !

Contrôler votre animation : lancement et arrêt

Nous attaquons la dernière ligne droite de ce chapitre.
Donc, pour réussir à gérer le lancement et l'arrêt de notre animation, nous allons devoir modifier un peu le code de notre classe Fenetre.
Il va falloir changer le libellé de ceux-ci, le premier affichera Go et le deuxième Stop et, pour éviter d'arrêter l'animation alors que celle-ci n'est pas lancée et ne pas l'animer alors qu'elle l'est déjà, nous allons tantôt activer / désactiver les boutons. Je m'explique.
  • Au lancement, l'animation est lancée. Le bouton Go ne sera pas cliquable alors que Stop, oui.
  • Si l'animation est stoppée, le bouton Stop ne sera plus cliquable et le bouton Go le sera.

Tu ne nous as pas dit comment on fait ça ! :colere2:

Je sais... Mais je vous cache encore pas mal de choses...
Ne vous inquiétez pas, c'est très simple à faire ; il existe une méthode pour faire ça :

Code : Java
1
2
3
JButton bouton = new JButton("bouton");
bouton.setEnabled(false); // Votre bouton n'est plus cliquable !
bouton.setEnabled(true); // Votre bouton de nouveau cliquable !


J'ajouterais que vous pouvez faire pas mal de choses avec ces objets ! Soyez curieux et testez les méthodes de ces objets, allez faire un tour sur le site de Sun Microsystems, fouillez, fouinez...
L'une des méthodes qui s'avère souvent utile et qui est utilisable pour tous ces objets (et les objets que nous verrons plus tard) est la méthode de gestion de dimension. Il ne s'agit pas de la méthode setSize(), mais la méthode setPreferredSize(Dimension preferredSize). Cette méthode prend un objet Dimension en paramètre qui lui, prend deux entiers en paramètre.

Voilà un exemple :

Code : Java
1
bouton.setPreferredSize(new Dimension(150, 120));


Ce qui donne sur notre application :

Image utilisateur


Retournons à nos moutons... :p
Afin de bien gérer notre animation, il va falloir améliorer notre méthode go().
Nous allons sortir de cette méthode les deux entiers dont nous nous servions afin de recalculer les coordonnées de notre rond et, concernant la boucle infinie, elle doit dorénavant pouvoir être stoppée !
Pour réussir ceci, nous allons déclarer une variable d'instance de type booléen qui changera d'état selon le bouton sur lequel nous cliquerons, et nous utiliserons celui-ci comme paramètre de notre boucle.

Voici le code de notre 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
 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
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
 
public class Fenetre extends JFrame{
 
    private Panneau pan = new Panneau();
    private JButton bouton = new JButton("Go");
    private JButton bouton2 = new JButton("Stop");
    private JPanel container = new JPanel();
    private JLabel label = new JLabel("Le JLabel");
    private int compteur = 0;
    private boolean animated = true;
    private boolean backX, backY;
    private int x,y ;
    
    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);
            
            //Ce sont maintenant nos classes internes qui écoutent nos boutons 
            bouton.addActionListener(new BoutonListener()); 
            bouton.setEnabled(false);
            bouton2.addActionListener(new Bouton2Listener());
            
            JPanel south = new JPanel();
            south.add(bouton);
            south.add(bouton2);
            container.add(south, BorderLayout.SOUTH);
            
            Font police = new Font("Tahoma", Font.BOLD, 16 );
            label.setFont(police);
            label.setForeground(Color.blue);
            label.setHorizontalAlignment(JLabel.CENTER);
            
            container.add(label, BorderLayout.NORTH);
            this.setContentPane(container);
            this.setVisible(true);
            go();
            
    }
        
        private void go(){
        //Les coordonnées de départ de notre rond
        x = pan.getPosX();
        y = pan.getPosY();
        //Pour cet exemple, j'utilise une boucle while
        //Vous verrez qu'elle fonctionne très bien
        while(this.animated){
                
            if(x < 1)backX = false;
            if(x > pan.getWidth()-50)backX = true;               
            if(y < 1)backY = false;
            if(y > pan.getHeight()-50)backY = true;
            
            
            if(!backX)pan.setPosX(++x);
            else pan.setPosX(--x);
            if(!backY) pan.setPosY(++y);
            else pan.setPosY(--y);
            pan.repaint();
 
            try {
                    Thread.sleep(3);
            } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
            }
        }       
        }
 
 
        /**
         * classe qui écoute notre bouton
         */
        class BoutonListener implements ActionListener{
 
                /**
                 * Redéfinitions de la méthode actionPerformed
                 */
                public void actionPerformed(ActionEvent arg0) {
                        animated = true;
                        bouton.setEnabled(false);
                        bouton2.setEnabled(true);
                        go();
                }
                
        }
        
        /**
         * classe qui écoute notre bouton2
         */
        class Bouton2Listener  implements ActionListener{
 
                /**
                 * Redéfinitions de la méthode actionPerformed
                 */
                public void actionPerformed(ActionEvent e) {
                        animated = false;       
                        bouton.setEnabled(true);
                        bouton2.setEnabled(false);
                }
                
        }       
}


À l'exécution, vous pouvez voir :
  • que le bouton Go n'est pas cliquable mais que l'autre l'est
  • que l'animation se lance
  • lorsque vous cliquez sur Stop, que l'animation s'arrête
  • que le bouton Go devient cliquable
  • et lors de votre tentative, que l'animation ne se lance pas ! o_O


Comment ça se fait ?

Comme je vous l'ai dit dans le chapitre sur les conteneurs, notre application, au démarrage de celle-ci, lance un thread : le processus principal de votre programme !
Au démarrage, l'animation est lancée, celle-ci dans le même thread que notre objet Fenetre.
Lorsque nous lui demandons de s'arrêter, aucun souci : les ressources mémoire sont libérées, on sort de la boucle infinie et l'application continue son court !
Mais lorsque nous redemandons à l'animation de se lancer... L'instruction dans la méthode actionPerformed appelle la méthode go et, vu que nous sommes dans une boucle infinie : on reste dans la méthode go et on ne sort plus de la méthode actionPerformed de notre bouton ! :waw:

Voici l'explication de ce phénomène



Java gère les appels aux méthodes selon ce qu'on appelle vulgairement : La pile !

Pour vous expliquer ceci, nous allons prendre un exemple tout bête ; regardez cet objet :

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
public class TestPile {
 
        public TestPile(){
                System.out.println("Début constructeur");
                methode1();
                System.out.println("Fin constructeur");
        }
        
        public void methode1(){
                System.out.println("Début méthode 1");
                methode2();
                System.out.println("Fin méthode 1");
        }
        
        public void methode2(){
                System.out.println("Début méthode 2");
                methode3();
                System.out.println("Fin méthode 2");
        }
        
        public void methode3(){
                System.out.println("Début méthode 3");
                System.out.println("Fin méthode 3");
        }
        
}


Si vous instanciez cet objet, vous obtiendrez dans votre console ceci :

Image utilisateur


Je suppose que vous avez remarqué, avec stupéfaction, que l'ordre des instructions est un peu bizarre !
Voici ce qu'il s'est passé :
  • à l'instanciation, notre objet appelle la méthode1 ;
  • cette dernière invoque la méthode2 ;
  • celle-ci utilise la méthode3 et une fois terminée, la JVM retourne dans la méthode2 ;
  • celle-ci terminée, on remonte à la fin de la méthode1... jusqu'à la dernière instruction appelante : le contructeur.


Lors de tous les appels, on dit que la JVM empile les invocations sur la pile ! Et, une fois la dernière méthode empilée terminée, la JVM dépile celle-ci !


Voilà un schéma résumant la situation :

Image utilisateur


Dans notre programme, imaginez que la méthode actionPerformed est représentée, dans le schéma ci-dessus, par méthode2 et que notre méthode go soit représentée par méthode3. Lorsque nous entrons dans méthode3, nous entrons dans une boucle infinie ! La conséquence directe : on ne ressort jamais de cette méthode et la JVM ne dépile plus !

Comment faire, alors ?

Nous allons utiliser une nouveau thread ! En gros, grâce à ceci, la méthode go se trouvera dans une pile à part !

Attends... On arrive pourtant à arrêter l'animation alors qu'elle est dans une boucle infinie ? Pourquoi ?

Tout simplement parce que nous demandons de faire une bête initialisation de variable dans la gestion de notre événement ! Si vous faites une deuxième méthode comprenant une boucle infinie et que vous l'invoquez lors du clic sur Stop, vous aurez exactement le même problème !

Je ne vais pas m'éterniser là-dessus tout de suite... Nous allons voir ça au prochain chapitre ! ;)
Mais avant, je pense qu'un TP serait le bienvenu... Histoire de réviser un peu !
Vous êtes d'accord ? Alors, direction le topo, le QCM, et en avant pour un TP ! :pirate:

Cadeau : votre bouton personnalisé optimisé !

Vu que je vous ai fait un peu saliver pour rien... je vous donne le code java d'un bouton corrigeant le problème d'écriture du libellé. Je ne ferai pas trop de commentaire à son sujet, tout est dans le 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
 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
125
126
127
128
129
130
131
132
133
134
import java.awt.Color;
import java.awt.FontMetrics;
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);
             
             //Objet qui permet de connaître les propriétés d'une police, dont la taille !
             FontMetrics fm = g2d.getFontMetrics();
             //Hauteur de la police d'écriture
             int height = fm.getHeight();
             //Largeur totale de la chaîne passée en paramètre
             int width = fm.stringWidth(this.name);
             
             //On calcule donc la position du texte dans le bouton, et le tour est joué !
             g2d.drawString(this.name, this.getWidth() / 2 - (width / 2), (this.getHeight() / 2) + (height / 4));
            
     }
 
        @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)
                {
                        try {
                                 img = ImageIO.read(new File("fondBoutonHover.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("fondBouton.png"));
                        } catch (IOException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                        }
                }               
        }
        
}


Essayez et vous verrez ! :p

Ce qu'il faut retenir

  • L'interface utilisée dans ce chapitre est : ActionListener. Celle-ci est présente dans le package java.awt.
  • La méthode de cette interface à implémenter est actionPerformed.
  • Lorsque vous avez plusieurs composants pouvant être écoutés, préférez faire un objet écouteur par composants.
  • Vous pouvez, et c'est conseillé, utiliser des classes internes pour écouter vos composants.
  • Une classe interne est une classe à l'intérieur d'une classe.
  • Une telle classe a accès à toutes les données et méthodes de sa classe externe.
  • Ce type de classe peut être déclarée private.
  • La JVM traite les méthodes appelées par une pile de méthode, définissant ainsi l'ordre d'exécution de celles-ci.
  • Une méthode est empilée à l'invocation de celle-ci, mais n'est dépilée que lorsque toutes ses intructions sont terminées !
  • Vous connaissez la méthode permettant d'écrire dans un JLabel, vous pouvez aussi récupérer le texte d'un tel objet en utilisant la méthode getText().


Une classe interne privée ?

Oui, parce que vous pouvez créer une instance de la classe interne à l'extérieur de sa classe externe, comme ceci :
Code : Java
1
2
Fenetre.BoutonListener boutonL;
boutonL = new Fenetre().new BoutonListener();


Par contre, vous ne pouvez pas déclarer de variable static dans une classe interne... Mais une classe interne peut être déclarée static.

Q.C.M.

Quel est le nom de l'interface abordée dans ce chapitre ?
Quel est la méthode à implémenter si on utilise cette interface ?
Quel est le type d'objet en paramètre de la méthode actionPerformed ?
Que va afficher le code suivant ?

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
public class Test{
        public static void main(String[] args) {
        
                affiche1();
        }       
        
        static void affiche1(){
                System.out.print("1;");
                affiche2();
        }
        
        static void affiche2(){         
                affiche3();
                System.out.print("2;");
        }
        
        static void affiche3(){         
                affiche4();
                System.out.print("3;");
        }
        
        static void affiche4(){
                System.out.print("4;");
                
        }
}

Statistiques de réponses au QCM

Encore un chapitre de bouclé !
Vous tenez toujours bon ?

À tout de suite pour votre premier TP en événementiel !
Chapitre précédent Sommaire Chapitre suivant

Partager

20 commentaires pour "Interaction bouton(s) - application"
Note moyenne : 3.57 / 4 (1025 votes)
Pseudo Commentaire
Hors ligne slifer # Posté le 03/09/2010 à 02:49:20

Études : Faculté des Sciences Ain Chock Casblanca

ce tuto est bien parfait pour des débutantes (dont moi bien évidement) , cysboy n'a pas dit que c'est la seule méthode possible de lancer et réanimer, je viens de remarquer que Ptitlue a trouver quelque chose de très proche de ce que j'ai pu inventé ^^, en effet ce que je propose c'est de ne jamais mettre la méthode "go()" dans actionPerformed, par contre on met un boolean qui sera un attribue de la Fenetre et qui change à chaque fois qu'on clique sur un bouton "if(e.getSource()==GO) animated=true; si l'autre il reçoit "false".
D'un autre coté dans la méthode go() il y aura une boucle infinie "for(;;)" ou "while(true)" et juste après on déclare et initialise un boolean à la valeur de notre attribue (ex : boolean B = animated;) et après une condition "if (B) (incrémentation ==> animation); else (faire rien);

PS: l'intérêt de déclarer le boolean a l'intérieur de la boucle est très important, c'est ça ce qui empêche le plantage de la pile d'exécution (Thread), si quelque malin veulent s'en passer et faire "if (animated)" ils tomberont sur le même problème de plantage(par expérience ^^) et voila finalement mon code :

Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
private boolean animated;
public void go(){
        for(;;){
boolean B=animated;
        if(B){les incrémentation;} // j'ai ma propre méthode d'animation alors 
                                 // j'ai pensé que ça pourra faire confusion si je la mette
        else
            ; // ce n'est pas obligatoire ce else
}
    }

    public void actionPerformed(ActionEvent ev){
        if(ev.getSource()==GO)
            animated=true;
        if(ev.getSource()==STOP)
            animated=false;
    }
Hors ligne Nicolas M. # Posté le 20/09/2010 à 20:25:20
M(NiCoLaSm) = 406,9 g/mol
Avatar

Ville : Notre-dame de bondeville
Pays : France métropolitaine

Citation : Le tuto
Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class MaClasseExterne extends JFrame{
 
     public MaClasseExterne(){
          //....
     }
 
     class MaClassInterne extends JPanel{
 
          public MaClassInterne(){
               //...
          }
     }
 
     class MaClassInterne2 extends JButton{
 
          public MaClassInterne(){
               //...
          }
     }
 
}

Il semblerait qu'il manque un « 2 » dans le nom du constructeur de MaClassInterne2... À moins que tu n'aies voulu créer une méthode MaClassInterne dans MaClassInterne2 ?.. :o

Autre chose, il serait bon de préciser comment on accède aux attributs de la classe « externe » depuis une classe interne ; ClasseExterne.this.attributDeClasseExterne , ça ne s'invente pas ! :-°

Image utilisateur Image utilisateur

Le saviez-vous ? Les forums sont environ 283 174 fois plus efficaces que ma boîte MP pour vous aider. ;)
Image utilisateur
 
Hors ligne cowboy. # Posté le 10/08/2011 à 04:40:59
qui risque rien gagne rien
Avatar

Bonjour a tous les zeros et Felicitation pour l'auteur .
je tiens a temoigner qu' apres avoir fait une 2eme lecture de ce chapitre ,j'ai pu parfaitement digerer le contenu.
j'ai plutot une reproche a faire ,Mais rassurer vous ,c'est adressee aux zeros que nous sommes :pirate: :
je trouve que ce cour possede une strategie DIDACTIQUE :p ; et par consequent essayez de comprendre " le pour et le pourquoi ".
ex : a la fin du cour , il faut bien reconnaitre que c'est sublime, puisque on sort du chapitre
avec en tete que dans le suivant on aborderait enfin une" go dans une thread a part... :p on sait aussi que la jvm empile et ne depile que lorsque la derniere,empilee , se termine :p .
il suffit de voir la derniere question du QCM, reponse 1-4-3-2 ;etant donnee que la 4 est infini.
cela implique qu' elle n'est pas terminee vu son arret via bouton stop, et donc la JVM ne depilera pas la 4.------> les autre methodes ne fonctionnerait pas etc etc etc
Bref BRAVO CYSBOY Ce tuto est un delice dans le vrais sens du terme ,avis de chimiste zero informatique :-°
MERCI POUR TES EFFORT
on a besoin de personne comme vous ,grande pertinence dans les explication...Bravo

Grand Zero que je suis ! :waw: en grandissant ,je serai un petit zeroo! .... puis je recommecerai a zero :p
 
Hors ligne thefone # Posté le 15/09/2011 à 10:12:48

tu as oublié un truc ptitlu:

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
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);
        
      //Ce sont maintenant nos classes internes qui écoutent nos boutons 
        bouton.addActionListener(new BoutonListener());
        bouton2.addActionListener(new Bouton2Listener());
        
        JPanel south = new JPanel();
        south.add(bouton);
        south.add(bouton2);
        container.add(south, BorderLayout.SOUTH);
        
        bouton.setEnabled(false);
        
        //Définition d'une police d'écriture
        Font police = new Font("Tahoma", Font.BOLD, 16 );
        //On applique celle-ci aux JLabel
        label.setFont(police);
        //On change la couleur de police
        label.setForeground(Color.blue);
        //Et on change l'alignement du texte grâce aux attributs static de la classe JLabel
        label.setHorizontalAlignment(JLabel.CENTER);
        
        container.add(label, BorderLayout.NORTH);
       
        this.setContentPane(container);
        this.setVisible(true);
       
        for(;;){
        	while(animated)
        		go();  
        	try {
                Thread.sleep(1);
        	} catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
        	}  
        }

}


là ça devrait marcher. par contre je ne comprend pas pourquoi il faut ce temps d'arrêt après le while du go. :(
Hors ligne Jambo2 # Posté le 22/01/2012 à 01:14:07

Citation : diegodelpy
A propos du Blocage quand on clique sur le bouton Go après avoir stoppé l'animation.
On ne rentre pas dans une boucle sans fin, mais effectivement on ne peut pas sortir de BoutonListener.actionPerformed.
Mais l'animation devrait reprendre et ce n'est pas le cas.
Est-ce parce que repaint est en fait un équivalent de "Invalidate" dans Delphi (et sûrement d'autres langages) et que le message (ordre de se redessinner) ne sera reçu et exécuté que aprés être sorti de BoutonListener.actionPerformed.
Mais alors pourquoi le "redessin" est effectif lors du premier lancement de Go, sans que l'on sorte de Go. Est-ce dû à l'utilisation d'une classe interne.
Dans Delphi, en mettant Application.ProcessMessages, les messages sont traités. Y a-t-il un équivalent en Java ? ProcessMessages est très lié à Windows.


Très bon poste. C'est à peu près ca.

Le problème n'est pas la boucle infinie. Même si la boucle était finie nous ne verrions pas d'animation, mais un changement brusque de la position initiale à la position finale. Le vrai souci ici est que repaint() fait un appel à EventQueue.invokeLater() et diffère l'action de dessin, ce qui a pour effect de changer le controle du programme de :

calcul, dessin, calcul, dessin, calcul, dessin...

en cette séquence :

calcul, calucl, calcul..., dessin, dessin, dessin

c'est pourquoi la balle apparaitrait à sa position finale : au moment où la routine de dessin est amenée à s'exécuter la premiere fois, toutes les étapes de calcul auront été faites et les coordonnées auront pris les valeurs finales.


Le dessin est effectif lors du premier lancement de Go car cet appel n'est pas fait depuis le thread qui traite les évènements, mais depuis le thread que la JVM crée pour lancer la méthode main (et donc y instancie la fenetre et y invoque Go). Voici ce qui se passe dans chaque thread :

Thread Main : calcul, calcul, calcul, calcul,...
Thread des évènements : dessin, dessin, dessin...

Voir tous les commentaires