Aller au menu - Aller au contenu

[Plan du site] Vous êtes ici --- > Le Site du Zéro > Les tutoriels > Officiels > Programmation > Programmation en Java > Java et la programmation événementielle > Les champs de texte : l'objet JTextField > Lecture du tutoriel

Les champs de texte : l'objet JTextField

Avatar
Auteur : cysboy
Difficulté : Connaisseur (3 / 5)
Visualisations : 8 861

Plus d'informations Plus d'informations
Dans ce chapitre, nous allons voir l'objet qui va vous permettre de saisir des informations.
Celui-ci est très simple d'utilisation aussi...

Ne perdons pas de temps, allons-y ! :pirate:
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

Utilisation

Je pense que vous savez ce qu'il vous reste à faire... Donc, si ce n'est pas encore fait, créez-vous un nouveau projet avec les classes habituelles.

Comme le titre du chapitre l'indique, nous allons utiliser l'objet JTextField. Comme vous pouvez vous en douter, cet objet a lui aussi les méthodes de redimensionnement, de couleur de police...

De ce fait, je commence donc avec un exemple complet. Regardez et testez ce 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
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
 
 
public class Fenetre extends JFrame {
        
        private JPanel container = new JPanel();
        private JTextField jtf = new JTextField("Valeur par défaut");
        private JLabel label = new JLabel("Un JTextField");
        
        
        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());
               
        JPanel top = new JPanel();
        
        Font police = new Font("Arial", Font.BOLD, 14);
        jtf.setFont(police);
        jtf.setPreferredSize(new Dimension(150, 30));
        jtf.setForeground(Color.BLUE);
        
        top.add(label);
        top.add(jtf);
                
        container.add(top, BorderLayout.NORTH);
        this.setContentPane(container);
        this.setVisible(true);            
        }
}


Ce qui nous donne :

Image utilisateur


Vous pouvez voir que c'est très simple ! Vous pouvez saisir ce que vous voulez dans cette zone de texte.

Vous pouvez initialiser le contenu avec la méthode setText(String str) ou le récupérer avec la méthode getText().


Il existe toutefois un objet très ressemblant à celui-ci, un peu plus étoffé. En fait, cet objet permet d'avoir un JTextField formaté pour recevoir un certain type de saisie (date, pourcentage...).

Voyons ça tout de suite...

Un objet plus restrictif : le JFormattedTextField

Avec ce genre d'objet, vous allez pouvoir vous éviter beaucoup de contrôles et de cast sur le contenu de vos zones de texte...

Si vous avez essayé de récupérer le contenu du JTextField utilisé ci-dessus (lors du clic sur un bouton, par exemple...) vous avez dû vous rendre compte que le type de contenu lui était égal...

Mais, un jour sûrement, vous aurez besoin d'une zone de texte qui n'accepte qu'un certain type de donnée... Avec l'objet JFormattedTextField, nous nous en rapprochons, mais vous verrez que vous pouvez faire encore mieux !
Cet objet retourne une valeur uniquement si celle-ci correspond à ce que vous lui avez demandé de contenir, je m'explique : si vous voulez que votre zone de texte DOIVE contenir des entiers, ou des dates... c'est possible !
Par contre, ce contrôle se fait lorsque vous quittez le champ en question ! Vous pouvez saisir des lettres dans un objet n'acceptant que des entiers, mais la méthode getText() ne renverra RIEN car le contenu sera effacé si les données ne correspondent pas aux attentes ! !


Voici un code et deux exemples :

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
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
 
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
 
 
public class Fenetre extends JFrame {
        
        private JPanel container = new JPanel();
        private JFormattedTextField jtf = new JFormattedTextField(NumberFormat.getIntegerInstance());
        private JFormattedTextField jtf2 = new JFormattedTextField(NumberFormat.getPercentInstance());
        private JLabel label = new JLabel("Un JTextField");
        private JButton b = new JButton ("OK");
        
        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());
               
        JPanel top = new JPanel();        
        
        Font police = new Font("Arial", Font.BOLD, 14);
        jtf.setFont(police);
        jtf.setPreferredSize(new Dimension(150, 30));
        jtf.setForeground(Color.BLUE);
        
        jtf2.setPreferredSize(new Dimension(150, 30));
        
        b.addActionListener(new BoutonListener());
        
        top.add(label);
        top.add(jtf);
        top.add(jtf2);
        top.add(b);
                
       // container.add(top, BorderLayout.NORTH);
        this.setContentPane(top);
        this.setVisible(true);            
        }       
        
        class BoutonListener implements ActionListener{
 
                public void actionPerformed(ActionEvent e) {
                        System.out.println("TEXT : jtf " + jtf.getText());
                        System.out.println("TEXT : jtf2 " + jtf2.getText());
                }
                
        }
}


