Vous êtes sur la page 1sur 16

ENI

TUTORIEL : AFFICHER ET MISE A JOUR DES Si l'une des colonnes JTable ou la fenêtre JTable
DONNEES A L'AIDE DE LA CLASSE SWING elle-même est redimensionnée, les autres
JTABLE colonnes peuvent réagir en s'élargissant ou en se
URL: réduisant pour s'adapter aux nouvelles
http://www.zdnet.fr/builder/programmation/java_c_ dimensions. Il est possible de contrôler ce
cplusplus/0,39020934,39115560,00.htm comportement à l'aide de la méthode
setAutoResizeMode():
La classe JTable de Swing automatise la
création de tableaux de données. Ces tableaux table.setAutoResizeMode(int mode);
peuvent être personnalisés à la volée.
Les valeurs possibles du champ entier mode sont:

La classe JTable de Swing offre un mécanisme AUTO_RESIZE_OFF


simple pour afficher de grands jeux de données. AUTO_RESIZE_NEXT_COLUMN
JTable recèle une multitude d'options de rendu et AUTO_RESIZE_SUBSEQUENT_COLUMNS
d'édition des données, la plupart pouvant être AUTO_RESIZE_LAST_COLUMN
personnalisées pour améliorer leur fonctionnalité. AUTO_RESIZE_ALL_COLUMNS
Cet article fournit une introduction pas à pas au
monde des classes JTable. Valeurs par défaut de la table

1. Simple Jtable La couleur attribuée par défaut au quadrillage des


cellules est Color.gray. Pour modifier la couleur de
Le listing A contient le code d'un exemple simple ce quadrillage, utilisez:
montrant le comportement habituel d'un tableau
JTable. L'utilisateur peut modifier la présentation table.setGridColor(Color.black);
de JTable, déplacer les colonnes par glisser-
déposer ou les redimensionner en étirant leur Vous pouvez modifier la hauteur des lignes avec:
ligne de séparation dans l'en-tête. Les colonnes
sont stockées dans un tableau String: table.setRowHeight(int pixelHeight);
String[] columnNames = {"Product","Number of
Boxes","Price"}; La hauteur de chaque cellule sera égale à la
hauteur de la ligne moins la hauteur des marges
Les données sont initialisées et stockées dans un de la ligne.
tableau d'objets en deux dimensions:
Par défaut, les couleurs de premier plan et
Object[][] data = d'arrière-plan sont déterminées par
{ l'implémentation de Swing. Vous pouvez modifier
{"Apples", new Integer(5),"5.00"}, ces couleurs à l'aide de:
{"Oranges", new Integer(3),"6.00"},
{"Pears", new Integer(2),"4.00"}, table.setSelectionBackground(Color.black);
{"Grapes", new Integer(3),"2.00"}, table.setSelectionForeground(Color.white);
};
Vous pouvez également masquer le quadrillage
des cellules, de la manière suivante:
La table JTable est construite à l'aide de données
(data) et de noms de colonnes (columnNames): table.setShowHorizontalLines(false);
JTable table = new JTable(data, columnNames); table.setShowVerticalLines(false);

Afficher la JTable
La figure A montre une table JTable dont le
La hauteur et la largeur de la JTable sont définies quadrillage horizontal est masqué.
comme suit:

table.setPreferredScrollableViewportSize(new
Dimension(300, 80));

1
ENI

Vous pouvez indiquer si la réorganisation des en-


têtes est autorisée en incluant une référence à la
sous-classe JTableHeader de la JTable en cours
et en appelant sa méthode
setReorderingAllowed():
table.getTableHeader().setReorderingAllowed(fals
e);

Largeur des colonnes


De même, vous pouvez vous assurer que les
Le composant JTable possède plusieurs classes colonnes ne peuvent pas être redimensionnées
et interfaces qui représentent les caractéristiques en étirant leur ligne de séparation entre les en-
de la table. TableColumn gère la largeur des têtes de colonne. Pour ce faire, utilisez la
colonnes et leur redimensionnement, notamment méthode setResizingAllowed():
les largeurs maximale et minimale. table.getTableHeader().setResizingAllowed(fal
se);
TableColumnModel gère des collections de
TableColumn, ainsi que la sélection des colonnes. Vous pouvez colorer l’entête par :
Pour définir la largeur d'une colonne donnée, table.getTableHeader().setBackground(Color.li
indiquez une référence au modèle de colonne de ghtGray) ;
la table. Ensuite, prenez la TableColumn voulue et
appelez sa méthode setPreferredWidth():
Modes de sélection
TableColumncolumn =
table.getColumnModel().getColumn(0);
Par défaut, lorsqu'un utilisateur sélectionne une
column.setPreferredWidth(100);
cellule dans une JTable, la ligne toute entière est
sélectionnée. Il existe plusieurs techniques pour
Lorsque l'utilisateur déplace les colonnes par personnaliser la manière dont un utilisateur peut
glisser-déposer, l'index de la colonne ne change effectuer des sélections. Vous pouvez autoriser
pas. La méthode getColumn(0) renverra toujours l'utilisateur à sélectionner une ou plusieurs lignes
la bonne colonne, quel que soit l'endroit où elle à l'aide de l'interface ListSelectionModel:
apparaît à l'écran.
table.setSelectionMode(ListSelectionModel.SI
On peut centrer les données dans les cellules NGLE_SELECTION);
par :

//Centrer les colonnes ListSelectionModel possède les champs suivants:


