précédent | suivant | table des matières

JTable

Sommaire
  1. Table simple
  2. Table avec modèle
    1. Méthodes de AbstractTableModel
    2. événements
  3. Modification du rendu TableCellRenderer
  4. Modification de l'édition de cellules
  5. Trier les lignes.
  6. Changer les en-têtes de colonne.

Démonstration

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.

1Cré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 : 

JTable1

Pour éviter ces problèmes, il faut passer par un modèle.


2Création d’un modèle de table

JTable2

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

21Mé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.

22Evé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 :

3Modification 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());

4Modification de l'édition de cellule

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

  1. Utiliser la classe DefaultCellEditor. La classe DefaultCellEditor permet de créer des éditeurs à partir de JTextField, de CheckBox, ou de JComboBox.
  2. Créer une classe qui implémente TableCellEditor, ou qui dérive de AbstractCellEditor. Cette classe doit définir au moins deux méthodes :

 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());

5Trier 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);
      }
   }
}

6Changer 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;
   }
};

haut de la page