Exemple valide :

Image utilisateur


Exemple invalide :

Image utilisateur


Vous pouvez voir que notre objet met aussi en forme la saisie lorsque celle-ci est valide ! Celui-ci sépare les nombres 3 par 3 afin de facilité la lecture...


Voici les types de contenus que vous pouvez utiliser :



Sans rentrer dans les détails, vous pouvez aussi utiliser un objet MaskFormatter qui permet d'avoir un format à taille fixe dans votre zone de texte. Ceci est très pratique lorsque vous voulez un numéro de téléphone, un numéro de sécurité sociale...
Vous devez définir ce format en paramètre dans le constructeur à l'aide de méta-caractères. Ceux-ci permettent de dire à votre objet MaskFormatter comment doit être constitué le futur contenu de votre zone de texte. Voici la liste de ces méta-caractères :


L'instanciation d'un tel objet peut lever une ParseException. Vous devrez donc l'entourer d'un bloc try{...}catch(ParseException e){...}.


Voici à quoi ressemblerait un format téléphonique :
Code : Java
1
2
3
4
5
6
7
try{
   MaskFormatter tel = new MaskFormatter("## ## ## ## ##");
   //Ou encore
   MaskFormatter tel2 = new MaskFormatter("##-##-##-##-##");
   //Vous pouvez ensuite le passer à votre zone de texte
   JFormattedTextField jtf = new JFormattedTextField(tel2);
   }catch(ParseException e){e.printStackTrace();}


Vous pouvez vous rendre compte qu'il n'y a rien de compliqué...
Je vous donne tout de même un exemple de code permettant de saisir un numéro de téléphone français et un numéro de téléphone américain :

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
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;

import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.text.MaskFormatter;
 
 
public class Fenetre extends JFrame {
        
    private JPanel container = new JPanel();
    private JFormattedTextField jtf;
    private JFormattedTextField jtf2;
    private JLabel label = new JLabel("Téléphone FR   ");
    private JLabel label2 = new JLabel("Téléphone USA");
    private JButton b = new JButton ("OK");
    
    /**
     * Constructeur de l'objet 
     */
    public Fenetre(){
        
        this.setTitle("Animation");
        this.setSize(300, 150);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLocationRelativeTo(null);
 
        container.setBackground(Color.white);
        container.setLayout(new BorderLayout());
        
        try{
        	   MaskFormatter tel = new MaskFormatter("##-##-##-##-##");
        	   MaskFormatter telUSA = new MaskFormatter("###-####");
        	   jtf = new JFormattedTextField(tel);
        	   jtf2 = new JFormattedTextField(telUSA);
        }catch(ParseException e){
        	e.printStackTrace();
        }

        
        JPanel top = new JPanel();        
        
        Font police = new Font("Arial", Font.BOLD, 14);
        jtf.setFont(police);
        jtf.setPreferredSize(new Dimension(150, 30));
        jtf.setForeground(Color.BLUE);
        
        jtf2.setPreferredSize(new Dimension(150, 30));
        
        b.addActionListener(new BoutonListener());
        
        top.add(label);
        top.add(jtf);
        top.add(label2);
        top.add(jtf2);
        top.add(b);
                
       // container.add(top, BorderLayout.NORTH);
        this.setContentPane(top);
        this.setVisible(true);            
     }       
        
        class BoutonListener implements ActionListener{
 
                public void actionPerformed(ActionEvent e) {
                        System.out.println("Téléphone FR  : " + jtf.getText());
                        System.out.println("Téléphone USA : " + jtf2.getText());
                }
                
        }
}


Vous pouvez constater qu'avec le méta-caractère utilisé avec notre objet MaskFormatter, nous sommes obligé de saisir des chiffres !


Et voici le résultat lorsque nous cliquons sur le bouton :

Image utilisateur



Je ne sais pas pour le numéro américain, mais le numéro de téléphone français est loin d'être un numéro de téléphone valide ! :o


Ah ! je savais que vous alliez remarquer ce petit détail, de taille je vous l'accorde.
Nous voilà confrontés à un problème qui vous hantera tant que vous programmerez : L'intégrité de vos données !

Comme démontré ci-dessus, vous pouvez aider le plus possible l'utilisateur sur ce qu'il doit renseigner comme données dans des champs, vous ne devrez JAMAIS FAIRE UNE CONFIANCE AVEUGLE EN CELLES-CI !
C'est simple : dans ma boîte, on part du principe de ne jamais faire confiance à l'utilisateur !

Nous sommes obligés de faire une multitude de contrôles en plus, mais les applications ont le mérite d'être un tant soit peu sécurisées...

Qu'est-ce que nous pouvons faire dans le cas de ta saisie ?

En réalité, beaucoup de choses :


En gros, vous devrez vérifier l'intégrité de vos données et, dans le cas qui nous intéresse, l'intégrité de vos chaînes de caractères, pendant ou après la saisie !
D'ailleurs, c'est ce que je vous propose de faire, pas plus tard que maintenant ! ^^

Contrôlez vos données post-saisie

Afin de voir comment contrôler au mieux vos données, nous allons travailler avec un JFormattedTextField acceptant tous types de caractères. 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
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
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;

import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.text.MaskFormatter;
 
 
public class Fenetre extends JFrame {
        
    private JPanel container = new JPanel();
    private JFormattedTextField jtf;
    private JFormattedTextField jtf2;
    private JLabel label = new JLabel("Téléphone FR   ");
    private JButton b = new JButton ("OK");
    
    /**
     * Constructeur de l'objet 
     */
    public Fenetre(){
        
        this.setTitle("Animation");
        this.setSize(300, 150);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLocationRelativeTo(null);
 
        container.setBackground(Color.white);
        container.setLayout(new BorderLayout());
        
        try{
        	   MaskFormatter tel = new MaskFormatter("**-**-**-**-**");
        	   jtf = new JFormattedTextField(tel);
        }catch(ParseException e){
        	e.printStackTrace();
        }

        JPanel top = new JPanel();        
        
        Font police = new Font("Arial", Font.BOLD, 14);
        jtf.setFont(police);
        jtf.setPreferredSize(new Dimension(150, 30));
        jtf.setForeground(Color.BLUE);
                
        b.addActionListener(new BoutonListener());
        
        top.add(label);
        top.add(jtf);
        top.add(b);
       
        this.setContentPane(top);
        this.setVisible(true);            
     }       
        
    class BoutonListener implements ActionListener{ 
        public void actionPerformed(ActionEvent e) {
                System.out.println("Téléphone FR  : " + jtf.getText());
        }  
    }
}


Maintenant, vous pouvez saisir n'importe quoi dans ce qui devait être un numéro de téléphone.
Il reste tout de même une restriction sur le nombre de caractères que doit prendre le champ, ici 10, mais, mis à part ça, vous êtes libres de saisir ce que vous voulez :

Image utilisateur


Première approche



Une méthode de contrôle, un peu compliquée au final, consisterait à exploser la chaîne de caractères grâce à la méthode split(String regex) et tester les éléments un par un...
Cette méthode, split(String regex) , permet de créer un tableau de String à partir d'une chaîne de caractères en l'explosant par rapport à l'expression régulière passée en paramètre. Un exemple est toujours mieux :
Code : Java
1
2
3
4
5
6
String str = "Je-suis-un-ZérO";
String[] tab = str.split("-");//Donne un tableau à 4 entrées
//tab[0] vaut "Je"
//tab[1] vaut "suis"
//tab[2] vaut "un"
//tab[3] vaut "ZérO"


Le paramètre de cette méthode n'est pas une chaîne de caractères banale ! Il s'agit en fait d'une expression régulière. Nous allons y venir. :)


Voici une façon de faire, un peu barbare mais elle fonctionne :

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
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;

import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.text.MaskFormatter;
 
 
public class Fenetre extends JFrame {
        
    private JPanel container = new JPanel();
    private JFormattedTextField jtf;
    private JFormattedTextField jtf2;
    private JLabel label = new JLabel("Téléphone FR   ");
    private JButton b = new JButton ("OK");
    
