IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Composants Java Discussion :

[Tutorial][swing][jtable] Trier les colonnes d'un JTable [Trucs & Astuces]


Sujet :

Composants Java

  1. #1
    Membre éprouvé

    Profil pro
    Inscrit en
    Mars 2002
    Messages
    652
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mars 2002
    Messages : 652
    Points : 1 151
    Points
    1 151
    Par défaut [Tutorial][swing][jtable] Trier les colonnes d'un JTable
    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 :

    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);
    }
    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
    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 !
    }
    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);
    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.

    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;
      }
    }
    En plus d'implémenter l'interface Comparator, j'ai ajouté 2 propriétés.
    - 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 :
    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;
      }
     
    }
    Celui-ci est complet et supporte même la mise à jour.
    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 :


    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));
        }
      }
    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.
    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.


    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);
      }
    }
    Ici, je surcharge le MouseAdapter plutôt que d'implémenter le MouseListener pour plus de clarté.
    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 :

    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());
    - liste : liste de mes articles (voir plus bas)
    - 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.

    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;
      }
    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
    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.

  2. #2
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Mars 2003
    Messages
    30
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2003
    Messages : 30
    Points : 30
    Points
    30
    Par défaut
    salut,

    Sans vouloir te gacher le plaisir de poster ton implémentation d'un tri dans une JTable. Sun a fait également un tuto qui me parait à première vue plus simple. Je n'ai pas regardé dans le détail ton code ou de sun donc peut être que le tien est plus souple....

    http://java.sun.com/docs/books/tutor...e.html#sorting

  3. #3
    Membre éprouvé

    Profil pro
    Inscrit en
    Mars 2002
    Messages
    652
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mars 2002
    Messages : 652
    Points : 1 151
    Points
    1 151
    Par défaut
    Merci de ta remarque Drummer.

    Tu retrouves les même éléments dans l'exemple de Sun que le mien a peu de choses.

    J'ai écris un Comparator spécialisé pour ma classe Article plutôt que de rentrer dans les détails d'un comparator générique.
    De leur coté, il utilise 2 types de comparator.
    - LEXICAL_COMPARATOR ( supportant les opération de comparaisons standard == > >= < <= != )
    - COMPARABLE_COMPARATOR ( utilisation de la méthode compareTo )
    Je ne trouve pas cette manière de faire proche de la réalité ( celle du terrain, de mon expérience ).
    Faire des tri sur 3 critères est impossible dans l'exemple de sun.

    Pour ma part, rien ne m'empèche de comparer ainsi des données de types différentes ( choux, carotte) ( non, plus sérieusement, des clients et des prospects par exemple ) et ce, sur de critères plus affinés que la simple colonne.

    Je n'ai volontairement pas utiliser d'inner classes.
    - 6 inner class pour la classe TableSorter
    - 1 inner class pour la classe TableSorterDemo

    J'ai par contre une classe de plus, la classe Article. Mais dans les faits, je me vois mal travailler avec un Object[][] comme modèle de données.

    NB : L'exemple de sun reste un bon exemple mais qui resemble trop à un execice de style accadémique et pas assez proche de la réalité.

  4. #4
    duj
    duj est déconnecté
    Membre confirmé

    Profil pro
    Inscrit en
    Juillet 2003
    Messages
    141
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juillet 2003
    Messages : 141
    Points : 474
    Points
    474
    Par défaut
    Salut,

    La différence majeure (si j'ai bien vu), à mon avis c'est quand même que l'implémentation proposée par SUN est générique (en voyant le schéma).
    Le TableModel triant les données est un "wrapper" d'un autre TableModel.

    Il suffit de faire ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    TableSorter sorter = new TableSorter(new MyTableModel());
    pour rendre n'importe quel TableModel triable.

    Et en plus , un même tablemodel peut être vu par plusieurs tableau, tout en étant trié de manière différente.

    Concernant l'array à 2 dimensions, il est utilisé dans une classe de démonstration, mais rien ne nous empêche que faire comme toi, dans le tablemodel "wrappé" (d'ailleurs, tu as tout à fait raison, c'est bien mieux comme ça).


    Concernant le problème de Comparator, tu a parfaitement raison, il vaut mieux utiliser un comparator comme le tien. De ce point de vu, l'exemple de SUN devrait être adapté, en lui passant, par exemple, un Comparator configurable.

    Bref, pour moi, l'idéal c'est un mélange des deux (avec une préférence pour le générique, je suis fénéant)

  5. #5
    Membre éprouvé

    Profil pro
    Inscrit en
    Mars 2002
    Messages
    652
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mars 2002
    Messages : 652
    Points : 1 151
    Points
    1 151
    Par défaut
    En effet, elle est plus générique que l'exemple que j'ai donné mais comme je l'indique à la fin de mon message, en règle générale, je travail avec quelques éléments de mon framework comme un Comparator et un TableModel générique qui couvre une grande majorité des cas.

    Il est aussi vrais que la manière d'assembler les 3 éléments est mieux défini chez sun avec le :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    TableSorter sorter = new TableSorter(new MyTableModel());
    Ils intercallent un Sorter entre la vue et le modèle alors que moi, je place le Sorter derrière le modèle et il est activable de la vue.

    Merci encore pour tes remarques.

  6. #6
    Membre éclairé Avatar de Ceddoc
    Homme Profil pro
    Développeur Java
    Inscrit en
    Janvier 2009
    Messages
    493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Janvier 2009
    Messages : 493
    Points : 698
    Points
    698
    Par défaut
    Salut, j'ai suivit ce tuto plutôt à la lettre et je suis très content du résultat, seul petit bémol je n'ai plus les icônes des en-têtes qui indiquent le tri. Je me suis battu en essayant de le faire à la main mais ça n'a rien donner de concluant. Avez vous des solutions, des pistes?

    Sinon y a t'il moyen de faire plus simple? en gardant les objets en données? avec swingx peut être?

    Merci d'avance de votre aide.

  7. #7
    Expert éminent sénior
    Avatar de sinok
    Profil pro
    Inscrit en
    Août 2004
    Messages
    8 765
    Détails du profil
    Informations personnelles :
    Âge : 44
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2004
    Messages : 8 765
    Points : 12 977
    Points
    12 977
    Par défaut
    Bah en fait depuis java 6 la JTable fournit directement le nécessaire pour faire des tris. Donc plus besoin de faire du bricolage. cf http://download.oracle.com/javase/tu...e.html#sorting

    Et donc si tu as perdu les flèches de tri dans le header, c'est que tu as redéfini le renderer du header, chose que l'on évite de faire en général.

Discussions similaires

  1. [Swing]JTable- Dimensionner les colonnes
    Par loutfi dans le forum Composants
    Réponses: 12
    Dernier message: 01/07/2011, 08h50
  2. [Swing / JTable] Rendre les cellules d'un JTable non éditables
    Par Jérôme_20 dans le forum Composants
    Réponses: 20
    Dernier message: 10/02/2011, 10h32
  3. Trier les colonnes d'une zone de liste
    Par ancylia dans le forum Access
    Réponses: 8
    Dernier message: 24/10/2005, 14h08
  4. [JTable] Changer les colonnes de manière dynamique
    Par gg2laba dans le forum Composants
    Réponses: 3
    Dernier message: 03/10/2005, 23h39
  5. [VBA] Trier les colonnes d'une listview
    Par alncool dans le forum Macros et VBA Excel
    Réponses: 1
    Dernier message: 01/09/2005, 14h12

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo