Afficher des données tabulaires avec Swing


Composant JTable (2)

Comment définir son propre TableModel

Montre comment définir un composant TableModel personnalisé avec un JTable

Dans un précédent article nous avons vu comment utiliser un composant JTable de manière basique pour afficher un tableau. Nous allons voir maintenant comment aller un peu plus loin à l’aide d’un objet TableModel personnalisé ce qui nous permettra par la suite de gérer par nous mêmes les données affichées dans le JTable.


 Le TableModel

Ce que propose l’API

Comme la plupart des composants Swing, les JTable délèguent la gestion des données à afficher à un modèle. Il s’agit d’une classe qui implémente l’interface TableModel. Dans l’article précédent, nous n’avons pas fait référence à cette classe parce que nous nous somme contenté d’utiliser celui que possède par défaut en interne le JTable. Mais si nous désirons contrôler par nous même les données en mémoire, nous devrons créer notre propre classe DataModel personnalisé puis passer sa référence au constructeur du JTable.

Bien sûr notre modèle devra implémenter toutes les méthodes définies dans l’interface TableModel.

Ces méthodes sont les suivantes :

public int getRowCount();
public int getColumnCount();
public String getColumnName(int columnIndex);
public Class<?> getColumnClass(int columnIndex);
public boolean isCellEditable(int rowIndex, int columnIndex);
public Object getValueAt(int rowIndex, int columnIndex);
public void setValueAt(Object aValue, int rowIndex, int columnIndex);
public void addTableModelListener(TableModelListener l);
public void removeTableModelListener(TableModelListener l);

Pour nous éviter d’implémenter l’ensemble des méthodes, Swing nous propose 2 classes qui définissent certaines méthodes.

La classe AbstractTableModel

La classe AbstractTableModel est, comme son nom l’indique, une classe abstraite qui vous évite de définir la totalité des méthodes. Les méthodes abstraites qu’il vous restera à définir sont :

public int getRowCount()
public int getColumnCount()
public Object getValueAt( int rowIndex, int columnIndex )

La classe DefaultTableModel

Il s’agit de la classe par défaut utilisée par les JTable. Elle est concrète et fonctionnelle et il est possible de surcharger ou ajouter certaines méthodes pour adapter cette classe à ses besoins. L’inconvénient, c’est que celle si implémente le mode de gestion des données à gérer sous forme d’une classe Vector. Cette dernière étant thread-safe n’est pas optimale et n’est généralement pas adaptée dans la plupart des cas. De plus, la documentation java préconise de privilégier l’emploi des nouvelles collections comme les ArrayList généralement plus efficaces.

As of the Java 2 platform v1.2, this class was retrofitted to implement the List interface, making it a member of the Java Collections Framework. Unlike the new collection implementations, Vector is synchronized. If a thread-safe implementation is not needed, it is recommended to use ArrayList in place of Vector.

Le choix de la classe AbstractTableModel

Comme on l’a vue, le DefaultTableModel ne sera pas adapté dans la plupart des cas. Si l’on ne veut pas implémenter la totalité des méthodes de l’interface TableModel, l’AbstractTableModel semble un bon compromis puisqu’il nous laisse la liberté du choix de la gestion des données en mémoire tout en nous évitant de définir la totalité des méthodes.

 Exemple d’utilisation d’un AbstractTableModel

Le programme de départ

Dans un premier temps, nous allons modifier le programme de l’article précédent en le décomposant en plusieurs classes sans ajouter de fonctionnalités mais cela va nous permettre de le faire évoluer plus simplement par la suite.

La première version se contentait d’une seule classe Main. Nous allons maintenant décomposer le programme en 3 classe : Main, MainFrame et TablePanel.

Voici la nouvelle version du programme.

La classe Main

  1. public class Main
  2. {
  3.         public static void main( String[] args )
  4.         {
  5.                 MainFrame mainFrame = new MainFrame( "JTable: Exemple 0b" );
  6.                 mainFrame.setVisible( true );
  7.         }
  8. }

Télécharger

Cette classe se contente de lancer le programme, de créer la fenêtre principale et de l’afficher.

La classe MainFrame

  1. public class MainFrame extends JFrame
  2. {
  3.         public MainFrame( String titre )
  4.         {
  5.                 super( titre );
  6.                
  7.                 tablePanel = new TablePanel( DATA, TITRES );
  8.                 add( tablePanel, BorderLayout.CENTER );
  9.                
  10.                 setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
  11.                 setSize( 500, 200 );
  12.                 setLocationRelativeTo( null );
  13.         }
  14.         private TablePanel tablePanel;
  15.        
  16.         //
  17.         private final static Object[][] DATA = {  
  18.                         { "Handel", "George Frideric", 1685, 1759 },
  19.                         { "Dvorak",     "Anton", 1841, 1904 },
  20. ...    
  21.                         { "Beethoven", "Ludwig van", 1770, 1827 }
  22.         };
  23.         private final static String[] TITRES = { "Nom", "Prénom", "Naissance", "Décé" };
  24. }

Télécharger

Cette classe qui hérite d’un JFrame, définie la fenêtre principale et créé un TablePanel qui est chargé d’afficher le tableau de données.

La classe TablePanel

  1. public class TablePanel extends JPanel
  2. {
  3.         public TablePanel( Object[][] data, Object[] colNames )
  4.         {
  5.                 super( new BorderLayout() );
  6.                 table = new JTable( data, colNames );
  7.                 add( new JScrollPane( table ), BorderLayout.CENTER );
  8.         }
  9.  
  10.         private JTable table;
  11. }