    /**
     * Constructeur de l'objet 
     */
    public Fenetre(){
        
        this.setTitle("Animation");
        this.setSize(300, 150);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLocationRelativeTo(null);
 
        container.setBackground(Color.white);
        container.setLayout(new BorderLayout());
        
        try{
        	   MaskFormatter tel = new MaskFormatter("**-**-**-**-**");
        	   jtf = new JFormattedTextField(tel);
        }catch(ParseException e){
        	e.printStackTrace();
        }

        JPanel top = new JPanel();        
        
        Font police = new Font("Arial", Font.BOLD, 14);
        jtf.setFont(police);
        jtf.setPreferredSize(new Dimension(150, 30));
        jtf.setForeground(Color.BLUE);
                
        b.addActionListener(new BoutonListener());
        
        top.add(label);
        top.add(jtf);
        top.add(b);
       
        this.setContentPane(top);
        this.setVisible(true);            
     }       
        
    class BoutonListener implements ActionListener{ 
        public void actionPerformed(ActionEvent e) {
                System.out.println("Téléphone FR  : " + jtf.getText());
                String[] tab = jtf.getText().split("-");

                //On contrôle le numéro de téléphone
                //-------------------------------------
                if(!controleData(tab)){
                	System.out.println("Numéro erroné ! ");
                	jtf.setText("");
                }
                else{
                	System.out.println("Numéro de téléphone OK ! ");
                }
        }  
        
        /**
         * Méthode qui va contrôler la saisie
         * @param data
         * @return Boolean
         */
        private boolean controleData(String[] data){
        	int i = 0;
        	
        	//On balaye tout le contenu du tableau et on vérifie 
        	//que les données sont conformes à nos attentes
        	while(i < data.length){
        		
        		switch(i){
        			
        			//Le premier élément doit être numérique et 
        			//égal à 01 ou 02 ou 03 ou 04 ou 05 ou 06 ou 08 ou 09
        			case 0:
        				try {
							int j = Integer.parseInt(data[i]);
							if(j < 1 || j > 6 && j != 8 && j != 9)
								return false;
						} catch (NumberFormatException e) {
							return false;
						}
        			
        				break;
        				
        			//Les autres chiffres doivent être compris entre 00 et 99 INCLUS
        			//Je ne sais pas si ça marche réellement comme ça, mais c'est pour l'exemple...
        			case 1:
        			case 2:
        			case 3:
        			case 4:
        				try {
							int j = Integer.parseInt(data[i]);
							if(j < 0 || j > 99)
								return false;
						} catch (NumberFormatException e) {
							return false;
						}
        				break;
        		}
        		
        		i++;
        	}
        	
        	return true;
        }
    }
}


Ce qui nous donne :

Image utilisateurImage utilisateur



Un peu fastidieux comme façon de contrôler !
Imaginez un peu que vous ayez une multitude de champs à vérifier... Une sacré galère au final !

Allez, dis-nous tout, on te connaît maintenant...

Personnellement, je trouve qu'utiliser des expressions régulières (ou regex) permet plus de souplesse et une économie de code assez conséquente.

On te croit sur parole ! Mais qu'est-ce qu'une regex ?

Bon, je n'ai pas l'intention de réinventer la poudre... Surtout que M@teo a très bien expliqué ça dans deux chapitres de son tuto PHP. Vu que je vais partir du principe que vous connaissez la base des expressions régulières, je vous conseille vivement d'aller faire un tour sur son tuto et, une fois lu, revenez me voir ici-même... ^^

Utiliser des expressions régulières



Comme vous avez pu le constater lors de la lecture du tuto de M@teo, les regex permettent de faire énormément de choses et, dans notre cas, de nous simplifier les contrôles de saisie de notre JFormattedTextField.

Maintenant, afin de pouvoir contrôler la saisie, nous allons devoir définir la regex.
Comme mis dans les commentaires du code précédent, je pars du principe qu'un numéro de téléphone est composé comme suit :


Si vous avez bien suivi le tuto de M@teo sur les regex, vous devez avoir une regex qui ressemble à ça :
#^0[0-689](-[0-9]{2}){2}$#
ou à ça :
#^0[0-689](-[\d]{2}){2}$#

Ces deux regex sont tout à fait correctes pour une application PHP, mais elles ne fonctionneront pas avec une application Java. Ceci pour deux raisons.


Afin de pouvoir utiliser les classes abrégées dans une regex, il faut faire en sorte que le backslash de la classe abrégée soit interprété comme tel et non comme un caractère d'échappement.
Comment ? o_O

Il faut tout simplement échapper le caractère d'échappement...
Ce qui nous donne : ^0[0-689](-[\\d]{2}){2}$
Le premier backslash échappe le second, ce qui a pour conséquence que celui-ci est interprété comme un backslash tout ce qu'il y a de plus normal et ainsi que notre classe abrégée fonctionne ! :magicien:

