Un applet Swing que usa JTable (grid)


Introducción

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.

¿Qué hace el applet?

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


Los atributos del applet

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
   ....
	

El patrón modelo-vista

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í.

El modelo

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:

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:

Configurando la vista

A continuación vamos a explicar lo que se hace en el método JBInit:

  1. Lo primero es esencial: crear la tabla, asignándole el modelo correspondiente.
  2. 
          tab = new JTable( mod );
    	
  3. Configuramos la tabla:
  4.  

  5. Creo el scroll, asignándole la tabla y una política de scrolls (mediante constantes disponibles en JScrollPane). Con estas constantes indicamos que el scroll vertical va a estar siempre visible y el horizontal sólo cuando sea necesario:
  6. 
          scroll = new JScrollPane( tab, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    	
  7. Añado a cada botón su listener de eventos:
  8. 
          boton_añadir.addActionListener(new applet_grid_boton_añadir_actionAdapter(this));
          boton_elim.addActionListener(new applet_grid_boton_elim_actionAdapter(this));
    	
  9. Un ejemplo más de anidamiento de paneles. En el panel superior pongo el campo de texto y los botones:
  10. 
          JPanel panel_sup = new JPanel();
          panel_sup.add( texto );
          panel_sup.add( boton_añadir );
          panel_sup.add( boton_elim );
    	
  11. Ordeno al panel raíz que su adminstrador de diseño va a ser de la clase BorderLayout:
  12. 
          this.getContentPane().setLayout( new BorderLayout() );
    	
  13. Añado al panel raíz dos cosas: primero el panel superior (en la zona Norte) y en segundo lugar el panel scroll (con su tabla correspondiente):
  14. 
          this.getContentPane().add( panel_sup, BorderLayout.NORTH );
          this.getContentPane().add( scroll, BorderLayout.CENTER );
    	

Respondiendo a los eventos

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
   }
	



Volver al índice