sabato 31 marzo 2012

Java: visualizzare gli elementi di una JTable come degli JCkeckBox

Se si vuole una JTable (una tabella swing di java) dove gli elementi (righe) selezionate sono visualizzate con un checkbox selezionato  e viceversa, con questo risultato:
è possibile modificare una proprietà della JTable tramite il metodo setDefaultRenderer(). Nel caso dello screenshot sopra, la tabella è divisa in un numero indeterminato di righe, e n° 6 colonne. La colonna interessata a visualizzare le celle con una checkbox è la prima, quindi il mio interesse è di modificare la colonna in questione. Per prima cosa definiamo la classe CheckBoxTableCellRenderer, ovvero una classe che eredita da JCheckBox e che si appoggià all'interfaccia JTableCellRenderer. Quest'ultima contiene solamente un metodo: getTableCellRendererComponent(), che noi definiamo a nostro piacere e che deve ritornare il componente (oggetto grafico) che vogliamo visualizzare nella casella, dunque il JCkeckBox

import javax.swing.JCheckBox;
import javax.swing.table.*;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.SwingConstants;
import java.awt.Component;
import java.awt.Insets;
import javax.swing.JTable;
import javax.swing.UIManager;

class CheckBoxTableCellRenderer
 extends JCheckBox
 implements TableCellRenderer {

 Border noFocusBorder;
 Border focusBorder;

 public CheckBoxTableCellRenderer() {
   super();
   setContentAreaFilled(true);  // sfondo opaco
   setBorderPainted(true);
   setHorizontalAlignment(SwingConstants.CENTER); (allineamento orizzontale centrato)
   setVerticalAlignment(SwingConstants.CENTER);   (allineamento verticale centrato)
 }

    @Override
 public Component getTableCellRendererComponent(JTable table, Object value,
                                                boolean isSelected,
                                                boolean hasFocus,
                                                int row, int column) {

   if(table == null) {
     //operazioni da fare nel caso la tabella non contenga alcun record
   }
   else { 
     if (isSelected) { //variabile locale che è vera se la riga è selezionata

         setForeground(table.getSelectionForeground());
         setBackground(table.getSelectionBackground());
     }
     else {

         setForeground(table.getForeground());
         setBackground(table.getBackground());
     }

     setEnabled(table.isEnabled());
     //questo pezzo di codice agisce sul bordo, per eliminare l'effetto "tasto"
     if (hasFocus) {
       if (focusBorder == null) {
         focusBorder = UIManager.getBorder("Table.focusCellHighlightBorder");
       }
       setBorder(focusBorder);
     }
     else {
       if (noFocusBorder == null) {
         if (focusBorder == null) {
           focusBorder = UIManager.getBorder("Table.focusCellHighlightBorder");
         }
         if (focusBorder != null) {
           Insets n = focusBorder.getBorderInsets(this);
           noFocusBorder = new EmptyBorder(n);
         }
       }
       setBorder(noFocusBorder);
     }

     //seleziona o meno il JCheckBox, in base al valore della cella (riga selezionata o meno)
     setSelected(Boolean.TRUE.equals(value));
   }
  //ritorna il JCheckBox 
   return this;
 }
}


Definita la classe CheckBoxTableCellRenderer, dobbiamo adesso adottarla nella JTable.
Supponiamo di avere una JTable di nome "table", quindi:



        TableColumnModel tcm = table.getColumnModel();
        TableColumn tc = tcm.getColumn(0); //la prima colonna è quella che ci interessa, in questo esempio
        tc.setCellRenderer(new CheckBoxTableCellRenderer());  

Fatto ciò, la prima colonna dunque apparirà come una lista di JCheckBox ordinati in verticale, proprio come nello screenshot sopra. Ma c'è ancora una cosa che manca: l'editor. Infatti se proviamo a modificare il valore di una JCheckBox (quindi il selezionare/deselezionare una riga), notiamo un'anomalia: compare la scritta "true" o "false". Perché? Perchè una JTable di default implementa una DefaultCellEditor, che mostra le celle come dei JTextBox, quindi mostra "true" o "false", in modo testo. Dunque un piccolo passaggio è doveroso:
tc.setCellEditor(new DefaultCellEditor(new JCheckBox()));
this.setCellSelectionEnabled(false);

Infatti (pace all'anima nostra), DefaultCellEditor prevede un costruttore con una JCheckBox. Mentre setCellSelectionEnabled() evita che si selezionino (con conseguente cambiamento del colore) le righe intere, infatti  a noi interessa distinguere se una riga è selezionata o meno guardando il JCheckBox.

Ricapitolando, i due passaggi fondamentali da compiere sulla colonna sono 2: modificare il suo renderer, e modificare il suo editor . Il renderer viene eseguito solamente quando la tabella viene disegnata, mentre il comportamento (editor) viene eseguito ogni qual volta modifichiamo il valore di una JCheckBox.
E' tutto.

Alberto

Nessun commento:

Posta un commento