Maintenant, nous sommes parés pour utiliser des regex...

Utiliser des regex

Avant de nous lancer tête baissée dans l'utilisation des regex en Java, vous devez savoir que vous pouvez procéder de deux façon différentes :


Les regex et l'objet String



Vous allez voir que c'est simplissime.
Nous avons donc convenu de la regex à utiliser afin de contrôler nos saisies de numéros de téléphone.
Pour mémoire : ^0[0-689](-[\\d]{2}){4}$

Il ne nous reste plus qu'à dire au contenu de notre JFormattedTextField qu'il doit correspondre à celle-ci.

Cette opération se fait grâce à la méthode matches(String regex) , qui renvoie true si notre chaîne correspond à la regex ou false, dans le cas contraire.

Voici le code qui met en oeuvre cette démarche :
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
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;

import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.text.MaskFormatter;
 
 
public class Fenetre extends JFrame {
        
    private JPanel container = new JPanel();
    private JFormattedTextField jtf;
    private JFormattedTextField jtf2;
    private JLabel label = new JLabel("Téléphone FR   ");
    private JButton b = new JButton ("OK");
    
    /**
     * Constructeur de l'objet 
     */
    public Fenetre(){
        
        this.setTitle("Animation");
        this.setSize(300, 150);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLocationRelativeTo(null);
 
        container.setBackground(Color.white);
        container.setLayout(new BorderLayout());
        
        try{
        	   MaskFormatter tel = new MaskFormatter("**-**-**-**-**");
        	   jtf = new JFormattedTextField(tel);
        }catch(ParseException e){
        	e.printStackTrace();
        }

        JPanel top = new JPanel();        
        
        Font police = new Font("Arial", Font.BOLD, 14);
        jtf.setFont(police);
        jtf.setPreferredSize(new Dimension(150, 30));
        jtf.setForeground(Color.BLUE);
                
        b.addActionListener(new BoutonListener());
        
        top.add(label);
        top.add(jtf);
        top.add(b);
       
        this.setContentPane(top);
        this.setVisible(true);            
     }       
        
    class BoutonListener implements ActionListener{ 
        public void actionPerformed(ActionEvent e) {
                System.out.println("Téléphone FR  : " + jtf.getText());
                
                if(jtf.getText().matches("^0[0-689](-[\\d]{2}){4}$")){
                	System.out.println("Numéro de téléphone OK ! !");
                }
                else{
                	System.out.println("Numéro de téléphone PAS OK ! !");
                }
        }  
    }
}


Ainsi que deux captures d'écran afin de bien vous montrer le résultat :

Image utilisateurImage utilisateur


Vous pouvez voir que c'est très simple à utiliser... :)
Je profite de cet aparté sur les regex afin d'introduire une autre méthode : replaceAll(String regex, String remplacement) .
Grâce à cette dernière, vous pourrez changer tous les caractères, ou chaînes de caractères correspondant à la regex passée en premier paramètre par la chaîne passée en deuxième paramètre.

Si nous appliquons ceci à notre exemple, en partant du principe que, si la saisie du numéro de téléphone est erronée, on remplace tous les caractères par des zéros, cela nous donne :

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
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;

import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.text.MaskFormatter;
 
 
public class Fenetre extends JFrame {
        
    private JPanel container = new JPanel();
    private JFormattedTextField jtf;
    private JFormattedTextField jtf2;
    private JLabel label = new JLabel("Téléphone FR   ");
    private JButton b = new JButton ("OK");
    
    /**
     * Constructeur de l'objet 
     */
    public Fenetre(){
        
        this.setTitle("Animation");
        this.setSize(300, 150);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLocationRelativeTo(null);
 
        container.setBackground(Color.white);
        container.setLayout(new BorderLayout());
        
        try{
        	   MaskFormatter tel = new MaskFormatter("**-**-**-**-**");
        	   jtf = new JFormattedTextField(tel);
        }catch(ParseException e){
        	e.printStackTrace();
        }

        JPanel top = new JPanel();        
        
        Font police = new Font("Arial", Font.BOLD, 14);
        jtf.setFont(police);
        jtf.setPreferredSize(new Dimension(150, 30));
        jtf.setForeground(Color.BLUE);
                
        b.addActionListener(new BoutonListener());
        
        top.add(label);
        top.add(jtf);
        top.add(b);
       
        this.setContentPane(top);
        this.setVisible(true);            
     }       
        