DefaultTableCellRenderer custom = new
DefaultTableCellRenderer(); • SINGLE_SELECTION permet la sélection
custom.setHorizontalAlignment(JLabel.CEN d'une ligne à la fois;
TER); • SINGLE_INTERVAL_SELECTION permet
for (int i=0 ; i<table.getColumnCount() ; i++) la sélection d'un groupe de lignes
table.getColumnModel().getColumn(i).setCell contiguës;
Renderer(custom); • MULTIPLE_INTERVAL_SELECTION
permet la sélection de lignes contiguës,
mais avec des fonctionnalités étendues.
En-têtes Ce mode de sélection permet à l'utilisateur
de créer plusieurs intervalles de sélection
JTableHeader gère l'affichage des en-têtes (lignes non contiguës) à l'aide de la touche
JTable. Vous pouvez sous-classer JTableHeader [Ctrl].
pour obtenir une présentation personnalisée. Par
exemple, si votre application nécessite un en-tête
s'étendant sur plusieurs colonnes, il vous suffit de La méthode setCellSelectionEnabled() permet aux
créer une sous-classe de JTableHeader et de utilisateurs de sélectionner à la fois des cellules
l'incorporer à votre classe JTable. individuelles et des lignes entières simultanément:

2
ENI

Pour vous assurer que l'utilisateur ne peut saisir


table.setCellSelectionEnabled(true); que des valeurs entières dans la deuxième
colonne (Number of Boxes), remplacez la
Si elle est paramétrée sur true (vrai), la méthode méthode setValueAt() et incluez la logique de
setCellSelectionEnabled() permettra également la validation dans la nouvelle méthode. Tout d'abord,
sélection de colonnes, comme illustré figure B, en vérifiez si la colonne est un entier et, le cas
plus des lignes et des cellules individuelles. échéant, qu'elle ne contient que des valeurs
entières:

if (data[0][col] instanceof Integer && !(value


instanceof Integer))
{... } else { data[row][col] = value;}

Ensuite, vérifiez que la valeur insérée est bien un


Modifier des cellules
entier. Dans le cas contraire, le champ ne doit pas
être mis à jour et un message d'erreur doit
Notre table toute simple permet à l'utilisateur d'en
s'afficher:
modifier n'importe quelle cellule. Le listing B
montre une table qui permet au programmeur de
try {
décider quelles cellules peuvent être modifiées.
data[row][col] = new Integer(value.toString());
La première étape consiste à créer une interface
} catch (NumberFormatException e) {
TableModel personnalisée:
JOptionPane.showMessageDialog(SimpleTabl
class SimpleTableModel extends
e.this,
AbstractTableModel {}
"Veuillez ne saisir que des valeurs entières.");
}
Les données sont encapsulées dans l'interface
TableModel et lorsque la JTable est initialisée,
Couleurs d'arrière-plan
l'interface TableModel personnalisée est
transmise en tant que paramètre au constructeur
Le listing C contient le code de la classe
de JTable au lieu du tableau d'objets en deux
ColorTable.java, qui montre comment ajouter de
dimensions: SimpleTableModel myModel = new
la couleur à une JTable. Vous pouvez ajouter des
SimpleTableModel();
couleurs d'arrière-plan à une JTable en
JTable table = new JTable(myModel);
remplaçant sa méthode prepareRenderer():
Pour rendre notre modèle éditable, il faut
JTable table = new JTable(data,
implémenter la méthode isCellEditable(int row,
columnNames){
int column) qui va indiquer quelles sont les
public Component
cellules éditables. Si vous vouliez autoriser la
prepareRenderer(TableCellRenderer r, int row,
modification des deuxième et troisième colonnes,
int col){}
mais rendre la première colonne constante, vous
};
devriez remplacer la méthode isCellEditable() de
l'interface TableModel :
Ensuite, insérez la logique qui détermine quelles
colonnes doivent être en couleur et quelles
public boolean isCellEditable(int row, int col){
couleurs utiliser:
if (col == 0) {return false;}
if (col == 2 && !isCellSelected(row, col)){
else {return true; }
Color bg = new Color(200, 100, 30);
}
c.setBackground(bg);
c.setForeground(Color.white);
}
Validation des cellules
Notez que lorsque vous modifiez la couleur
Il faut implémenter la méthode d'arrière-plan de la cellule, vous devez également
setValueAt(Object value, int column, int row) changer la couleur du texte affiché dans la cellule
qui est automatiquement appelée lorsque pour le rendre plus lisible, le cas échéant. La
l'utilisateur valide sa modification figure C illustre une JTable où des couleurs ont
été ajoutées dans la première et la troisième
colonnes.

3
ENI

public class MonModele MonModele mm =


extends new
AbstractTableModel{ MonModele(donnees,
Object donnees[][]; titreColonnes);
String titres[]; JTable jTable2 = new
public MonModele( JTable(mm);
Prenez le contrôle Object donnees[][], String
Nos exemples relèvent du fondement de base sur titres[]){
lequel les classes JTable reposent. À l'aide de ces this.donnees = donnees;
outils, vous pouvez maîtriser rapidement et this.titres = titres;
facilement la mise en forme des tables générées }
par vos applications Java. Vous empêcherez ainsi public int
vos utilisateurs de faire des erreurs lors d'une getColumnCount(){
utilisation standard. return donnees[0].length;
}
Création d’un modèle de table
public Object
getValueAt(int parm1, int
parm2){
return donnees[parm1]
[parm2];
}
public int getRowCount() {
return donnees.length;
}
public String
getColumnName(int col){
return titres[col];
}
}

Méthodes de AbstractTableModel

Quelques méthodes de la classe


AbstractTableModel :

