Un JTable de java sabe manejar algunos de los datos standard de java, sabe dibujar celdas que contengan un String, un Integer, etc. Es posbible que en un momento dado queramos hacer que nuestros datos se pinten de alguna manera distinta o que tengamos datos que el JTable no sabe manejar, alguna clase propia. Por ejemplo, podemos querer pintar iconos en las celdas del JTable, hacer que cambie el tipo de letra, sus colores, etc.
Para ello el JTable nos proporciona la posibilidad de cambiar su TableCellRenderer , la clase que sabe y se encarga de dibujar los datos que hay en cada celda. Podemos hacer nuestro propio TableCellRenderer que dibuje de forma especial esos datos o que entienda nuevos tipos de datos. Luego basta con pasarle este TableCellRenderer al JTable .
Para hacer nuestro propia clase capaz de dibujar datos en una celda, debemos hacer una clase que implemente la interface TableCellRenderer. Esta sólo tiene un método:
class RenderTabla implements TableCellRenderer
{
public Component getTableCellRendererComponent(JTable
table, Object value, boolean isSelected,
boolean hasFocus, int row, int
column) { ... }
}
Sólo tenemos que hacer un método. Este método debe convertir el valor value que recibe en un Component de java (un JPanel, un JLabel, un JButton o lo que queramos) y devolverlo. Para poder hacer esta conversión, el método recibe:
Una vez construida nuestra clase, sólo debemos llamar al método adecuado de JTable.
JTable miTabla = new JTable();
RenderTabla miRender = new RenderTabla();
miTabla.setDefaultRenderer ( ..., miRender);
Nos falta un parámetro por poner en la función. Vamos a ver qué es ese parámetro en el siguiente apartado.
El JTable considera que todos los datos de una columna son de la misma clase. Cuando hicimos el modelo de datos, en uno de los métodos, devolviamos el tipo de dato (la clase) para cada columna. Era el método getColumnClass(). A través de este método el JTable asocia para cada columna una clase y considera que los datos de esa columna son de esa clase. Puede haber varias columnas con datos de la misma clase.
Cuando le damos al JTable un TableCellRenderer, asocia ese TableCellRenderer a una clase concreta. De forma que cuando tenga que dibujar un dato que sea de esa clase, llamará a este TableCellRenderer.
Por ello, si en el modelo de datos hicimos que getColumnClass() devolviera Integer.class para la terecera columna (la edad), podemos asociar un TableCellRenderer a los Integer , para que esta tercera columna se dibuje con nuestro TableCellRenderer .
miTabla.setDefaultRenderer (Integer.class, miRender);
Los datos que no sean Integer (como las dos columnas de nombre y apellido, que son String), seguirán siendo tratadas con el TableCellRenderer por defecto de la tabla
Nuestro RenderTabla debe implementar TableCellRenderer que solo tiene un método, como indicamos arriba.
En nuestro ejemplo hemos hecho que RenderTabla sea capaz de interpretar tanto String (para nombre y apellido) como Integer (para edad). En ambos casos creamos un JLabel y lo rellenamos a nuestro gusto. Podríamos crear cualquier Component de java, desde el sencillo JLabel del ejemplo, hasta otro JPanel que contenga dentro lo que queramos.
// Creamos la etiqueta
JLabel etiqueta = new JLabel();
// Ponemos un color distinto para la etiqueta
según si la celda está o no seleccionada.
if (isSelected)
etiqueta.setBackground (Color.CYAN);
else
etiqueta.setBackground (Color.YELLOW);
// Si el objeto que nos pasan es un String,
lo ponemos en el JLabel.
if (value instanceof String)
{
// Para que el JLabel
haga caso al color de fondo, tiene que ser opaco.
etiqueta.setOpaque(true);
etiqueta.setText((String)value);
}
// Si el objeto que nos pasan es Integer,
metemos un icono en la etiqueta, en función del valor del Integer.
if (value instanceof Integer)
{
int valor = ((Integer)value).intValue();
if (valor > 60)
etiqueta.setIcon (iconos[2]);
else if (valor > 30)
etiqueta.setIcon (iconos[1]);
else
etiqueta.setIcon (iconos[0]);
// Ponemos como tooltip
el valor
etiqueta.setToolTipText (Integer.toString (valor));
}
// Devolvemos la etiqueta que acabamos
de crear.
return etiqueta;
Con esto queda relleno nuestro método. Es una práctica bastante habitual, para ahorrar clases, el que nuestro RenderTabla, además de implementar TableCellRenderer, herede, por ejemplo, de JLabel. Cuando nos pasen un objeto, lo pintamos sobre la misma clase (es decir, en vez de etiqueta en el código, ponemos this) y finalmente hacemos un return this.
Esto funcionaría porque el JTable no mete directamente el JLabel en la celda. Si fuera así, deberíamos devolver un JLabel distinto cada vez. En realidad, JTable hace una "foto" del JLabel cuando se lo devolvemos y pone la foto en la celda. Por eso podemos reusar el mismo JLabel una y otra vez. Por eso mismo, si devolvemos un JButton, NO se podrá pulsar en la tabla (salvo que cambiemos también el TableCellEditor) ya que sólo es una foto del botón. Y si ponemos un gif animado NO se verá moviéndose en la celda. Sólo es una instantánea del gif en una determinada posición.
Puedes ver un applet de ejemplo funcionando y con acceso a los fuentes.