Programación Android, Ejemplo de Bases de datos & ListView I

Share if you like...Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn0

Android SQLite

Después de explicar cómo gestionar bases de datos en las entradas Bases de Datos I (Modelo-Vista-Controlador), Base de datos II y Bases de Datos III, vamos a mostrar un ejemplo práctico en el que utilizaremos también un ListView para mostrar los datos que recojamos de la Base de Datos, y además incorporaremos la orientación a objetos en nuestro código, para que quede más ordenado el código de nuestra aplicación y posteriormente sea más fácil de mantener.

En primer lugar vamos a explicar un poco en qué consistirá el proyecto: Vamos a crear dos tablas distintas. En la primera, en caso de que no contenga datos, insertaremos una colección de datos al ejecutar la aplicación. Una vez que tengamos datos en la primera de las tablas, pulsando un botón en la interfaz gráfica copiaremos el contenido de la primera tabla a una segunda tabla. También habilitaremos un botón que borrará todo el contenido de la segunda tabla. Por último, pulsando uno de los registros del ListView en que se muestran los datos de la segunda tabla se abrirá una nueva Activity en la que mostraremos en detalle los datos que corresponden a ese registro, y además podremos modificarlos.

Creando el interfaz principal

Para la interfaz hemos optado por un LinearLayout que contiene 2 botones y 2 ListViews, además de 2 TextViews para ayudar a diferenciar entre los ListViews:

Main Layout
Main Layout

El código sería:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/LinearLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".ListViewBDMain" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <Button
            android:id="@+id/btnCopiar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="T1 a T2"
            android:layout_weight="1" />

        <Button
            android:id="@+id/btnBorrar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Borrar T2" />

    </LinearLayout>

    <TextView
        android:id="@+id/tvTabla1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Tabla 1:" />

    <ListView
        android:id="@+id/lvTabla1"
        android:layout_width="match_parent"
        android:layout_height="165dp" >
    </ListView>

    <TextView
        android:id="@+id/tvTabla2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Tabla 2:" />

    <ListView
        android:id="@+id/lvTabla2"
        android:layout_width="match_parent"
        android:layout_height="165dp" >
    </ListView>

</LinearLayout>

Creando la base de datos

Una vez que tenemos la interfaz gráfica de de nuestra Activity principal, crearemos la clase que va a gestionar nuestra base de datos, inicialmente quedaría así:

public class DBAdapter {
	final static int VERSION = 1;
	final static String DB_NAME = "Ejemplo";

	private DBOpenHelper dbHelper;
	private SQLiteDatabase sqlDB;

	public DBAdapter(Context context) {
		dbHelper = new DBOpenHelper(context);
	}

	public void open() {
		sqlDB = dbHelper.getWritableDatabase();
	}

	public void close() {
		sqlDB.close();
	}

	private class DBOpenHelper extends SQLiteOpenHelper {

		public DBOpenHelper(Context context) {
			super(context, DB_NAME, null, VERSION);
		}

		@Override
		public void onCreate(SQLiteDatabase db) {
			// TODO Crear tablas

		}

		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

		}
	}
}

Como recomendación, ahora que empezamos a crear diversas clases para cada tarea de nuestras aplicaciónes, deberíamos empezar a ordenar también nuestras clases en el proyecto, creando diferentes packages en función del tipo de clase que estemos utilizando. Nosotros como norma utilizamos el package que se crea por defecto en nuestro proyecto para almacenar las Activities, y posteriormente creamos un package distinto en función de las clases. Por ejemplo nuestra clase Application irá en el package com.proyectosimio.application, y las clases que correspondan a la gestión de la base de datos irán en el package com.proyectisimio.database. De esta manera nos será más fácil localizar una clase dentro de nuestro proyecto.

Una vez que tenemos el código principal de la clase, pasaremos a crear una nueva clase por cada una de las tablas que tenga nuestra BD, y estas clases serán las que gestionen internamente todo lo que tenga que ver con ellas. Es decir, contendrán los campos de las tablas, y los métodos insert, update, delete, etc. que se refieran únicamente a la propia tabla. Como dijimos que en este ejemplo vamos a utilizar dos tablas que contendrán la misma información, nuestras dos clases van a ser muy parecidas, salvo por el nombre de la tabla y algún método específico para cada clase. En primer lugar crearemos los campos y métodos básicos que necesitaremos en ambas clases:

/**
 * Clase que gestiona la tabla tabla1 de nuestra BD
 *
 * @author ProyectoSimio
 *
 */
public class Tabla1 {
	// Nombre de la tabla
	private final static String NAME = "tabla1";

	// Objeto que utilizamos para acceder a la base de datos.
	private SQLiteDatabase sqlDB;

	/**
	 * Constructor
	 *
	 * @param sqlDB
	 */
	public Tabla1(SQLiteDatabase sqlDB) {
		this.sqlDB = sqlDB;
	}

	/**
	 * Clase que contiene los campos de la tabla.
	 *
	 * @author ProyectoSimio
	 *
	 */
	public class Columns implements BaseColumns {
		public final static String ID = "_id";
		public final static String NOMBRE = "nombre";
		public final static String APELLIDO = "apellido";
		public final static String TELEFONO = "telefono";
	}