Le composant JTable est un «visualisateur» qui int findColumn(String Retourne l'indice du colonne
prend les données à afficher dans un modèle qui co) à partir de son nom.
implémente l’interface TableModel, ou qui dérive
Retourne la classe des
de la classe abstraite AbstractTableModel
objets de la colonne.
(javax.swing.table.AbstractTableModel). La classe
public Class
AbstractTableModel implémente les méthodes de
Class getColumnClass(int c){
TableModel sauf :
getColumnClass(int co) // un exemple : <
return getValueAt(0,
• public int getRowCount() : le nombre de
c).getClass();
lignes.
}
• public int getColumnCount() : le nombre
de colonnes. boolean isCellEditable( Retourne true si la cellule est
• public Object getValueAt(intligne, int int li, int co) éditable, et false sinon.
colonne) : l'objet à l'intersection de ligne et public boolean
colonne. isCellEditable(
int row, int col) {
Pour obtenir la même table que précédemment // toutes les cellules
éditables :
return true;
Définir la classe : Puis écrire :
4
ENI

// seules les premières de • fireTableRowsDeleted(int d, int f) : les


chaque colonne lignes entre d et f ont été supprimées.
return row ==0; • fireTableCellUpdated(int l, int c) : la valeur
// seules les cellules de la de la cellule (l, c) a été modifiée.
colonne 3 • fireTableChangedEvent(TableModelEvent
return col == 3; e) : transmet l'événement e à tous les
} TableModelListener qui se sont
enregistrés comme auditeur de ce modèle.
void setValueAt( Remplace la valeur à la ligne
Object v, int li, int co) li et colonne co par v.
Modification du rendu de cellule
Les cellules sont visualisées par des instances de
Evénements DefaultTableCelleRenderer défini par :
public class DefaultTableCellRenderer extends
La classe JTable écoute les événements en JLabel implements TableCellRenderer{
provenance de son modèle. Le modèle a ...
plusieurs méthodes pour signaler une modification }
des données : On peut changer la couleur du fond, ou la couleur
• fireTableDataChanged() : les données du des caractères, mais pas la police des caractères,
modèles ont été modifiées : soit les qui rest la police de la table.
données elle-même soit le nombre de ((DefaultTableCellRenderer)
lignes. (maTable.getDefaultRenderer(String.class))).setF
• fireTableStructureChanged() : le nombre oreground(Color.BLUE);
de colonnes, le nom ou le type des Pour visualiser une cellule, autrement que de
colonnes a changé. façon standard, il faut créer les classes
• fireTableRowsInserted(int d, int f) : des visualisateur de cellule qui implémentent
lignes ont été insérées entre d et f. l’interface TableCellRenderer.
• fireTableRowsUpdated(int d, int f) : des Exemples :
lignes entre d et f ont été mises à jour.

La couleur, plutôt que Color(r= , g= b= ) OUI/NON plutôt que true/false


// colorer les colonnes ayant la classe Color // colorer en rouge la colonne detype boolean et et
remplacer true par Oui et false par Non
class MonAfficheurCelluleCouleur
extends JLabel
implements TableCellRenderer { class MonAfficheurCelluleBool extends JLabel
public MonAfficheurCelluleCouleur() { implements TableCellRenderer {
this.setOpaque(true); public MonAfficheurCelluleBool() {
} this.setOpaque(true);
public Component }
getTableCellRendererComponent( public Component getTableCellRendererComponent(
JTable table, Object value, JTable table, Object value,
boolean isSelected, boolean isSelected, boolean hasFocus,
boolean hasFocus, int row, int col) {
int row, int col) { setForeground(Color.red);
setBackground((Color) value); setHorizontalAlignment(JLabel.CENTER);
return this; if(((Boolean)value).booleanValue())
} setText("OUI");
} else setText("NON");
return this;
}
}
maTable.setDefaultRenderer(Color.class, maTable.setDefaultRenderer(Boolean.class,
new MonAfficheurCelluleCouleur()); new MonAfficheurCelluleBool());

5
ENI

Classe principale Pour spécifier un nouvel éditeur de cellule, il faut :

import javax.swing.*; 1. Utiliser la classe DefaultCellEditor. La


