Vamos a realizar un applet que contiene un componente JTable (un grid), es decir, un modo de presentación semejante a una hoja de cálculo. Además es un ejemplo de:
Un componente JTable es muy apropiado para mostrar una estructura de dados semejante a una tabla de una base de datos, en general cualquier estructura de tipo hoja de celdas.
El applet muestra en la parte superior un campo de texto y unos botones (Añadir y Eliminar). En la parte central muestra una pequeña hoja de cálculo con datos. Comportamiento:
El applet es del tipo Swing (JApplet) y por tanto exige una versión de Java 1.2 o superior. La página simplemente muestra el applet, no es del tipo "detectar JRE y descargar versión necesaria" (este tipo de página ya ha sido tratada):
Se pueden ver los botones (JButton) y el campo de texto (JTextField). Pero lo más esencial es entender JTable y JScrollPane. El primero es el componente en forma de rejilla (grid) y el segundo es el scroll del JTable.
public class applet_grid extends JApplet {
JTable tab; // Grid (tabla)
modelo_grid mod = new modelo_grid(); // Modelo de datos para el grid
JScrollPane scroll; // Scroll de grid (tabla)
JTextField texto = new JTextField( "Ponga aquí lo que desea añadir"); // Campo de texto
JButton boton_añadir = new JButton( "Añadir"); // Botón Añadir
JButton boton_elim = new JButton( "Eliminar"); // Botón Eliminar
....
Lo más importante es entender que JTable ha sido definido siguiendo el patrón modelo-vista. Donde la vista es el JTable y el modelo de datos es independiente de él, no está embebido en la vista:
El dódigo fuente de la vista puede encontrarlo aquí.
El dódigo fuente del modelo puede encontrarlo aquí.
La clase del modelo la construimos como subclase de AbstractTableModel.
class modelo_grid extends AbstractTableModel {
Vector filas = new Vector(); // Vector de filas (vector de vectores)
Vector columnas = new Vector(); // Vector de columnas
/*** Constructor: carga datos ***/
modelo_grid() { cargar_datos(); }
....
En nuestro caso hemos puesto en un vector las columnas (los nombres de las columnas para ser exactos) y en otro las filas. El vector de las filas es un vector de vectores (vector de filas). Ambos vectores se cargan en cargar_datos():
void cargar_datos() {
/**** Creo el vector que define las columnas ***/
columnas.add( (String) "Persona");
columnas.add( (String) "Actividad");
columnas.add( (String) "Objeto");
/**** Creo un vector de vectores (vector de filas) ***/
Vector fila0 = new Vector();
fila0.add( (String) "María");
fila0.add( (String) "Salta");
fila0.add( (String) "El charco");
filas.add( fila0 ); // Añado fila (vector) al vector de vectores
Vector fila1 = new Vector();
fila1.add( (String) "Pedro");
fila1.add( (String) "Juega");
fila1.add( (String) "Baloncesto");
filas.add( fila1 ); // Añado fila (vector) al vector de vectores
....
Lo importante es entender que la clase hereda de AbstractTableModel. Por tanto, debemos implementar los métodos abstractos de AbstractTableModel. Esta es una clase abstracta que ofrece un comportamiento básico y que tiene tres métodos abstractos:
Estos métodos son imprescindibles para que la vista pueda escribir los datos en las celdas. Ya sabemos que cuando heredamos de una clase abstracta, debemos implementar los métodos abstractos. En nuestro caso la implementación es muy sencilla, básicamente consiste en definir las acciones imprescindibles para que la vista pueda presentar los datos:
public int getColumnCount() {
return columnas.size();
}
public int getRowCount() {
return filas.size();
}
public Object getValueAt( int fila, int col ) {
Vector v = (Vector) filas.elementAt(fila);
return v.elementAt( col );
}
Lo que hemos hecho en estos métodos es sencillo, cuando devolvemos el número de de columnas (getColumnCount) lo único necesario es devolver el número de elementos del vector de columnas (columnas.size()). En el caso del número de filas se trata de devolver el número de elementos del vector de filas (filas.size()). En el método getValueAt() le indicamos al modelo lo que debe hacer cuando la vista le pida un elemento de la hoja.
Existen otros métodos que, aún no siendo imprescindibles, como los anteriores, como los anteriores, resultan de interés:
public void setValueAt( Object valor, int fila, int col ) {
Vector v = (Vector) filas.elementAt(fila);
v.set( col, valor);
}
public boolean isCellEditable( int fila, int col ) { return true; }
Para terminar con la clase del modelo es necesario explicar los métodos que ofrecen el servicio de añadir o eliminar filas de la hoja o tabla. Estos métodos definen lo que debemos hacer cuando se presionan los botones Añadir y Eliminar del applet:
public void eliminar( int fila ) {
filas.remove( fila );
this.fireTableRowsDeleted( fila, fila ); // Actualiza una fila de la vista
}
public void añadir( String texto, int col ) {
Vector fila_nueva = new Vector(); // Nueva fila
/*** Recorro todas las cols de la nueva fila, sólo pongo el texto en columna col ***/
for ( int i = 0; i < this.getColumnCount(); i ++) {
if (i != col)
fila_nueva.add("");
else
fila_nueva.add(texto);
}
filas.add( fila_nueva ); // Añado fila al modelo
this.fireTableRowsInserted( filas.size(),filas.size() ); // Ordeno a la tabla que se actualice
}
A continuación vamos a explicar lo que se hace en el método JBInit:
tab = new JTable( mod );
scroll = new JScrollPane( tab, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
boton_añadir.addActionListener(new applet_grid_boton_añadir_actionAdapter(this));
boton_elim.addActionListener(new applet_grid_boton_elim_actionAdapter(this));
JPanel panel_sup = new JPanel();
panel_sup.add( texto );
panel_sup.add( boton_añadir );
panel_sup.add( boton_elim );
this.getContentPane().setLayout( new BorderLayout() );
this.getContentPane().add( panel_sup, BorderLayout.NORTH );
this.getContentPane().add( scroll, BorderLayout.CENTER );
La respuesta al botón Añadir es sencilla: obtengo el texto que hay en el campo de texto y le ordeno al modelo que añada una nueva fila (pondrá el texto en la columna seleccionada). Recordar que el modelo es el responsable de actualizar la vista:
void boton_añadir_actionPerformed(ActionEvent e) {
String texto_para_añadir = texto.getText(); // Obtengo texto que voy a añadir
int col = tab.getSelectedColumn(); // Obtengo número de columna seleccionada
if ( col == -1 ) // Si no hay columna seleccionada
col = 0; // Selecciono la primera
/* Ordeno al modelo que añada nueva fila y que escriba texto en la columna col */
mod.añadir( texto_para_añadir, col);
}
La respuesta al botón Eliminar implica ordenar al modelo que elimine la fila seleccionada (si no hay selección, no hace nada). Recordar que el modelo es el responsable de actualizar la vista:
void boton_elim_actionPerformed(ActionEvent e) {
int fila = tab.getSelectedRow(); // Obtengo numero de fila seleccionada
if ( fila != -1 ) // Si hay fila seleccionada
mod.eliminar( fila); // Ordeno al modelo que elimine fila
}