précédent | suivant | table des matières
|
Le composant JTable permet d’afficher des tables de données, en permettant éventuellement l’édition de ces données. Un JTable ne contient pas ses données mais les obtient à partir d’un tableau d’objets à 2 dimensions, ou à partir d’un modèle de données. Le rendu et le mode d'édition des cellules de la table peuvent être modifiés.
Création d’une table simple
Object[][] donnees = {
{"Swing", "Astral", "standard",
Color.red, Boolean.TRUE},
{"Swing", "Mistral", "standard",
Color.yellow, Boolean.FALSE},
{"Gin", "Oasis", "standard",
Color.blue, Boolean.FALSE},
{"Gin", "boomerang", "compétition",
Color.green, Boolean.TRUE},
{"Advance", "Omega", "performance",
Color.cyan, Boolean.TRUE},
} ;
String[] titreColonnes = {
"marque","modèle", "homologation",
"couleur", "vérifiée ?"};
JTable jTable1 = new JTable(
donnees, titreColonnes); |
Permet d’afficher la table suivante : ![]() |
Pour éviter ces problèmes, il faut passer par un modèle.
|
Le composant JTable est un «visualisateur» qui prend les données à afficher dans un modèle qui implémente l’interface TableModel, ou qui dérive de la classe abstraite AbstractTableModel (javax.swing.table.AbstractTableModel). La classe AbstractTableModel implémente les méthodes de TableModel sauf :
Pour obtenir la même table que précédemment :
| Définir la classe : | Puis écrire : |
public class MonModele
extends AbstractTableModel{
Object donnees[][];
String titres[];
public MonModele(
Object donnees[][], String titres[]){
this.donnees = donnees;
this.titres = titres;
}
public int getColumnCount(){
return donnees[0].length;
}
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];
}
} |
MonModele mm = new MonModele(donnees, titreColonnes); JTable jTable2 = new JTable(mm); |
Pour obtenir une table contenant le triangle de Pascal :
| Définir la classe : | Puis écrire : |
public class MonModelePascal
extends AbstractTableModel{
public MonModelePascal(){}
public int getColumnCount(){
return 10;
}
public Object getValueAt(int parm1, int parm2){
if(parm1==0)
if( parm2==0) return "1"; else return "";
else if(parm2>parm1) return "";
else if (parm2==parm1) return "1";
else if (parm2==0) return "1";
else{
int i = Integer.parseInt(
(String)getValueAt(parm1-1, parm2-1));
int j = Integer.parseInt(
(String)getValueAt(parm1-1, parm2));
return Integer.toString(i+j);
}
}
public int getRowCount() {
return 10;
}
public String getColumnName(int col){
return "";
}
} |
MonModelePascal mmP = new MonModelePascal(); JTable jTable3 = new JTable(mmP); |
Méthodes de AbstractTableModel
Quelques méthodes de la classe AbstractTableModel :
int findColumn(String co) |
Retourne l'indice du colonne à partir de son nom. |
Class getColumnClass(int co) |
Retourne la classe des objets de la colonne.
public Class getColumnClass(int c){
// un exemple : <
return getValueAt(0, c).getClass();
} |
boolean isCellEditable( int li, int co) |
Retourne true si la cellule est éditable, et false sinon.
public boolean isCellEditable(
int row, int col) {
// toutes les cellules éditables :
return true;
// seules les premières de chaque colonne
return row ==0;
// seules les cellules de la colonne 3
return col == 3;
} |
void setValueAt( Object v, int li, int co) |
Remplace la valeur à la ligne li et colonne co par v. |
Evénements
La classe JTable écoute les événements en provenance de son modèle. Le modèle a plusieurs méthodes pour signaler une modification des données :
Modification du rendu de cellule
Les cellules sont visualisées par des instances de DefaultTableCelleRenderer défini par :
public class DefaultTableCellRenderer extends JLabel implements TableCellRenderer{
...
}
On peut changer la couleur du fond, ou la couleur des caractères, mais pas la police des caractères, qui rest la police de la table.
((DefaultTableCellRenderer)(maTable.getDefaultRenderer(String.class))).setForeground(Color.BLUE);
Pour visualiser une cellule, autrement que de façon standard, il faut créer les classes visualisateur de cellule qui implémentent l’interface TableCellRenderer.
Exemples :
| La couleur, plutôt que Color(r= , g= b= ) | OUI/NON plutôt que true/false |
class MonAfficheurCelluleCouleur
extends JLabel
implements TableCellRenderer {
public MonAfficheurCelluleCouleur() {
this.setOpaque(true);
}
public Component
getTableCellRendererComponent(
JTable table, Object value,
boolean isSelected,
boolean hasFocus,
int row, int col) {
setBackground((Color) value);
return this;
}
} |
class MonAfficheurCelluleBool extends JLabel
implements TableCellRenderer {
public MonAfficheurCelluleBool() {
this.setOpaque(true);
}
public Component getTableCellRendererComponent(
JTable table, Object value,
boolean isSelected, boolean hasFocus,
int row, int col) {
setForeground(Color.red);
setHorizontalAlignment(JLabel.CENTER);
if(((Boolean)value).booleanValue())
setText("OUI");
else setText("NON");
return this;
}
} |
maTable.setDefaultRenderer(Color.class, new MonAfficheurCelluleCouleur()); |
maTable.setDefaultRenderer(Boolean.class, new MonAfficheurCelluleBool()); |
Modification de l'édition de cellule
Pour spécifier un nouvel éditeur de cellule, il faut :
Exemple avec DefaultCellEditor :
enum Homologation {
standard("standard"),
performance("performance"),
competition("compétition");
String chaine;
Homologation(String s){
this.chaine = s;
};
public String toString(){
return chaine;
}
} |
Object [] x = {
Homologation.standard,
Homologation.performance,
Homologation.competition};
JComboBox jComboBox = new JComboBox(x);
maTable.setDefaultEditor(Homologation.class,
new DefaultCellEditor(jComboBox)); |
Exemples avec dérivation de AbstractCellEditor et implémentation de TableCellEditor :
| Editeur de couleur | Editeur de Booléen |
class EditeurCouleur extends AbstractCellEditor
implements TableCellEditor {
Color couleurCourante;
JButton bouton;
JColorChooser choixCouleur;
public EditeurCouleur() {
bouton = new JButton();
bouton.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent e){
bouton.setBackground(couleurCourante);
Color c = JColorChooser.showDialog(
null, "choix d'une couleur",
couleurCourante);
if(c!=null) {
couleurCourante = c;
fireEditingStopped();
}
}
});
bouton.setBorderPainted(false);
}
public Object getCellEditorValue() {
return couleurCourante;
}
public Component getTableCellEditorComponent(
JTable table, Object value,
boolean isSelected,
int row, int column) {
couleurCourante = (Color) value;
return bouton;
}
} |
class EditeurBooleen
extends AbstractCellEditor
implements TableCellEditor {
Boolean valeurCourante;
JButton bouton
public EditeurBooleen() {
bouton = new JButton();
bouton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
int ret = JOptionPane.showConfirmDialog(null,
"OUI pour TRUE\nNON pour FALSE.",
"édition de valeur booléenne",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
if (ret==JOptionPane.OK_OPTION)
valeurCourante = Boolean.TRUE;
else valeurCourante = Boolean.FALSE;
fireEditingStopped();
}
});
bouton.setBorderPainted(false);
}
public Object getCellEditorValue() {
return valeurCourante;
}
public Component getTableCellEditorComponent(
JTable table, Object value,
boolean isSelected,
int row, int column){
valeurCourante = (Boolean) value;
return button;
} |
maTable.setDefaultEditor(Color.class, new EditeurCouleur()); |
maTable.setDefaultEditor(Boolean.class, new EditeurBooleen()); |
Trier les lignes
Pour ne pas changer l'ordre des valeurs du modèle, on introduit un filtre entre le modèle et le JTable.
TableModel modele = new MonTableModele();
filtre = new FiltreTriModel(modele);
maTable = new JTable(filtre);
maTable.getTableHeader().addMouseListener(new MouseAdapter(){
public void mouseClicked(MouseEvent e){
int tableC = jTable2.columnAtPoint(e.getPoint());
int modelCol = jTable2.convertColumnIndexToModel(tableC);
filtre.sort(modelCol);
}
});
et le Filtre est défini comme suit :
class FiltreTriModel extends AbstractTableModel{
TableModel model;
Ligne [] lignes;
int colonneTri;
FiltreTriModel ( TableModel m){
model = m;
lignes = new Ligne[model.getRowCount()];
for( int i = 0; i<lignes.length; ++i)
lignes[i] = new Ligne(i);// voir la classe Ligne plus bas
}
public int getRowCount() {
return model.getRowCount();
}
public int getColumnCount() {
return model.getColumnCount();
}
public Object getValueAt(int rowIndex, int columnIndex) {
return model.getValueAt(lignes[rowIndex].index, columnIndex);
}
public Class<?> getColumnClass( int i){
return model.getColumnClass(i);
}
public String getColumnName(int i){
return model.getColumnName(i);
}
// a implémenter si la table est éditable
public boolean isCellEditable(int row, int col) {
return true;
}
// a implémenter si les données peuvent changer
public void setValueAt(Object value, int row, int col) {
model.setValueAt(value, lignes[row].index, col);
fireTableCellUpdated(lignes[row].index, col);
}
public void sort(int c){
colonneTri = c;
try{
Arrays.sort(lignes);
fireTableDataChanged();
}catch (RuntimeException e){} // les données ne sont pas comparables !
}
//------- la classe Ligne -------
private class Ligne implements Comparable{
int index;
public Ligne (int i){index = i;}
public int compareTo(Object o) {
Ligne autreLigne = (Ligne)o;
Object cellule = model.getValueAt(index, colonneTri);
Object autreCellule = model.getValueAt(autreLigne.index, colonneTri);
return((Comparable)cellule).compareTo(autreCellule);
}
}
}
Changer les en-têtes de colonne.
TableCellRenderer tbch = getTableHeaderRenderer();
for (int i = 0; i < maJTable.getColumnCount(); i++) {
TableColumn tc = maJTable.getColumnModel().getColumn(i);
tc.setHeaderRenderer(tbch);
}
Avec une méthode getTableHeaderRenderer définie par :
public static TableCellRenderer getTableHeaderRenderer() {
return new TableCellRenderer() {
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
JLabel lbl = new JLabel();
lbl.setBorder(new EtchedBorder());
lbl.setHorizontalAlignment( JLabel.CENTER );
Font font = new java.awt.Font("Comic Sans MS", java.awt.Font.PLAIN, 14);
lbl.setOpaque(false);
lbl.setFont(font);
lbl.setForeground(Color.BLUE);
lbl.setText((String) value);
return lbl;
}
};