import javax.swing.table.*; classe DefaultCellEditor permet de créer
import java.awt.*; des éditeurs à partir de JTextField, de
CheckBox, ou de JComboBox.
public class App_CellRender extends JFrame { 2. Créer une classe qui implémente
TableCellEditor, ou qui dérive de
private ModeleClasse modele = new AbstractCellEditor. Cette classe doit définir
ModeleClasse(); au moins deux méthodes :
private JTable tableau; o Object
getCellEditorValue() : retourne la
public App_CellRender() { valeur courante de la cellule.
super(); o Component
getTableCellEditorComponent(JTa
setTitle("JTable avec modèle "); ble table, Object value, boolean
setDefaultCloseOperation isSelected, int row, int
(JFrame.EXIT_ON_CLOSE); column) : configure et retourne le
tableau = new JTable(modele); composant éditeur.
// tableau = new JTable();
tableau.setModel(modele); Exemple avec DefaultCellEditor :
// tableau.setDefaultRenderer(Color.class, new
MonAfficheurCelluleCouleur()); enum Homologation { Object [] x = {
// tableau.setDefaultRenderer(Boolean.class, standard("standard"), Homologation.standar
new MonAfficheurCelluleBool()); performance("performan d,
ce"), Homologation.perfor
getContentPane().add(new competition("compétition mance,
JScrollPane(tableau), BorderLayout.CENTER); "); Homologation.compet
pack(); String chaine; ition};
} Homologation(String s){ JComboBox jComboBox =
this.chaine = s; new JComboBox(x);
public static void main(String[] args) { }; maTable.setDefaultEditor(
new App_CellRender_1().setVisible(true); public String toString(){ Homologation.class,
return chaine; new
} DefaultCellEditor(jComboB
}
} ox));

Modification de l'édition de cellule Exemples avec dérivation de AbstractCellEditor et


implémentation de TableCellEditor :

Editeur de couleur Editeur de Booléen


class EditeurCouleur extends AbstractCellEditor class EditeurBooleen
implements TableCellEditor { extends AbstractCellEditor
Color couleurCourante; implements TableCellEditor {
JButton bouton; Boolean valeurCourante;
JColorChooser choixCouleur; JButton bouton
public EditeurCouleur() { public EditeurBooleen() {
bouton = new JButton(); bouton = new JButton();
bouton.addActionListener( bouton.addActionListener(new ActionListener(){
new ActionListener(){ public void actionPerformed(ActionEvent e){
public void actionPerformed(ActionEvent e){ int ret = JOptionPane.showConfirmDialog(null,
bouton.setBackground(couleurCourante); "OUI pour TRUE\nNON pour FALSE.",
Color c = JColorChooser.showDialog( "édition de valeur booléenne",

6
ENI

null, "choix d'une couleur", JOptionPane.YES_NO_OPTION,


couleurCourante); JOptionPane.QUESTION_MESSAGE);
if(c!=null) { if (ret==JOptionPane.OK_OPTION)
couleurCourante = c; valeurCourante = Boolean.TRUE;
fireEditingStopped(); else valeurCourante = Boolean.FALSE;
} fireEditingStopped();
} }
}); });
bouton.setBorderPainted(false); bouton.setBorderPainted(false);
} }
public Object getCellEditorValue() { public Object getCellEditorValue() {
return couleurCourante; return valeurCourante;
} }
public Component getTableCellEditorComponent( public Component getTableCellEditorComponent(
JTable table, Object value, JTable table, Object value,
boolean isSelected, boolean isSelected,
int row, int column) { int row, int column){
couleurCourante = (Color) value; valeurCourante = (Boolean) value;
return bouton; return button;
} }
}
}
maTable.setDefaultEditor(Color.class, maTable.setDefaultEditor(Boolean.class,
new EditeurCouleur()); new EditeurBooleen());

Trier les lignes lignes = new Ligne[model.getRowCount()];


for( int i = 0; i<lignes.length; ++i)
lignes[i] = new Ligne(i);// voir la classe Ligne
Pour ne pas changer l'ordre des valeurs du
plus bas
modèle, on introduit un filtre entre le modèle et le
}
JTable.
public int getRowCount() {
return model.getRowCount();
TableModel modele = new MonTableModele(); }
filtre = new FiltreTriModel(modele); public int getColumnCount() {
maTable = new JTable(filtre); return model.getColumnCount();
maTable.getTableHeader().addMouseListener(ne }
w MouseAdapter(){ public Object getValueAt(int rowIndex, int
public void mouseClicked(MouseEvent e){ columnIndex) {
int tableC = return
jTable2.columnAtPoint(e.getPoint()); model.getValueAt(lignes[rowIndex].index,
int modelCol = columnIndex);
jTable2.convertColumnIndexToModel(tableC); }
filtre.sort(modelCol); public Class<?> getColumnClass( int i){
} return model.getColumnClass(i);
}); }
public String getColumnName(int i){
et le Filtre est défini comme suit : return model.getColumnName(i);
}
class FiltreTriModel extends // a implémenter si la table est éditable
AbstractTableModel{ public boolean isCellEditable(int row, int col) {
TableModel model; return true;
Ligne [] lignes; }
int colonneTri; // a implémenter si les données peuvent
FiltreTriModel ( TableModel m){ changer
model = m;
7
ENI

public void setValueAt(Object value, int row, lbl.setOpaque(false);


int col) { lbl.setFont(font);
model.setValueAt(value, lignes[row].index, col); lbl.setForeground(Color.BLUE); // caractère
fireTableCellUpdated(lignes[row].index, col); en blue
} lbl.setText((String) value);
public void sort(int c){ return lbl;
colonneTri = c; }
try{ };
Arrays.sort(lignes); }
fireTableDataChanged();
}catch (RuntimeException e){} // les données
ne sont pas comparables ! 3. Affichage et mise à jour des tableaux
} d’objets dans un JTABLE
//------- la classe Ligne -------
private class Ligne implements Comparable{ En général, Java étant un langage orienté objet,
int index; on manipule des objets. On va donc créer un objet
public Ligne (int i){index = i;} Ami qui va représenter un de nos amis :
public int compareTo(Object o) { Voici les caractéristiques d'un ami :
Ligne autreLigne = (Ligne)o; • Un nom et un prénom (classe String).
Object cellule = model.getValueAt(index, • Une couleur préférée (classe Color).
colonneTri); • Un sexe (booléen homme/femme).
Object autreCellule = • Un sport qu'on pratique avec lui
model.getValueAt(autreLigne.index, colonneTri); (énumération Sport).
return((Comparable)cellule).compareTo(autreCell Voici notre énumération Sport :
ule);
} public enum Sport {
} TENNIS,
} FOOTBALL,
NATATION,
Changer les en-têtes de colonne. RIEN;
}
TableCellRenderer tbch =
getTableHeaderRenderer();
for (int i = 0; i < maJTable.getColumnCount(); i+ public class Ami {
+) { private String nom;
TableColumn tc = private String prenom;
maJTable.getColumnModel().getColumn(i); private Color couleur;
tc.setHeaderRenderer(tbch); private boolean homme;
} private Sport sport;

Avec une méthode getTableHeaderRenderer public Ami(String nom, String prenom, Color
définie par : couleur, boolean homme, Sport sport) {
super();
public static TableCellRenderer
getTableHeaderRenderer() { this.nom = nom;
return new TableCellRenderer() { this.prenom = prenom;
public Component this.couleur = couleur;
getTableCellRendererComponent(JTable table, this.homme = homme;
Object value, boolean isSelected, boolean this.sport = sport;
hasFocus, }
int row, int column) {
JLabel lbl = new JLabel(); public String getNom() {
lbl.setBorder(new EtchedBorder()); return nom;
lbl.setHorizontalAlignment( JLabel.CENTER ); }
Font font = new java.awt.Font("Comic Sans
MS", java.awt.Font.PLAIN, 24); public void setNom(String nom) {
this.nom = nom;
8
ENI

} }

public String getPrenom() { public int getRowCount() {


return prenom; return amis.length;
} }

public void setPrenom(String prenom) { public int getColumnCount() {


this.prenom = prenom; return entetes.length;
} }
public Color getCouleur() {
return couleur; } public String getColumnName(int columnIndex) {
public void setCouleur(Color couleur) { return entetes[columnIndex];
this.couleur = couleur; }
}
public boolean isHomme() { public Object getValueAt(int rowIndex, int
return homme; columnIndex) {
} switch(columnIndex){
public void setHomme(boolean homme) { case 0:
this.homme = homme; return amis[rowIndex].getPrenom();
} case 1:
public Sport getSport() { return amis[rowIndex].getNom();
return sport; case 2:
} return amis[rowIndex].getCouleur();
public void setSport(Sport sport) { case 3:
this.sport = sport; return amis[rowIndex].isHomme();
} case 4:
} return amis[rowIndex].getSport();
default:
Et on va prendre en compte une simple classe return null; //Ne devrait jamais arriver
des données dans notre modèle : }
}
public class ModeleStatiqueObjet extends }
AbstractTableModel {
private final Ami[] amis; 4.1. Affichage

private final String[] entetes = {"Prénom", "Nom", Pour ce qui est de l'affichage, il suffit d'utiliser le
"Couleur favorite", "Homme", "Sport"}; nouveau modèle au lieu de l'ancien :
public class JTableBasiqueAvecModeleStatiqueObjet
public ModeleStatiqueObjet() { extends JFrame {
super(); public JTableBasiqueAvecModeleStatiqueObjet() {
super();
amis = new Ami[]{ setTitle("JTable avec modèle statique et des
new Ami("Johnathan", "Sykes", Color.red, objets");
true, Sport.TENNIS), setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
new Ami("Nicolas", "Van de Kampf", ;
Color.black, true, Sport.FOOTBALL),
new Ami("Damien", "Cuthbert", Color.cyan, JTable tableau = new JTable(new
true, Sport.RIEN), ModeleStatiqueObjet());
new Ami("Corinne", "Valance", Color.blue,
false, Sport.NATATION), getContentPane().add(new JScrollPane(tableau),
new Ami("Emilie", "Schrödinger", BorderLayout.CENTER);
Color.magenta, false, Sport.FOOTBALL),
new Ami("Delphine", "Duke", Color.yellow, pack();
false, Sport.TENNIS), }
new Ami("Eric", "Trump", Color.pink, true,
Sport.FOOTBALL) public static void main(String[] args) {
}; new JTableBasiqueAvecModeleStatiqueObjet().
9
ENI

setVisible(true); }
}
} public Object getValueAt(int rowIndex, int
Rien ne change au niveau du rendu. Dans le columnIndex) {
chapitre suivant, on va rendre dynamique notre switch(columnIndex){
modèle en permettant l'ajout et le retrait d'ami. case 0:
return amis.get(rowIndex).getPrenom();
case 1:
4.2. Ajouter/Supprimer des lignes return amis.get(rowIndex).getNom();
case 2:
La première chose à faire est donc de rendre return amis.get(rowIndex).getCouleur();
notre modèle dynamique. Pour cela, on va donc case 3:
ajouter des méthodes addAmi et removeAmi. return amis.get(rowIndex).isHomme();
Pour avertir le JTable qu'il y a eu des case 4:
modifications sur le modèle, il faut appeler les return amis.get(rowIndex).getSport();
méthodes fireXXX qui sont définies dans default:
AbstractTableModel. On va donc utiliser cette fois return null; //Ne devrait jamais arriver
une ArrayList, ce que pourrait donner notre }
modèle dynamique : }
public class ModeleDynamiqueObjet extends
AbstractTableModel { public void addAmi(Ami ami) {
private final List<Ami> amis = new amis.add(ami);
ArrayList<Ami>(); fireTableRowsInserted(amis.size() -1,
amis.size() -1);
private final String[] entetes = {"Prénom", }
"Nom", "Couleur favorite", "Homme", "Sport"};
public void removeAmi(int rowIndex) {
public ModeleDynamiqueObjet() { amis.remove(rowIndex);
super(); fireTableRowsDeleted(rowIndex, rowIndex);
}
amis.add(new Ami("Johnathan", "Sykes", }
Color.red, true, Sport.TENNIS));
amis.add(new Ami("Nicolas", "Van de Pour la méthode add(), on ajoute le nouvel Ami
Kampf", Color.black, true, Sport.FOOTBALL)); dans la liste ensuite de quoi on prévient la JTable
amis.add(new Ami("Damien", "Cuthbert", qu'un nouvel élément a été inséré. Pour la
Color.cyan, true, Sport.RIEN)); méthode remove() le principe est le même, on
amis.add(new Ami("Corinne", "Valance", commence par supprimer l'élément de la liste et
Color.blue, false, Sport.NATATION)); enfin on prévient le tableau qu'un élément a été
amis.add(new Ami("Emilie", "Schrödinger", supprimé. On va ajouter deux actions dans notre
Color.magenta, false, Sport.FOOTBALL)); interface graphique. La première va ajouter un
amis.add(new Ami("Delphine", "Duke", ami (pour simplifier, cela va toujours rajouter le
Color.yellow, false, Sport.TENNIS)); même objet, alors qu'en réalité, il faudrait
amis.add(new Ami("Eric", "Trump", proposer à l'utilisateur de configurer le nouvel
Color.pink, true, Sport.FOOTBALL)); ami) et la seconde va supprimer le ou les
} éléments sélectionnés. Voici ce que ça va nous
donner :
public int getRowCount() {
return amis.size(); public class
} JTableBasiqueAvecModeleDynamiqueObjet extends
JFrame {
public int getColumnCount() { private ModeleDynamiqueObjet modele = new
return entetes.length; ModeleDynamiqueObjet();
} private JTable tableau;

public String getColumnName(int columnIndex) public JTableBasiqueAvecModeleDynamiqueObjet()


{ { super();
return entetes[columnIndex]; setTitle("JTable avec modèle dynamique");
10
ENI

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Il faut donc comprendre que le tableau de lignes


tableau = new JTable(modele); renvoyé par la méthode getSelectedRows() peut
getContentPane().add(new JScrollPane(tableau), retourner plusieurs intervalles. Les résultats sont
BorderLayout.CENTER); retournés dans l'ordre ascendant. Il nous faut
JPanel boutons = new JPanel(); donc les supprimer depuis la fin, sinon on
boutons.add(new JButton(new AddAction())); fausserait les résultats.
boutons.add(new JButton(new RemoveAction()));
getContentPane().add(boutons, Cela va nous donner l'affichage suivant :
BorderLayout.SOUTH);
pack();
}

public static void main(String[] args) {


new
JTableBasiqueAvecModeleDynamiqueObjet().
setVisible(true);
}

private class AddAction extends AbstractAction {


private AddAction() {
super("Ajouter");
}

public void actionPerformed(ActionEvent e) {


modele.addAmi(new Ami("Megan", "Sami",
Color.green, false, Sport.NATATION));
}
}
private class RemoveAction extends AbstractAction {
private RemoveAction() {
super("Supprimmer");
}
JTable avec un modèle dynamique
public void actionPerformed(ActionEvent e) {
int[] selection = tableau.getSelectedRows();
L'affichage des cellules
for(int i = selection.length - 1; i >= 0; i--){
modele.removeAmi(selection[i]); On va maintenant passer à la personnalisation de
} l'affichage des différentes cellules. Voici ce qu'on
} va effectuer comme changements :
}
}
• Afficher la couleur au lieu du toString() de
Il faut savoir qu'une JTable peut fonctionner selon Color.
• Afficher l'image du sexe au lieu d'un
plusieurs modes de sélection qui sont
configurables via la méthode setSelectionMode. booléen.
• Afficher le nom d'un ami en gras.
Le paramètre mode est une des valeurs suivantes
venant de ListSelectionModel :
Pour cela, il va falloir commencer par spécifier
• SINGLE_SELECTION : permet de dans le modèle à quelle classe correspond
sélectionner une seule ligne. chacune colonne. On ne peut configurer des
• SINGLE_INTERVAL_SELECTION : renderers que par colonne. A la suite de quoi, on
permet de sélectionner un intervalle de configurera les renderers pour chaque classe de
ligne. colonne au niveau de la JTable. Voici donc la
• MULTIPLE_INTERVAL_SELECTION : première chose à faire. Il suffit de redéfinir la
permet de sélectionner de multiples méthode getColumnClass() dans notre modèle :
intervalles. C'est la valeur par défaut.
11
ENI

On peut donc passer au suivant. Cette fois, on va


afficher une image pour le sexe de la personne (il
@Override faut aussi penser à modifier l'en-tête de la colonne
public Class getColumnClass(int columnIndex){ pour mettre Sexe au lieu d'homme).
switch(columnIndex){
case 2:return Color.class;
case 3:return Boolean.class; public class SexeCellRenderer extends
default: return Object.class; DefaultTableCellRenderer {
} private Icon manImage;
} private Icon womanImage;

À noter que ceci n'est pas vraiment indispensable public SexeCellRenderer() {


car cela est automatiquement fait par super();
AbstractTableModel.
manImage = new ImageIcon("man.png");
On va maintenant créer nos renderers. Un womanImage = new ImageIcon("woman.png");
renderer est simplement une classe implémentant }
TableCellRenderer qui est une interface ne
contenant qu'une seule méthode retournant un @Override
composant Swing. En pratique, on hérite public Component
généralement de DefaultCellRenderer qui getTableCellRendererComponent(JTable table,
représente un JLabel comme renderer. Object value, boolean isSelected, boolean
hasFocus, int row, int column) {
Il faut éviter dans la mesure du possible de super.getTableCellRendererComponent(tabl
renvoyer un nouvel objet dans un renderer si on a e, value, isSelected, hasFocus, row, column);
beaucoup d'éléments dans notre JTable. Cela
voudrait dire qu'il faudrait créer un objet pour Boolean homme = (Boolean)value;
chaque ligne et à chaque fois qu'on redessine la setText("");
JTable, ce qui peut dégrader très fortement les if(homme){
performances. C'est pourquoi on garde un même setIcon(manImage);
objet qu'on modifie pour chaque cellule. } else {
setIcon(womanImage);
On va donc créer notre premier renderer qui va }
simplement modifier le background du JLabel return this;
avec la couleur favorite de l'élément courant : }
}
public class ColorCellRenderer extends
DefaultTableCellRenderer { On commence donc par charger les images dans
@Override le constructeur ensuite de quoi dans la méthode
public Component de rendu, en fonction du sexe de la personne, on
getTableCellRendererComponent(JTable table, affiche la bonne image. On peut donc passer au
Object value, boolean isSelected, boolean dernier renderer :
hasFocus, int row, int column) {
super.getTableCellRendererComponent(tabl public class BoldCellRenderer extends
e, value, isSelected, hasFocus, row, column); DefaultTableCellRenderer {
@Override
Color color = (Color) value; public Component
setText(""); getTableCellRendererComponent(JTable table,
setBackground(color); Object value, boolean isSelected, boolean
return this; hasFocus, int row, int column) {
} super.getTableCellRendererComponent(tabl
} e, value, isSelected, hasFocus, row, column);
setFont(getFont().deriveFont(Font.BOLD));
C'est donc extrêmement simple d'implémenter ce return this;
renderer. Il suffit de récupérer la couleur de l'ami }
et de la mettre en background de notre JLabel. }

12
ENI

On va maintenant configurer le tout dans notre new DefaultTableCellRenderer();


tableau :
public Component getTableCellRendererCompo
tableau.setDefaultRenderer(Boolean.class, nent(JTable table, Object value,
new SexeCellRenderer()); boolean isSelected, boolean hasFocus, int ro
tableau.setDefaultRenderer(Color.class, w, int column) {
new ColorCellRenderer()); Component renderer =
tableau.getColumnModel().getColumn(1). DEFAULT_RENDERER.getTableCellRendere
setCellRenderer(new BoldCellRenderer()); rComponent(table, value,
isSelected, hasFocus, row, column);
Pour les deux premiers, on peut directement les Color foreground, background;
lier à une classe de colonne, mais pour le if (isSelected) {
renderer qui met en gras le texte, on ne peut pas foreground = Color.YELLOW;
le lier à String puisqu'on veut seulement le mettre background = Color.GREEN;
sur une colonne et non les deux. Ceci va nous } else {
donner l'affichage suivant : if (row % 2 == 0) {
foreground = Color.BLUE;
background = Color.WHITE;
} else {
foreground = Color.WHITE;
background = Color.BLUE;
}
}
renderer.setForeground(foreground);
renderer.setBackground(background);
return renderer;
}
}
public class ResizeTable {
public static void main(String args[]) {
JTable avec des renderers personnalisés
final Object rowData[][] = {
Vous pouvez faire des renderers beaucoup plus { "1", "one", "I" },
évolués avec d'autres composants qu'un JLabel { "2", "two", "II" },
comme des JPanel ou même pourquoi pas une { "3", "three", "III" }};
JTable. final String columnNames[] = { "#", "English", "
Roman" };

final JTable table = new JTable(rowData, colu


Alterner les couleurs paires et impaires mnNames);
JScrollPane scrollPane = new JScrollPane(table);
import java.awt.BorderLayout;
import java.awt.Color; table.setDefaultRenderer(Object.class,new Eve
import java.awt.Component; nOddRenderer());

import javax.swing.JFrame; JFrame frame = new JFrame("Resizing Table");


import javax.swing.JScrollPane; frame.setDefaultCloseOperation(JFrame.EXIT_
import javax.swing.JTable; ON_CLOSE);
import javax.swing.table.DefaultTableCellRender
er; frame.add(scrollPane, BorderLayout.CENTER);
import javax.swing.table.TableCellRenderer;
frame.setSize(300, 150);
class EvenOddRenderer implements TableCellR frame.setVisible(true);
enderer {
}
public static final DefaultTableCellRenderer DE }
FAULT_RENDERER =
13
ENI

cancelCellEditing();
Autre exemple }
});
Il s'agit d'une classe permettant d'éditer une
cellule de JTable sous la forme d'un PopupMenu. private JPanel panelButtons;
Le PopupMenu contient des checkBoxMenuItem protected JXCheckBoxMenuItem[]
permettant de sélectionner plusieurs éléments en checkBoxMenuItems;
même temps, et présélectionnés selon la valeur
de la cellule. private static final MouseListener mouseListener =
new MouseAdapter() {
PopupEditor.java };

package popup; public PopupEditor(TableCellRenderer renderer,


import java.awt.Color; Object[] items) {
import java.awt.Component; this.renderer = renderer;
import java.awt.Dimension;
import java.awt.FlowLayout; panelButtons = new JPanel(new
import java.awt.Rectangle; FlowLayout(FlowLayout.RIGHT, 0, 0));
import java.awt.event.ActionEvent; panelButtons.add(validate);
import java.awt.event.ActionListener; panelButtons.add(cancel);
import java.awt.event.MouseAdapter;
import java.awt.event.MouseListener; setChoosableItems(items);
import java.beans.PropertyChangeEvent; }
import java.beans.PropertyChangeListener;
import java.util.ArrayList; protected JButton makeButton(String text, Color
import java.util.List; color,
ctionListener listener) {
import javax.swing.AbstractCellEditor; JButton button = new JButton(text);
import javax.swing.BorderFactory; button.setForeground(color);
import javax.swing.JButton; button.setBorder(BorderFactory.createRaisedBev
import javax.swing.JCheckBoxMenuItem; elBorder());
import javax.swing.JPanel; button.setFocusPainted(false);
import javax.swing.JPopupMenu; button.addActionListener(listener);
import javax.swing.JTable; return button;
import javax.swing.table.TableCellEditor; }
import javax.swing.table.TableCellRenderer;
public boolean stopCellEditing() {
public class PopupEditor extends popupMenu.close();
AbstractCellEditor implements TableCellEditor, return super.stopCellEditing();
PropertyChangeListener { }

private TableCellRenderer renderer; public void cancelCellEditing() {


popupMenu.close();
private JXPopupMenu popupMenu = new super.cancelCellEditing();
JXPopupMenu(); }

private JButton validate = makeButton("\u21B5", public void setChoosableItems(Object[] items) {


Color.GREEN.darker(), popupMenu.removeAll();
new ActionListener() { this.checkBoxMenuItems = new
public void actionPerformed(ActionEvent e) { JXCheckBoxMenuItem[items.length];
stopCellEditing(); for (int i = 0; i < items.length; i++) {
} JXCheckBoxMenuItem menuItem = new
}); JXCheckBoxMenuItem(items[i]);
checkBoxMenuItems[i] = menuItem;
private JButton cancel = makeButton("X", popupMenu.add(menuItem);
Color.RED, new ActionListener() { }
public void actionPerformed(ActionEvent e) {

14
ENI

popupMenu.addSeparator();
popupMenu.add(panelButtons); Rectangle rect = table.getCellRect(row, column,
popupMenu.pack(); false);
popupMenu.setPackSize(popupMenu.getPreferre Dimension size = popupMenu.getPackSize();
dSize()); popupMenu.setPreferredSize(new
} Dimension(Math.max(rect.width,
size.width), size.height));
public Object getCellEditorValue() { popupMenu.show(table, rect.x, rect.y +
List list = new ArrayList(); rect.height);
for (int i = 0; i < checkBoxMenuItems.length; i++) {
JXCheckBoxMenuItem menuItem = Component comp =
checkBoxMenuItems[i]; renderer.getTableCellRendererComponent(table,
if (menuItem.isSelected()) { value,
list.add(menuItem.getItem()); isSelected, false, row, column);
} comp.removeMouseListener(mouseListener);
} comp.addMouseListener(mouseListener);
return list.toArray(); return comp;
} }

protected void setCellEditorValue(Object value) { private static class JXPopupMenu extends


for (int i = 0; i < checkBoxMenuItems.length; i++) { JPopupMenu {
JXCheckBoxMenuItem menuItem =
checkBoxMenuItems[i]; private Dimension packSize;
menuItem.setSelected(false);
} private boolean closeable;

if (value != null) { public Dimension getPackSize() {


Object[] values = (Object[]) value; return packSize;
for (int i = 0; i < checkBoxMenuItems.length; i++) { }
JXCheckBoxMenuItem menuItem =
checkBoxMenuItems[i]; public void setPackSize(Dimension packSize) {
for (int j = 0; j < values.length; j++) { this.packSize = packSize;
if (values [j] != null && }
menuItem.getItem().equals(values [j])) {
menuItem.setSelected(true); public void close() {
break; closeable = true;
} setVisible(false);
} }
}}
public void setVisible(boolean b) {
public void propertyChange(PropertyChangeEvent if (b || closeable) {
evt) { super.setVisible(b);
if (evt.getOldValue() == this && evt.getNewValue() closeable = false;
== null) { }
popupMenu.close(); }
} }
}
public Component private static class JXCheckBoxMenuItem
getTableCellEditorComponent(JTable table, extends JCheckBoxMenuItem {
Object value,
boolean isSelected, int row, int column) { private Object item;
table.removePropertyChangeListener("tableCellE
ditor", this); public JXCheckBoxMenuItem(Object item) {
table.addPropertyChangeListener("tableCellEditor super(item.toString());
", this); this.item = item;
}
setCellEditorValue(value);

15
ENI

public Object getItem() { }


return item;
}
} TestPopupEditor.java

} package popup;

ArrayTableCellRenderer.java import javax.swing.JFrame;


import javax.swing.JScrollPane;
package popup; import javax.swing.JTable;
import import javax.swing.table.TableCellRenderer;
javax.swing.table.DefaultTableCellRenderer;
public class TestPopupEditor extends JFrame {
public class ArrayTableCellRenderer extends
DefaultTableCellRenderer { public TestPopupEditor() {
Object[] couleurs = { "Blanc", "Rouge", "Vert",
private String separator; "Bleu", "Jaune", "Noir" };
public ArrayTableCellRenderer() { Object[] formes = { "Rond", "Carré", "Triangle" };
this(null); Object[] poids = { "1kg", "2kg", "3kg", "4kg",
} "5kg" };

public ArrayTableCellRenderer(String separator) { JTable jTable = new JTable(new Object[][] {


this.separator = separator != null ? separator : " "; new Object[] { new Object[] { "Rouge", "Vert" },
} null, null },
new Object[] { null, new Object[] { "Carré",
protected void setValue(Object value) { "Triangle" },
if (value != null) { new Object[] { "1kg", "2kg" } }, new Object[3] },
value = format((Object[]) value); new String[] { "Couleurs", "Formes", "Poids" });
}
super.setValue(value); jTable.setDefaultRenderer(Object.class, new
} ArrayTableCellRenderer());

protected Object format(Object[] values) { TableCellRenderer popupRenderer = new


boolean separate = false; ArrayTableCellRenderer();
StringBuffer sb = new StringBuffer(); jTable.getColumnModel().getColumn(0).setCellEdi
tor(
for (int i = 0; i < values.length; i++) { new PopupEditor(popupRenderer, couleurs));
if (values[i] != null) { jTable.getColumnModel().getColumn(1).setCellEdi
if (separate) { tor(
sb.append(getSeparator()); new PopupEditor(popupRenderer, formes));
} jTable.getColumnModel().getColumn(2).setCellEdi
sb.append(values[i]); tor(
separate = true; new PopupEditor(popupRenderer, poids));
}
} getContentPane().add(new JScrollPane(jTable));
return sb; setSize(600, 300);
} setVisible(true);
public String getSeparator() { }
return separator;
} public static void main(String[] args) {
public void setSeparator(String separator) { new TestPopupEditor();
this.separator = separator; }
} }

16

Vous aimerez peut-être aussi