Afficher un tableau avec un Table Model Swing en 5 minutes
Date de publication : 13 janvier 2012. Date de mise à jour : 25 janvier 2012.
Par
Thierry Leriche-Dessirier (www.icauda.com)
Ce miniarticle montre (par l'exemple) comment ajouter un tableau dans une fenêtre
Swing, à l'aide d'un table model, en quelques minutes.
1 commentaire
I. Introduction
I-A. À propos
I-B. Avant de commencer
I-C. Mise-à-jour
II. Découverte du projet d'exemple
II-A. Télécharger, installer et importer le projet d'exemple
II-B. Les classes déjà présentes dans le projet d'exemple
III. Action
III-A. Table model
III-B. Tableau
III-C. Un tableau plus beau
IV. Conclusions
V. Remerciements
VI. Annexes
VI-A. Liens
VI-B. Les classes importantes en entier
I. Introduction
Dans ce document, nous allons voir comment créer un "table model" simple puis s'en servir pour
afficher et alimenter un tableau dans une IHM Swing.
I-A. À propos
Découvrir une technologie n'est pas chose facile. En aborder plusieurs d'un coup l'est encore moins.
Partant de ce constat, cet article a été écrit pour aller à l'essentiel. Les points importants
sont présentés dans le corps de l'article et les éléments secondaires sont expliqués en annexes.
I-B. Avant de commencer
 |
Dans ce tutoriel, j'utilise Maven et Eclipse à titre d'illustration. Ils n'ont absolument rien
à voir avec les "table model". Néanmoins il me fallait un support de travail.
|
Pour écrire ce tutoriel, j'ai utilisé les éléments suivants :
- Java JDK 1.6.0_24-b07 ;
- Eclipse Indigo 3.7 JEE 64b ;
- Maven 3.0.3.
I-C. Mise-à-jour
25/01/2012 : Ajout d'un lien vers la série d'articles "en 5 minutes".
II. Découverte du projet d'exemple
II-A. Télécharger, installer et importer le projet d'exemple

Le projet dans Eclipse (on voit les classes de départ)
 |
Pour suivre ce tutoriel, vous pouvez vous contenter de lire les codes proposés ci-dessous (codes complets
en annexes) et de faire confiance aux captures d'écran.
|
II-B. Les classes déjà présentes dans le projet d'exemple
Le projet contient des classes qui ne sont liées ni aux tableaux ni aux "table model". Ces classes vont servir
de support pour la suite.
D'abord, le projet utilise deux Java beans et un "enum" pour décrire le
métier : Eleve.java, NoteEleve.java et Sexe.java. Ce sont des classes relativement simples
avec des attributs et des accesseurs.
| Bean Eleve |
public class Eleve {
private String nom;
private String prenom;
private Integer annee;
private Sexe sexe;
...
|
| Bean NoteEleve |
public class NoteEleve {
private Eleve eleve;
private Double note;
...
|
| Enum Sexe |
public enum Sexe {
HOMME("Garçon"), FEMME("Fille");
private final String label;
Sexe(String label) {
this.label = label;
}
public String getLabel() {
return label;
}
}
|
Le projet dispose d'un service (singleton) qui renvoie la liste des notes des
élèves au dernier examen.
| Service NoteService |
public class NoteService {
private static NoteService instance;
private List<NoteEleve> notes;
private NoteService() { ... }
public static synchronized NoteService getInstance() {
if (instance == null) {
instance = new NoteService();
}
return instance;
}
public synchronized List<NoteEleve> findLastNotes() {
chargerDepuisBaseDeDonnees();
return notes;
}
|
Pour simplifier ce tutoriel, les notes sont "codées en dur" directement dans le
service (on parle de "bouchon") et ne sont donc pas chargées depuis la base de
données.
| Bouchon dans le service |
private void chargerDepuisBaseDeDonnees() {
if (notes != null) {
return;
}
notes = new ArrayList<NoteEleve>();
addEleveEtNote("Durand", "Marie", 3, FEMME, 5);
addEleveEtNote("Alesi", "Julie", 3, FEMME, 8);
...
addEleveEtNote("Michelet", "Jean", 3, HOMME, 0);
addEleveEtNote("Dupond", "Pierre", 3, HOMME, 2);
addEleveEtNote("Timberot", "Martin", 3, HOMME, 4);
addEleveEtNote("Gravatas", "Paul", 3, HOMME, 5);
...
}
private void addEleveEtNote(String nom, String prenom, Integer annee, Sexe sexe, int note) {
addEleveEtNote(nom, prenom, annee, sexe, new Double(note));
}
private void addEleveEtNote(String nom, String prenom, Integer annee, Sexe sexe, Double note) {
final Eleve eleve = new Eleve(nom, prenom, annee, sexe);
final NoteEleve noteEleve = new NoteEleve(eleve, note);
notes.add(noteEleve);
}
|
Une classe très simple, héritant de JFrame, sert d'interface graphique (IHM) pour le programme.
| IHM |
public class NotesJFrame extends JFrame {
public NotesJFrame() {
super();
setTitle("Notes des élèves");
setPreferredSize(new Dimension(500, 400));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
}
}
|
Enfin, une dernière classe, possèdant une méthode "main()" permet de lancer
l'application d'exemple.
| Main |
public class Launcher {
public static void main(String[] args) {
NotesJFrame notesJFrame = new NotesJFrame();
notesJFrame.setVisible(true);
}
}
|
Pour le moment, l'IHM est donc relativement vide. Dans le chapitre
suivant, nous verrons comment ajouter le tableau des notes, ce qui est
l'objectif de ce tutoriel.

Une IHM bien vide
III. Action
III-A. Table model
Durée estimée : 2 minutes.
Pour commencer, écrivons un "table model" avec juste ce qu'il faut pour
que cela compile. Eclipse nous aide dans cette tâche en créant directement
les méthodes à implémenter.
| Table model minimal |
public class NotesModele extends AbstractTableModel {
@Override
public int getColumnCount() {
return 0;
}
@Override
public int getRowCount() {
return 0;
}
@Override
public Object getValueAt(int arg0, int arg1) {
return null;
}
}
|
Suite à la lecture de ce code, on comprend bien que le "table model" est lié au tableau de données
que l'on souhaite afficher. Les deux premières méthodes indiquent le nombre de colonnes et de lignes
tandis que la troisième méthode permet d'avoir le contenu d'une cellule.
On souhaite aussi que les colonnes du tableau aient un entête.
| Entêtes |
public class NotesModele extends AbstractTableModel {
private final String[] entetes = { "Nom", "Prénom", "Année", "Sexe", "Note" };
@Override
public int getColumnCount() {
return entetes.length;
}
@Override
public String getColumnName(int columnIndex) {
return entetes[columnIndex];
}
...
|
En procédant comme ça, on va clairement choisir les données qui nous intéressent
parmi toutes celles qui seront renvoyées par le service.
On va maintenant connecter le "table model" au service.
| Service |
public class NotesModele extends AbstractTableModel {
private NoteService noteService;
public NotesModele() {
noteService = NoteService.getInstance();
}
...
|
Dans la foulée, on va en profiter pour aller chercher les notes du dernier examen
directement depuis le constructeur, ce qui sera largement assez bien dans le cadre de ce tutoriel.
| Notes |
public class NotesModele extends AbstractTableModel {
private NoteService noteService;
private List<NoteEleve> notes;
public NotesModele() {
noteService = NoteService.getInstance();
notes = noteService.findLastNotes();
}
public List<NoteEleve> getNotes() {
return notes;
}
...
|
Maintenant qu'on a une liste, on peut compléter les méthodes restées vides.
| Valeurs des cellules |
public class NotesModele extends AbstractTableModel {
@Override
public int getRowCount() {
return notes.size();
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
switch (columnIndex) {
case 0:
return notes.get(rowIndex).getEleve().getNom();
case 1:
return notes.get(rowIndex).getEleve().getPrenom();
case 2:
return notes.get(rowIndex).getEleve().getAnnee();
case 3:
return notes.get(rowIndex).getEleve().getSexe();
case 4:
return notes.get(rowIndex).getNote();
default:
throw new IllegalArgumentException();
}
}
...
|
Petite subtilité, on va aider le "table model" à distinguer les types de données
associés à chaque cellule. Cela prendra du sens plus tard.
| Types |
public class NotesModele extends AbstractTableModel {
@Override
public Class<?> getColumnClass(int columnIndex) {
switch (columnIndex) {
case 0:
case 1:
return String.class;
case 3:
return Sexe.class;
case 2:
return Integer.class;
case 4:
return Double.class;
default:
return Object.class;
}
}
...
|
À ce stade, on a un "table model" relativement simple mais suffisant.
III-B. Tableau
Durée estimée : 1 minute.
On va maintenant ajouter un tableau à l'IHM en se servant du "table model"
qu'on vient d'écrire. On commence par ajouter une référence au "table model".
| Table model dans l'IHM |
public class NotesJFrame extends JFrame {
private NotesModele modele;
public NotesJFrame() {
...
modele = new NotesModele();
pack();
}
|
Puis on ajoute le tableau.
| Tableau dans l'IHM |
public class NotesJFrame extends JFrame {
private NotesModele modele;
private JTable table;
public NotesJFrame() {
...
modele = new NotesModele();
table = new JTable(modele);
getContentPane().add(new JScrollPane(table), CENTER);
pack();
}
|
Nous avons presque fini et nous pouvons déjà admirer le résultat en lançant l'application.

Le tableau est visible mais laid
III-C. Un tableau plus beau
Durée estimée : 2 minutes.
On va maintenant essayer de rendre l'affichage un peu plus sexy. Pour commencer, on
va rendre triables les colonnes.
| Tri sur les colonnes |
public class NotesJFrame extends JFrame {
private NotesModele modele;
private JTable table;
public NotesJFrame() {
...
table = new JTable(modele);
table.setAutoCreateRowSorter(true);
...
|

Colonnes triables (ici avec un tri sur la colonne des prénoms)
On s'intéresse ensuite à l'apparence des cellules. Pour cela il faut créer des
objets "TableCellRenderer". On commence par le sexe. On ne veut pas afficher le nom de l'enum
mais son label. En outre, on veut écrire en bleu pour les garçons et en rose pour les filles.
| SexeCellRenderer |
public class SexeCellRenderer extends DefaultTableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
Sexe sexe = (Sexe) value;
setText(sexe.getLabel());
if (sexe == FEMME) {
setForeground(PINK);
} else {
setForeground(BLUE);
}
return this;
}
}
|
On peut simplement utiliser ce "renderer" sur le tableau pour toutes
les cellules dont le contenu est de type "Sexe".
| Du sexe en tableau |
public class NotesJFrame extends JFrame {
public NotesJFrame() {
...
modele = new NotesModele();
table = new JTable(modele);
table.setAutoCreateRowSorter(true);
table.setDefaultRenderer(Sexe.class, new SexeCellRenderer());
...
|

Mise en forme selon le sexe
On veut ensuite mettre en évidence toutes les notes en dessous de la moyenne (pour donner un travail
supplémentaire aux élèves concernés).
| NoteCellRenderer |
public class NoteCellRenderer extends DefaultTableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
Double note = (Double) value;
setText(note.toString());
if (note < 10) {
setBackground(Color.RED);
} else {
setBackground(Color.GREEN);
}
return this;
}
}
|
Cette fois, on va indiquer à quelle colonne s'applique ce renderer. En effet, on peut penser qu'il
n'y aura qu'une seule colonne de type "Sexe" mais plusieurs de type "Double".
| Un tableau en couleur |
public class NotesJFrame extends JFrame {
public NotesJFrame() {
...
modele = new NotesModele();
table = new JTable(modele);
table.setAutoCreateRowSorter(true);
table.setDefaultRenderer(Sexe.class, new SexeCellRenderer());
table.getColumnModel().getColumn(4).setCellRenderer(new NoteCellRenderer());
...
|

Les mauvaises notes en rouge
Nous allons en rester là dans le cadre de ce tutoriel mais on pourrait encore s'amuser longtemps avec
les tableaux et les "table model".
IV. Conclusions
Globalement, il est donc assez simple d'utiliser un "table model" pour afficher un tableau en Swing. Bien qu'on ne
le voie pas dans ce petit article, les "table model" deviennent vraiment intéressants lorsqu'on veut modifier
ou mettre à jour les données (pour s'en rendre compte, je conseille vivement la lecture des tutoriels que j'ai
indiqués en annexes). Dans le cadre d'une approche MVC avec Swing, les "table model" deviennent
même incontournables.
Le code final de ce tutoriel est disponible dans le fichier ZIP
notes2.zip.
V. Remerciements
Je tiens à remercier, en tant qu'auteur de tutoriel rapide, toutes les personnes qui m'ont aidé
et soutenu. Je pense tout d'abord à mes collègues qui subissent mes questions au quotidien
mais aussi à mes contacts et amis du web, dans le domaine de l'informatique ou non, qui m'ont fait part de leurs
remarques et critiques. Bien entendu, je n'oublie pas l'équipe de developpez.com qui m'a guidé dans la rédaction de
cet article et m'a aidé à le corriger et le faire évoluer, principalement sur le forum.
Plus particulièrement j'adresse mes remerciements, par ordre alphabétique, à
jacques_jean,
keulkeul et
le yam's
VI. Annexes
VI-A. Liens
VI-B. Les classes importantes en entier
| NotesModele |
package com.thi.notes.ihm;
import java.util.List;
import javax.swing.table.AbstractTableModel;
import com.thi.notes.domain.NoteEleve;
import com.thi.notes.domain.Sexe;
import com.thi.notes.service.NoteService;
public class NotesModele extends AbstractTableModel {
private static final long serialVersionUID = -1944258978183994752L;
private final String[] entetes = { "Nom", "Prénom", "Année", "Sexe", "Note" };
private NoteService noteService;
private List<NoteEleve> notes;
public NotesModele() {
super();
noteService = NoteService.getInstance();
notes = noteService.findLastNotes();
}
@Override
public int getColumnCount() {
return entetes.length;
}
@Override
public String getColumnName(int columnIndex) {
return entetes[columnIndex];
}
@Override
public int getRowCount() {
return notes.size();
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
switch (columnIndex) {
case 0:
return notes.get(rowIndex).getEleve().getNom();
case 1:
return notes.get(rowIndex).getEleve().getPrenom();
case 2:
return notes.get(rowIndex).getEleve().getAnnee();
case 3:
return notes.get(rowIndex).getEleve().getSexe();
case 4:
return notes.get(rowIndex).getNote();
default:
throw new IllegalArgumentException();
}
}
@Override
public Class<?> getColumnClass(int columnIndex) {
switch (columnIndex) {
case 0:
case 1:
return String.class;
case 3:
return Sexe.class;
case 2:
return Integer.class;
case 4:
return Double.class;
default:
return Object.class;
}
}
public List<NoteEleve> getNotes() {
return notes;
}
}
|
| NotesJFrame |
package com.thi.notes.ihm;
import static java.awt.BorderLayout.CENTER;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import com.thi.notes.domain.Sexe;
public class NotesJFrame extends JFrame {
private static final long serialVersionUID = 3928008548751894521L;
private NotesModele modele;
private JTable table;
public NotesJFrame() {
super();
setTitle("Notes des élèves");
setPreferredSize(new Dimension(500, 400));
setDefaultCloseOperation(EXIT_ON_CLOSE);
modele = new NotesModele();
table = new JTable(modele);
table.setAutoCreateRowSorter(true);
table.setDefaultRenderer(Sexe.class, new SexeCellRenderer());
table.getColumnModel().getColumn(4).setCellRenderer(new NoteCellRenderer());
getContentPane().add(new JScrollPane(table), CENTER);
pack();
}
}
|
| SexeCellRenderer |
package com.thi.notes.ihm;
import static java.awt.Color.BLUE;
import static java.awt.Color.PINK;
import java.awt.Component;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import com.thi.notes.domain.Sexe;
public class SexeCellRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = -5127020452000139717L;
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
Sexe sexe = (Sexe) value;
setText(sexe.getLabel());
if (sexe == Sexe.FEMME) {
setForeground(PINK);
} else {
setForeground(BLUE);
}
return this;
}
}
|
| NoteCellRenderer |
package com.thi.notes.ihm;
import static java.awt.Color.GREEN;
import static java.awt.Color.RED;
import java.awt.Component;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
public class NoteCellRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 7842444473467746594L;
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
Double note = (Double) value;
setText(note.toString());
if (note < 10) {
setBackground(RED);
} else {
setBackground(GREEN);
}
return this;
}
}
|


Les sources présentées sur cette page sont libres de droits
et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation
constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ©
2011 Thierry Leriche-Dessirier. Aucune reproduction,
même partielle, ne peut être faite de ce site et de l'ensemble de son contenu :
textes, documents, images, etc. sans l'autorisation expresse de l'auteur.
Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 €
de dommages et intérêts.
Cette page est déposée.