    class BoutonListener implements ActionListener{ 
        public void actionPerformed(ActionEvent e) {
                System.out.println("Téléphone FR  : " + jtf.getText());
                
                if(jtf.getText().matches("^0[0-689](-[\\d]{2}){4}$")){
                	System.out.println("Numéro de téléphone OK ! !");
                }
                else{
                	System.out.println("Numéro de téléphone PAS OK ! !");
                	//Si la saisie est erronée
                	//On remplace tous les caractères alphabétiques par des 0
                	String str = jtf.getText().replaceAll("\\w", "0");
                	jtf.setText(str);
                	System.out.println("Après remplacement : " + str);
                }
        }  
    }
}


Et le résultat :

Image utilisateur


Je pense que cette méthode pourrait vous être utile : on ne sait jamais... ;)

Maintenant, nous allons voir comment avoir le même résultat avec l'API regex.

Les regex et l'API regex



Avec cette méthode, nous allons utiliser deux nouveaux objets :


Vous pourrez voir que ces objets sont très simples à utiliser.
L'utilisation de l'objet Pattern se fait comme ceci :
Pattern pattern = Pattern.compile("^0[0-689](-[\\d]{2}){4}$");

Cette instruction déclare et initialise notre objet Pattern, celui-ci est maintenant prêt à tester des chaînes de caractères !

Le test d'une chaîne par rapport à une regex via l'objet Pattern se fait grâce à la méthode matcher(String string) : il ne s'agit pas de la regex en paramètre, mais de la chaîne à tester ! ;)

Comme je vous l'ai dit plus haut, la comparaison via l'objet Pattern renvoie un objet Matcher qui, lui, contient le résultat du test (vrai ou faux) que nous pourrons récupérer grâce à la méthode matches() .

Voici un exemple simple :

Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
String[] tab = {"abcdef", "16464","1v"};
//Regex qui vérifie si la chaîne ne contient que des chiffres
Pattern pattern = Pattern.compile("\\d+");
		
for(String str : tab){
	Matcher matcher = pattern.matcher(str);	
	System.out.print("Teste sur '"+str+"' : ");
	//On regarde le résultat
	if(matcher.matches())
		System.out.println("OK ! ! ");
	else
		System.out.println("PAS OK ! ! ");
}


Et voilà le résultat :
Image utilisateur


Rien de plus simple, n'est-ce pas ? :)

On voit bien que le résultat est le même, mais... l'intérêt ?

Je vais y venir, mais avant de vous expliquer pourquoi il est intéressant de passer par l'objet Pattern, vous devez savoir que vous pouvez ne pas utiliser l'objet Matcher. :waw:

Je vois bien que vous êtes un peu dans le flou...
Reprenez ce que je vous ai dit plus haut : l'objet Pattern retourne un objet Matcher.
Par conséquent, vous pouvez gagner un peu de mémoire en ne déclarant pas d'objet Matcher mais en vous servant de celui que vous retourne l'objet Pattern !

Voilà le code précédent mettant en oeuvre cette démarche :
Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
String[] tab = {"abcdef", "16464","1v"};
//Regex qui vérifie si la chaîne ne contient que des chiffres
Pattern pattern = Pattern.compile("\\d+");
		
for(String str : tab){
	System.out.print("Teste sur '"+str+"' : ");
	//On regarde le résultat, et plus besoin d'instancier un objet Matcher
	if(pattern.matcher(str).matches())
		System.out.println("OK ! ! ");
	else
		System.out.println("PAS OK ! ! ");
}


Je ne vous mets pas de capture d'écran car elle est identique à la précédente ! ;)
Tu ne voudrais pas nous expliquer ça ?

Bien sûr...

En fait, repensez à la pile d'exécution lorsque nous avons abordé les threads.
Ici, c'est la même chose. L'instruction pattern.matcher(str).matches() se découpe en deux.
Lors de l'exécution, la JVM va lire cette ligne, elle voit qu'il y a plusieurs appels de méthode : par conséquent, elle va invoquer celle qui doit être exécutée en premier, faire ce qu'elle a à faire, puis passer à la suivante...

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

Image utilisateur


La flèche indique le sens dans lequel la JVM va lire l'instruction et l'exécuter.

