Ceci n'est pas une question mais ma manière de faire, elle est totalement critiquable mais je la place ici en guise de tutorial (je ne suis pas rédacteur). Si quelqu'un a le courage d'en faire un vrai tutorial swing, ce serait bien
Objectif : Faire un JTable supportant le tri ascendant et descendant des colonnes.
Pour cela, je commence par me faire une petite interface permettant de savoir si l'objet en question supporte le tri :
Dans mon exemple, je doit gérer une liste d'article ayant 3 propriétés :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 package divers_test.jtable; public interface Sortable { public void sort(int column); }
Pour pouvoir trier les données, il faut... un **truc** qui fait des tris. Ne chercher pas à implémenter des algorithmes de tri tordus, le JDK fais ça tout seul avec une méthode magique d'une classe bien mal connue, Collections.sort(List, Comparator);
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package divers_test.jtable; public class Article { private String libelle; private boolean rupture; private double prixHT; public Article(String libelle, double prix, boolean rupture) { this.libelle = libelle; this.prixHT = prix; this.rupture = rupture; } // liste des getter setter non mise ici pour alleger le code ! }
Je vais donc implémenter mon propre comparateur car trier des chaînes, des nombres et des boolean, c'est pas tout a fait la même chose.
En plus d'implémenter l'interface Comparator, j'ai ajouté 2 propriétés.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 package divers_test.jtable; import java.util.Comparator; public class ArticleComparator implements Comparator { private int column = 0; private boolean ascending = true; public void setColumn(int column) { if( column == this.column ) ascending = !ascending; else { this.column = column; ascending = true; } } public int getColumn() { return column; } public ArticleComparator() { } public int compare(Object o1, Object o2) { int res = 0; if( o1 instanceof Article && o2 instanceof Article ) { Article art1 = (Article) o1; Article art2 = (Article) o2; switch( column) { case 0 : // Libellé res = art1.getLibelle().compareTo(art2.getLibelle()); break; case 1 : // montant res = (int)(art1.getPrixHT() - art2.getPrixHT()); break; case 2 : // en stock ? res = art1.isRupture() == art2.isRupture() ? 0 : 1; break; } } System.out.println(""+(ascending ? res : -res)); return ascending ? res : -res; } }
- column : la colonne sur laquelle effectuer le tri
- ascending : tri croissant ou décroissant.
Le setter de column regarde si la colonne à trier est la même que la précédente, si oui, on inverse le tri, si non, on garde la nouvelle colonne en référence et on remet le tri en ordre croissant.
Bon, arrivé là, on a tous les éléments nécessaires. Enfin presque.
Il nous faut des données, une JTable.
Mon JTable dispose d'un modèle ( je travail TOUJOURS avec un Model pour mes JTable et je vous assure que c'est extrêmement pratique ).
Voici le code de mon modèle :
Celui-ci est complet et supporte même la mise à jour.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 package divers_test.jtable; import javax.swing.table.DefaultTableModel; import java.util.*; import javax.swing.event.*; public class ArticleTableModel extends DefaultTableModel implements Sortable{ private Collection _data; private ArticleComparator _comparator = null; private String[] _colName = {"Article","Prix","Rupture"}; public ArticleTableModel(Collection data) { super(); _data = data; } public int getColumnCount() { return _colName.length; } public String getColumnName(int columnIndex) { return _colName[columnIndex]; } public int getRowCount() { return _data == null ? 0 : _data.size(); } public Object getValueAt(int rowIndex, int columnIndex) { Article art = (Article)_data.toArray()[rowIndex]; switch ( columnIndex ) { case 0 : return art.getLibelle(); case 1 : return new Double(art.getPrixHT()); case 2 : return new Boolean(art.isRupture()); } return ""; } public boolean isCellEditable(int rowIndex, int columnIndex) { return true; } public void setValueAt(Object aValue, int rowIndex, int columnIndex) { Article art = (Article)_data.toArray()[rowIndex]; switch ( columnIndex ) { case 0 : art.setLibelle(aValue.toString()); break; case 1 : art.setPrixHT(((Double)aValue).doubleValue()); break; case 2 : art.setRupture(((Boolean)aValue).booleanValue()); } } public Class getColumnClass(int columnIndex) { return _data == null ? null : getValueAt(0,columnIndex).getClass(); } public void sort(int column) { if( _comparator != null ) { _comparator.setColumn(column); Collections.sort((List)_data,_comparator); fireTableChanged(new TableModelEvent(this)); } } public void setComparator(ArticleComparator _comparator) { this._comparator = _comparator; } public ArticleComparator getComparator() { return _comparator; } }
Il implémente mon interface Sortable et maintient une référence sur un ArticleComparator.
La méthode qui nous intéresse le plus ici est la méthode suivante :
Je m'assure d'avoir associer un comparator (c’est toujours plus propre ), j'indique le numéro de la colonne sur laquelle effectuer le tri et... je tri.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 public void sort(int column) { if( _comparator != null ) { _comparator.setColumn(column); Collections.sort((List)_data,_comparator); fireTableChanged(new TableModelEvent(this)); } }
Enfin, je délègue à la méthode Collections.sort();
Au passage, je suis obligé de transtyper ma Collection en List mais peut importe, ArrayList, LinkedList et Vector implémentent les deux interfaces, pas de souci.
Le fireTableChange me sert à indiquer au contrôleur ( le JTable ) que les données on changer et qu'il doit se remettre à jour.
Aller, question : Que manque t'il ?
Pas de réponse ?
Bon, il nous faut un clic de souris pour activer le tri !
J'ai toujours de coté ma petite classe HeaderListener qui justement se charge de récupérer la colonne sur laquelle la souris a jeter son dévolu.
Ici, je surcharge le MouseAdapter plutôt que d'implémenter le MouseListener pour plus de clarté.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package divers_test.jtable; import java.awt.event.*; import javax.swing.table.*; public class HeaderListner extends MouseAdapter { JTableHeader header; public HeaderListner(JTableHeader header) { this.header = header; } public void mouseClicked(MouseEvent e) { int col = header.columnAtPoint(e.getPoint()); TableModel model = header.getTable().getModel(); if( model instanceof Sortable ) ((Sortable)model).sort(col); } }
Je vais transmettre le header de mon JTable en paramètre au constructeur afin de pouvoir récupérer la liste des colonnes.
Et là, encore une méthode magique du JDK; columnAtPoint() qui permet de récupérer l'indice de colonne à un point(x,y) donné.
De la, je récupère le modèle de la table et m'assure que celui-ci implémente l'interface Sortable (la mienne, celle du début).
Si oui, j'invoque la méthode sort (celle du modèle).
Il ne reste qu’à assembler tout ça dans notre interface graphique. Je ne vous donne ici que le code se référant au JTable :
- liste : liste de mes articles (voir plus bas)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 Collection liste = getArticles(); model = new ArticleTableModel(liste); tblArticle.setModel(model); tblArticle.getTableHeader().addMouseListener(new HeaderListner(tblArticle.getTableHeader())); model.setComparator(new ArticleComparator());
- model : de type ArticleTableModel.
- tlbArticle : Ca, c'est mon JTable
- tblArticle.getTableHeader().addMouseListener : Abonnement de mon Listener à moi que j'ai fait pour que les entêtes de colonne détecte le clic de la souris.
Aller, je vous donne la méthode getArticles() aussi en bonus, c'est un Factory qui me retourne une collection d'articles en "dur" pour le test.
Vous, mettez-y du JDBC, EJB, LDAP ou autre source de données pour votre cas.
Voilà, ce qui parait complexe, c'est qu'il y en a partout. Je sais, c'est comme ça, mais c'est aussi ce qui fais la force de Java
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 public static Collection getArticles() { Collection result = new ArrayList(); result.add( new Article("Le Nom de la rose - Edition Collector",24.99,false) ); result.add( new Article("Le Bon, la Brute et le Truand - Edition Double Collector",24.99,false) ); result.add( new Article("Zatôichi - Edition Simple",19.99,false) ); result.add( new Article("Master and Commander - Edition Simple",15.99,false) ); result.add( new Article("Kill Bill - Volume 1 - Edition Double",24.99,false) ); result.add( new Article("Master and Commander",24.99,false) ); result.add( new Article("Bad Boys II- Edition Double Collector ",27.99,false) ); result.add( new Article("Bad Boys II - Edition Simple ",24.99,false) ); result.add( new Article("S.W.A.T.",24.99,false) ); result.add( new Article("2 fast 2 furious ",24.99,false) ); return result; }![]()
Je ne conseillerais jamais assez d'utiliser des interfaces au profit des classes d'implémentation. Ici, ma petite interface Sortable me sert pour d'autres composants dans mes applications.
J'ai aussi dans mon framework un Comparator et un TableModel générique qui prennent en charge beaucoup de choses.
Voilà, je suis ouvert à tout commentaires et remarques (même critiques) et j'espère ne pas me faire taper dessus par les modos vus la taille du message.
Bon code à tous !
ps: je tiens a disposition les sources complets si vous le désirez.
Partager