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