[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
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 !
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 :
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...
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 :
Exemple invalide :
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 :
- NumberFormat
avec :
- getIntegerInstance()
- getPercentInstance()
- getNumberInstance()
- DateFormat
avec :
- getTimeInstance()
- getDateInstance()
- MessageFormat
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 :
- # indique un chiffre ;
- ' indique un caractère d'échappement ;
- U indique une lettre (les minuscules sont changées en majuscules) ;
- L indique une lettre (les majuscules sont changées en minuscules) ;
- A indique un chiffre ou une lettre ;
- ? indique une lettre ;
- * indique que tous les caractères sont acceptés ;
- H indique que tous les caractères hexadécimaux sont acceptés (0->9, a->f ou A->F).
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 : Java1
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 :
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 !
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 :
- tester chaque élément de votre numéro ;
- tester le numéro en entier ;
- dans le cas ou vous n'utilisez pas de MaskFormatter, vérifier en plus que les saisies soient numériques ;
- utiliser une expression régulière ;
- empêcher la saisie d'un type de caractères ;
- ...
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 !
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 :
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 : Java1
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"
|
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 :
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 :
- un 0 en tout premier chiffre ;
- un chiffre qui peut être : 1, 2, 3, 4, 5, 6, 8, 9 ;
- ensuite, 4 blocs composés d'un "-" suivi d'un nombre compris entre 00 et 99 inclus.
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.
- En Java, il n'y a pas besoin de délimiteurs. Vous pouvez donc enlever les deux "#". Ceci concerne les deux regex.
- Ce point, par contre, ne concerne que la deuxième. Le caractère "\", est utilisé comme caractère d'échappement, ceci afin de dé-spécialiser des caractères comme "\n", "\r"... La classe abrégée "\d", correspondant à un chiffre, ne fonctionnera donc pas.
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 ?

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 !
Maintenant, nous sommes parés pour 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 :
- en utilisant un objet String ;
- en utilisant l'API regex qui se trouve dans le package java.util.regex
.
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 :
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 :
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 :
- un objet Pattern qui va contenir notre regex et qui va retourner un objet contenant le résultat de la comparaison ;
- l'objet Matcher qui est le résultat de la comparaison de la regex avec la chaîne à tester.
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 :
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.
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 :
La flèche indique le sens dans lequel la JVM va lire l'instruction et l'exécuter.
- Elle va lire le pattern.matcher(str)
qui, comme je vous l'ai déjà dit, retourne un objet Matcher. Étape 1.
- Ensuite, elle va exécuter la méthode matches()
qui est une méthode de l'objet Matcher. Étape 2.
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...

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 !
Tout est dans le titre de cette sous-partie !
Vous connaissez déjà :
- l'interface MouseListener qui interagit avec votre souris ;
- l'interface ActionListener qui interagit lors d'un clic sur un composant ;
- l'interface ItemListener qui écoute les événements sur une liste déroulante.
Voici à présent l'interface
KeyListener.
Comme dit dans le titre, celle-ci va vous permettre d'intercepter les événements clavier lorsqu'on :
- presse une touche ;
- relâche une touche ;
- tape sur une touche.
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 :
- KeyPressed(KeyEvent event) : appelée lorsqu'on presse une touche ;
- keyReleased(KeyEvent event) : appelée lorsqu'on relâche une touche. C'est à ce moment que le composant se voit affecter la valeur de la touche ;
- keyTyped(KeyEvent event) : appelée entre les deux méthodes citées ci-dessus.
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 :
- getKeyCode() : retourne le code de la touche ;
- getKeyChar() : retourne le caractère correspondant à la touche.
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 |