Lors de l'étape 2, c'est comme si vous aviez un objet Matcher à la place de l'instruction correspondant à l'étape 1... La méthode matches() peut donc être invoquée !

Ainsi vous gagnez en objets, en lignes de codes et en mémoire... :)

Maintenant, la réponse à la question que vous vous posez :

Pourquoi utiliser l'objet Pattern alors que l'objet String gère les regex ?


En fait, les deux méthodes sont équivalentes...
C'est vrai que dans notre exemple, nous ne contrôlons qu'un champ. Mais ce ne sera peut-être pas toujours le cas...

Imaginez-vous en train de développer un progiciel de gestion avec, sur une de ses IHM, 35 champs de saisie qui doivent contenir des codes spécifiques à une norme... La solution des regex semble la plus optimisée mais vous n'allez pas répéter la regex pour tous les contrôles de tous les champs ! !
Le jour où votre chef va vous demander de mettre à jour ladite expression car un nouveau code vient de faire son apparition, vous allez sûrement oublier un ou plusieurs champs ! ;)

Le fait d'utiliser un objet Pattern, dans ce cas, permet de centraliser la donnée qui va vous servir à contrôler vos champs et, au lieu de faire X modifications, vous n'avez qu'à changer l'objet Pattern.

Mais il y a une autre alternative



Vous pouvez aussi stocker votre regex dans un objet de type String et utiliser ce dernier dans tous vos contrôles, en utilisant la méthode matches(String regex) . Le but final étant de centraliser les données dont vous vous servirez pour faire vos contrôles et que celles-ci soient facilement modifiables sans risque d'oubli.

En bref, ces deux méthodes sont équivalentes.
Je vous ai un peu induits en erreur, mais il était important que vous connaissiez l'API regex.

Vous devez savoir tout de même que lorsque vous utilisez la méthode matches(String regex) de l'objet String , celui-ci fait appel à l'objet Pattern dans cette méthode... :waw:
De même, lorsque vous utilisez la méthode replaceAll(String regex, String remplacement) , celle-ci invoque l'expression Pattern.compile(regex).matcher(str).replaceAll(repl) .

Pour finir sur l'utilisation des regex



Vous pouvez utiliser la méthode qui vous convient, mais gardez en tête qu'il faut que vos contrôles soient facilement modifiables !

Bon, vous venez de voir comment on peut gérer les saisies après les avoir tapées. Maintenant, je vous propose de voir comment intercepter les saisies des utilisateurs avant que votre composant ne soit affecté de quelque valeur que ce soit ! :)

Contrôle du clavier : l'interface KeyListener

Tout est dans le titre de cette sous-partie !
Vous connaissez déjà :


Voici à présent l'interface KeyListener.
Comme dit dans le titre, celle-ci va vous permettre d'intercepter les événements clavier lorsqu'on :


Vous savez ce qu'il vous reste à faire : créer un implémentation de cette interface dans notre projet.
Créez une classe interne implémentant cette interface et utilisez l'astuce d'Eclipse pour générer les méthodes à implémenter.

Vous constatez que celle-ci a trois méthodes :


Comme vous devez vous en douter, l'objet KeyEvent va nous permettre d'obtenir des informations sur les touches qui ont été utilisées... Parmi celles-ci, nous allons utiliser :


Vous pouvez aussi savoir si certaines touches de contrôle ont été utilisées (SHIFT, CTRL...), connaître le composant à l'origine de l'événement... Nous n'en parlerons pas ici mais ce genre d'informations sont faciles à trouver : Google.

Pour des raisons de simplicité, nous n'allons pas utiliser de JFormattedTextField mais un JTextField sans MaskFormatter. Ainsi, nous n'aurons pas à nous préoccuper des "-" de notre champ.

Pour commencer, nous allons voir dans quel ordre se passent les événements clavier.
Voici le code source que nous allons utiliser, il est presque identique aux précédents, rassurez-vous :

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
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.text.ParseException;
import java.util.regex.Pattern;

import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.text.MaskFormatter;
 
 
public class Fenetre extends JFrame {
        
    private JPanel container = new JPanel();
    private JTextField jtf;
    
    private JLabel label = new JLabel("Téléphone FR   ");
    private JButton b = new JButton ("OK");
    //Création de l'objet pattern dont nous allons nous servir pour 
    //tester le contenu de notre champ
    private Pattern regex;
    
    /**
     * Constructeur de l'objet 
     */
    public Fenetre(){
        
    	//On initialise notre pattern
    	this.