	// Objeto en el que guardamos todos los nombres de los campos de la tabla
	// para utilizarlos posteriormente en algunos métodos.
	private final static String[] COLUMNS = { Columns.ID, Columns.NOMBRE,
			Columns.APELLIDO, Columns.TELEFONO };

	// Sentencia para crear la tabla en la BD.
	public final static String CREATE_TABLE = "create table if not exists "
			+ NAME + "(" + Columns.ID + " integer primary key autoincrement, "
			+ Columns.NOMBRE + " varchar(30) not null, " + Columns.APELLIDO
			+ " varchar(30), " + Columns.TELEFONO + " varchar(25)";

	/**
	 * Inserta una nueva tupla en la tabla
	 *
	 * @param nombre
	 * @param apellido
	 * @param telefono
	 * @return true si se inserta correctamente.
	 */
	public boolean insert(String nombre, String apellido, String telefono) {
		// Relacionamos campos con valores en el objeto ContentValues.
		ContentValues values = new ContentValues();
		values.put(Columns.NOMBRE, nombre);
		values.put(Columns.APELLIDO, apellido);
		values.put(Columns.TELEFONO, telefono);

		// El método insert devuelve un long que corresponde al valor del _id
		// que se inserta, y -1 si falla la inserción. Si es superior a 0
		// implica que se inserta correctamente, por lo tanto lo que devolvemos
		// es si lo que devuelve el insert es superior o no a 0.
		return sqlDB.insert(NAME, null, values) > 0;
	}

	/**
	 * Devuelve el cursor con todos los nombres registrados en la tabla.
	 *
	 * @return Cursor
	 */
	public Cursor getNombres() {
		String[] columns = { Columns.NOMBRE };
		return sqlDB.query(NAME, columns, null, null, null, null, null);
	}

	/**
	 * Devuelve el nombre de la tabla.
	 *
	 * @return NAME
	 */
	public String getName() {
		return NAME;
	}

	/**
	 * Devuelve el array con los nombres de todos los campos de la tabla.
	 *
	 * @return COLUMNS
	 */
	public String[] getColumns() {
		return COLUMNS;
	}
}

Este código sería el mismo para ambas tablas, por lo que sólo tendríamos que cambiar el nombre de la clase a Tabla2, y en el nombre de la tabla pondremos tabla2 en lugar de tabla1. El método getNombres() lo utilizaremos para recoger todos los nombres de las tablas y mostrarlos en el ListView correspondiente. Aunque lo hemos creado ya, no lo vamos a utilizar hasta que vayamos a mostrar los datos en el ListView.

A partir de aquí se diferenciarán nuestras dos clases, ya que en la segunda tabla necesitaremos métodos que nos permitan actualizar y borrar los datos, pero por el momento sólo hemos creado el botón de borrado, y el proceso de actualizar nos obligará a utilizar una nueva clase que aún no hemos creado, por lo que por ahora sólo añadiremos el método para borrar los datos de la clase Tabla2:

	/**
	 * Borra la tabla y vuelve a crearla en la BD.
	 */
	public void deleteTable() {
		sqlDB.execSQL("drop table if exists " + NAME);
		sqlDB.execSQL(CREATE_TABLE);
	}

Ahora que tenemos nuestras dos tablas creadas, vamos a instanciar variables de cada tabla en la clase DBAdapter, para poder leer y guardar datos en las tablas. Para ello declararemos las variables al inicio de la clase, y en el método open() de DBAdapter las inicializaremos:

// Declaramos las variables.
Tabla1 tabla1;
Tabla2 tabla2;

/**
 * Abre la conexión con la base de datos.
 */
public void open() {
	sqlDB = dbHelper.getWritableDatabase();

	tabla1 = new Tabla1(sqlDB);
	tabla2 = new Tabla2(sqlDB);
}

Posteriormente en el método onCreate() de la clase DBOpenHelper añadiremos la creación de las tablas:

// Dentro de la clase DBOpenHelper
@Override
public void onCreate(SQLiteDatabase db) {
	sqlDB.execSQL(Tabla1.CREATE_TABLE);
	sqlDB.execSQL(Tabla2.CREATE_TABLE);

}

Creando Application

Ahora pasaremos a crear nuestra clase Application personalizada, y recordemos que deberemos indicarlo también en el manifiest.xml, para que nuestro programa utilice nuestra propia clase. La llamaremos BDApplication, que extenderá de Application, y necesitará implementar los métodos onCreate() y onTerminate(). Además, tendrá que contener un objeto de la clase DBAdapter para poder acceder a sus métodos y de esta forma poder leer y guardar datos de la BD. Declararemos el objeto, y posteriormente lo inicializaremos en el método onCreate(), y posteriormente llamaremos al método open() del objeto DBAdapter, para que en cuanto se inicie la aplicación se abra la conexión a la base de datos. Por último llamamos al método close() del objeto DBAdapter en el método onTerminate() de nuestra clase Application:

public class BDApplication extends Application {

	DBAdapter dbAdapter;