Télécharger

Cette classe dérive de JPanel et est chargée de gérer le tableau de données. Pour l’instant, cette classe se contente de recevoir les données à afficher en paramètre de son constructeur et de créer le JTable correspondant.

Remarque : le constructeur commence par appeler le constructeur de JPanel (méthode super) pour définir un BorderLayout qui n’est pas le layout par défaut des JPanel contrairement aux JFrame.

Si nous exécutons le programme, nous devrions avoir le résultat suivant :

Nous avons réalisé un programme équivalent au précédent mais que nous avons mieux décomposé en utilisant plusieurs classe. Cela fait un programme un peu plus long mais qui va être plus facile à faire évoluer par la suite.

Ajout d’un AbstractTableModel

Nous allons modifier le programme pour utiliser un TableModel personnalisé en remplacement de celui par défaut sans ajouter de nouvelles fonctionnalités.

Pour cet exemple, nous pouvons définir cette nouvelle classe dans le même fichier que le TablePanel.

Voici le source avec les méthodes à implémenter obligatoirement.

  1. class ArrayTableModel extends AbstractTableModel
  2. {
  3.         @Override
  4.         public int getRowCount()
  5.         {
  6.                 // TODO Auto-generated method stub
  7.                 return 0;
  8.         }
  9.         @Override
  10.         public int getColumnCount()
  11.         {
  12.                 // TODO Auto-generated method stub
  13.                 return 0;
  14.         }
  15.         @Override
  16.         public Object getValueAt( int rowIndex, int columnIndex )
  17.         {
  18.                 // TODO Auto-generated method stub
  19.                 return null;
  20.         }
  21. }

Télécharger

Ces 3 méthodes seront utilisées par le JTable afin de connaître le nombre de colonnes et de lignes ainsi que les données à afficher dans chaque cellule.

Mais avant, nous allons modifier la classe TablePanel pour créer le TableModel et et le passer en référence au constructeur du JTable.

Voici le code :

  1. public class TablePanel extends JPanel
  2. {
  3.         public TablePanel( Object[][] data, Object[] colNames )
  4.         {
  5.                 super( new BorderLayout() );
  6.                 tableModel = new ArrayTableModel();
  7.                 table = new JTable( tableModel );
  8.                 add( new JScrollPane( table ), BorderLayout.CENTER );
  9.         }
  10.  
  11.         private JTable table;
  12.         private ArrayTableModel tableModel;
  13. }

Télécharger

Si vous exécutez cette version du programme, vous vous apercevrez qu’il ne génère pas d’erreur car nous avons bien définie les méthodes abstraites mais que la fenêtre est vide car ces méthodes ne font rien. Pour le JTable, il s’agit d’afficher un tableau de 0 colonne et 0 ligne !!

Il nous faut donc maintenant compléter le TableModel pour qu’il puisse indiquer au JTable ce qu’il doit afficher.

Comme dans cet exemple nous nous contentons de n’afficher qu’un tableau de données, le code restera simple.

Nous allons ajouter un premier tableau chargé de contenir les données à afficher et un second contenant les titres des colonnes.

  1. private Object[][] datas;
  2. private Objetc[] colNames;

Télécharger

On peut maintenant compléter les méthodes comme suit ;

  1. @Override
  2. public int getRowCount()
  3. {
  4. return datas.length;
  5. }
  6. @Override
  7. public int getColumnCount()
  8. {
  9.         return colNames.length;
  10. }
  11. @Override
  12. public Object getValueAt( int rowIndex, int columnIndex )
  13. {
  14.         return datas[ rowIndex][ columnIndex ];
  15. }

Télécharger

Le JTable pourra maintenant savoir combien de lignes et de colonnes afficher ainsi que le contenu des cellules.

Il reste maintenant à définir un constructeur pour initialiser le TableModel avec les données à gérer.

  1. public ArrayTableModel( Object[][] datas, Object[] colNames )
  2. {
  3.     this.datas = datas;
  4.     this.colNames = colNames;
  5. }

Télécharger

Modifions l’appel du constructeur dans le TablePanel

  1. tableModel = new ArrayTableModel( datas, colNames );

Si vous exécutez maintenant le programme, vous verrez que les données s’affichent correctement mais que les entêtes affichent "A", "B",.. au lieu du titre des colonnes. Il nous faut encore redéfinir une méthode pour cela :

  1. public String getColumnName( int columnIndex )
  2. {
  3.         return ( String )this.colNames[ columnIndex ];
  4. }

Télécharger

Si vous exécutez le programme, l’affichage devrait maintenant être correct. Bien sur, ce programme ne fait rien de plus que dans les versions précédentes mais nous avons maintenant tous les éléments en main pour modifier le comportement et la gestion des données à gérer. Dans cet exemple nous avons choisi de gérer les données en mémoire dans un tableau d’objets à 2 dimensions mais rien nous empêche de modifier le modèle pour utiliser un ArrayList ou de chercher des données se trouvant en base de données.

 Conclusion

Cet article a montré comment intégrer notre propre TableModel pour avoir les bases pour nous permettre d’adapter le JTable à nos propres besoins.


Article n° 119

Crée par: chris

Créé le: 17 août 2018

Modifié le: 17 août 2018

Nombre de visites: 117

Popularité: 6 %

Popularité absolue: 1

Mots clés de cet article


SPIP

2003-2023 LePpf
Plan du site | | Contact | RSS 2.0 | Sur YouTube

Visiteurs connectés : 3

Nombre moyen de visites quotidiennes sur le site: 236