Adaptadores para bases de datos

Un adaptador (Adapter) es un mecanismo de Android que hace de puente entre nuestros datos y las vistas contenidas en un ListView (o un GridView o Spinner). Dado que es muy frecuente usar bases de datos e nuestras aplicaciones, existen adaptadores específicos para este caso. La forma de trabajar habitual es hacer una consulta en una base de datos (comando SQL SELECT) y obtener un objeto Cursor con el resultado. Este objeto será pasado al adaptador para que lo recorra y cree tantas vistas como registros se hayan encontrado.

La forma más sencilla de pasar los datos del Cursor obtenido en una consulta a un ListView es utilizar la clase SimpleCursorAdapter. En una lección acterior vimos un ejemplo que usaba el siguiente código:

adaptador = new SimpleCursorAdapter(this,
      R.layout.elemento_lista,
      Lugares.listado(),
      new String[] { "nombre", "direccion"},
      new int[] { R.id.nombre, R.id.direccion},
   0);

Los parámetros necesarios son: el contexto de nuestra aplicación, un layout con el diseño básico que queremos repetir, un objeto Cursor con una consulta a nuestra base de datos, una lista con los nombres de los registros que queremos visualizar en cada layout, una lista con los id de recurso de los elementos del layout donde queremos visualizar estos datos y finalmente un campo de opciones.

Este tipo de adaptador tiene una restricción, los id de los elementos a reemplazar solo pueden ser del tipo TextView o ImageView. Por lo que cosas más complejas como modificar el valor de un RatingBar no son posibles.

Cuando queramos definir un Adaptador sin ningún tipo de restricciones aprendimos a extender la clase BaseAdapter. A continuación aprenderemos a usar otra clase sin restricciones, pero que nos facilita mucho del trabajo a realizar cuando los datos a mostrar vienen de un objeto Cursor. Se trata de la clase CursorAdapter.

Ejercicio: Utilizando un CursorAdapter en Mis Lugares

1.     Crea la clase AdaptadorCursorLugares en MisLugares con el siguiente código:

public class AdaptadorCursorLugares extends CursorAdapter {
   private LayoutInflater inflador; // Crea Layouts a partir del XML
   TextView nombre, direccion, distancia;
   ImageView foto;
   RatingBar valoracion;

   public AdaptadorCursorLugares(Context contexto, Cursor c) {
      super(contexto, c, false);
   }

   @Override
   public View newView(Context contexto, Cursor c, ViewGroup padre) {
      inflador = (LayoutInflater) contexto

            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
      View vista = inflador.inflate(R.layout.elemento_lista, padre, false);
      return vista;
   }

   @Override
    public void bindView(View vista, Context contexto, Cursor c) {
      nombre = (TextView) vista.findViewById(R.id.nombre);
      direccion = (TextView) vista.findViewById(R.id.direccion);
      foto = (ImageView) vista.findViewById(R.id.foto);
      valoracion = (RatingBar) vista.findViewById(R.id.valoracion);
      nombre.setText(c.getString(c.getColumnIndex("nombre")));
      direccion.setText(c.getString(c.getColumnIndex("direccion")));
      int tipo = c.getInt(c.getColumnIndex("tipo"));
      foto.setImageResource(TipoLugar.values()[tipo].getRecurso());
      foto.setScaleType(ImageView.ScaleType.FIT_END);
      valoracion.setRating(c.getFloat(c.getColumnIndex("valoracion")));
      distancia = (TextView) vista.findViewById(R.id.distancia);
    GeoPunto posicion = new GeoPunto(
        c.getDouble(c.getColumnIndex("longitud")),
        c.getDouble(c.getColumnIndex("latitud")));
      if (Lugares.posicionActual != null && posicion != null
            && posicion.getLatitud() != 0) {
         int d = (int) Lugares.posicionActual.distancia(posicion);
         if(d < 2000) {
            distancia.setText(d + " m");
         } else {
            distancia.setText(d / 1000 + "Km");
         }
      }
   }

}

NOTA: Para añadir los import adecuados pulsa Alt-Intro en Android Studio o Ctrl-Shift-O en Eclipse.  Cuando te pregunte sobre el paquete de CursorAdapter selecciona: android.support.v4.widget.CursorAdapter

Para extender un CursorAdapter hemos de seguir los siguientespasos:

Primero, llamamos al constructor indicando el contexto, el cursor con la consulta y un booleano indicando si la consulta ha de ser regenerada (se recomienda el valor false).

Luego, sobreescribimos el método newView() para crear cada vista de la consulta. En este método no hay que rellenar los datos correspondientes solo crear las vistas.

Finalmente, sobreescribimos el método bindView() para rellenar los datos de cada vista. Se nos pasa la vista creada de la llamada anterior y un cursor situado en el elemento que nos toca rellenar en este momento. Con esta información resulta muy fácil rellenar los diferentes elementos. Por ejemplo, el siguiente código pone el texto en la vista nombre utilizando el String obtenido en la posición actual del cursor, tomando el valor del segundo campo o columna. Si vas a la definición de la tabla lugares en primer lugar está el campo _id, en segundo lugar nombre, en tercero dirección, Además ten en cuenta que se empieza a numerar desde el cero.

nombre.setText(c.getString(1));

Esta forma de trabajar resulta poco legible, además el orden de las columnas puede cambiar en un SELECT donde no se seleccionen todas. En el código hemos preferido usar la siguiente construcción:

nombre.setText(c.getString(c.getColumnIndex("nombre")));

El método getColumnIndex() buscará la columna por su nombre devolviendo su índice.

2.     Abre la clase MainActivity y en el método onCreate() comenta las líneas que inicilizan adaptador y reemplazalas por la siguiente:

adaptador = new AdaptadorCursorLugares(this, Lugares.listado());

3.     Ejecuta la aplicación y verifica que las vistas se muestra correctamente.