	@Override
	public void onCreate() {
		dbAdapter = new DBAdapter(this);
		dbAdapter.open();

		super.onCreate();
	}

	@Override
	public void onTerminate() {
		dbAdapter.close();
		super.onTerminate();
	}

}

Nota: Al declarar el objeto de la clase DBAdapter nos aparecerá subrayado el código ya que no tenemos el import que nos permite acceder al objeto. Para solucionar esto de forma rápida pulsamos crtl + shift + O (letra) y así se nos organizarán automáticamente los import, añadiendo los necesarios y eliminando los innecesarios.

También en nuestra clase Application vamos a crear un método que comprobará si la primera tabla ya contiene datos, y en caso contrario crearemos una colección de datos que añadiremos a la tabla. Este método también lo llamaremos desde el método onCreate():

public void rellenarDatosTabla1() {
	if (dbAdapter.tabla1IsEmpty()){
		for(int i = 0; i<30; i++) {
			String nombre = "nombre" + String.valueOf(i);
			String apellido = "apellido" + String.valueOf(i);
			String numero = "555-" + String.valueOf(i);
			dbAdapter.tabla1Insert(nombre, apellido, numero);
		}
	}
}

Como podemos ver en el código, hemos llamado a los métodos tabla1IsEmpty() y tabla1Insert() del objeto DBAdapter, que aún no existen. Estos métodos serán los encargados de comprobar si existen o no datos e insertarlos, por lo tanto tendremos que crearlos dentro de la clase DBAdapter.

public boolean tabla1IsEmpty() {
	return tabla1.isEmpty();
}

public boolean tabla1Insert(String nombre, String apellido, String numero) {
	return tabla1.insert(nombre, apellido, numero);
}

El método insert de la clase Tabla1 sí existe, pero no el método que comprobará si existen o no datos en la tabla, por lo que tendremos que crear ese método en la clase Tabla1:

public boolean isEmpty() {
	return sqlDB.query(NAME, COLUMNS, null, null, null, null, null)
			.getCount() == 0;
}

Si miramos la documentación de Android veremos que el método query() nos devuelve un objeto de la clase Cursor, este objeto contiene el método getCount(), que devuelve el número de tuplas que contiene la consulta. Como lo que hemos recogido en la consulta han sido todos los datos, el getCount() nos dirá cuantos datos contiene la tabla, por lo que la respuesta es si esa cantidad es igual a 0.

En la próxima entrada veremos cómo recoger los datos para mostrarlos en el ListView, y cómo copiar los datos de una tabla a otra, mostrando los datos también en el segundo ListView.

Descargas

Puedes ver el resultado del ejemplo en tu móvil descargando nuestra aplicación desde Google Play.

Para descargar lo que llevamos hecho del proyecto pulsar aquí.

Share if you like...Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn0
The following two tabs change content below.
Reborn as IT Developer. Desarrollador Android y fundador de Proyecto Simio. "En realidad, yo no puedo enseñar nada a nadie, sólo puedo hacerles pensar." - Sócrates.

14 thoughts on “Programación Android, Ejemplo de Bases de datos & ListView I

  1. Buenas noche, me podrias aclarar una duda, la cual es….. en donde se manda los datos de la BASE DE DATOS al listview?

    GRACIAS

    1. Buenas Bryan.

      Este tutorial está dividido en tres partes, si lees todas las entradas podrás seguir viendo cómo mostrar los datos de la base de datos en el listview. Al final de cada parte tiene un enlace para ver la siguiente :)

      Un saludo.

    1. No, imagina que quieres hacer un inner join de varias tablas, ¿cómo pides las columnas de cada tabla?
      Y cuando analices los datos que contiene el cursor, ¿cómo vas a preguntar por las columnas?

      Es necesario que esa clase sea pública :)

  2. Hola …. disculpa, no me queda muy claro el uso de las dos clases DBApplication y DBAdapter … siento que me pudiera ahorrar la clase DBAdapter .. no me queda clara sus usos y no quisiera escribir sólo código sin entender bien el objetivo.

    Gracias

    1. Pues se trata de usar el patrón MVC, Application es la capa del controlador de tu aplicación, mientras que DBAdapter es la capa del modelo. Si juntas las clases DBAdapter y Application estás mezckando capas.

      Un saludo.

  3. Disculpa pero en la clase Tabla1 tienes el CREATE_TABLE como private….
    No deberia ser protected?….
    De otro modo como privado seria imposible acceder al CREATE_TABLE en el metodo onCreate de la clase DBAdapter
    Gracias por el aporte.

    1. Buenas Vivian,
      Pues el motivo es que las variables estáticas son los nombres de las columnas, que pueden ser utilizadas desde muchas clases que ni tan siquiera contengan un objeto de esta clase, por lo tanto necesitamos poder acceder a esas variables sin tener instancias de objetos.

      Sin embargo con el método insert no sucede esto mismo, este método sólo se puede utilizar cuando se tiene una instancia del objeto, ya que necesita estar inicializado y que exista una conexión abierta a la base de datos, para posteriormente poder leer o escribir datos a/de la base de datos.

      Un saludo.

Deja un comentario