Tutorial CRUD Android + SQLite (X): Spinner con datos de tabla relacionada

Posted by in Programación Android

Siguiendo con el Tutorial Android + SQLite en la entrada anterior vimos cómo filtrar resultados SQLite, en esta veremos cómo incluir un spinner con datos de una tabla relacionada.

Crearemos una nueva tabla SITUACION en nuestra base de datos con las diferentes situaciones en las que puede estar nuestra entidad principal HIPOTECA. Estas situaciones se visualizarán en el formulario con un campo spinner que recojerá los posibles valores almacenados en la tabla SITUACION. En esta entrada profundizaremos en los siguientes aspectos:

  • Crear tabla relacionada
  • Llenar Spinner con datos de SQLite
  • Utilizar Spinner en formulario

 

1: Crear tabla relacionada

Volvemos a realizar una actualización de la base de datos tal y como lo hicimos en la entrada Actualizar la base de datos SQLite. En esta ocasión crearemos una nueva tabla SITUACION y un campo en la tabla HIPOTECA para indicar la situación en la que se encuentra. Para ello definiremos el método upgrade_4 en la clase HipotecaDbHelper.java para crear la nueva tabla, rellenar con los valores por defecto, crear el nuevo campo en la tabla principal y rellenar con el valor inicial.

private void upgrade_4(SQLiteDatabase db)
{
    //
    // Upgrade versión 4: Incluir la clasificación SITUACION para las hipotecas
    //
    db.execSQL( "CREATE TABLE SITUACION(" +
                " _id INTEGER PRIMARY KEY," +
                " sit_nombre TEXT NOT NULL)");

    db.execSQL( "CREATE UNIQUE INDEX sit_nombre ON SITUACION(sit_nombre ASC)" );

    db.execSQL("INSERT INTO SITUACION(_id, sit_nombre) VALUES(1,'Inicial')");
    db.execSQL("INSERT INTO SITUACION(_id, sit_nombre) VALUES(2,'Información')");
    db.execSQL("INSERT INTO SITUACION(_id, sit_nombre) VALUES(3,'Solicitada')");
    db.execSQL("INSERT INTO SITUACION(_id, sit_nombre) VALUES(4,'Negociación')");
    db.execSQL("INSERT INTO SITUACION(_id, sit_nombre) VALUES(5,'Denegada')");
    db.execSQL("INSERT INTO SITUACION(_id, sit_nombre) VALUES(6,'Desestimada')");
    db.execSQL("INSERT INTO SITUACION(_id, sit_nombre) VALUES(7,'Concedida')");
    db.execSQL("INSERT INTO SITUACION(_id, sit_nombre) VALUES(8,'Firmada')");

    db.execSQL("ALTER TABLE HIPOTECA ADD hip_sit_id INTEGER NOT NULL DEFAULT 1");

    Log.i(this.getClass().toString(), "Actualización versión 4 finalizada");
}

 

Además, modificaremos el número de versión y los métodos onCreate y onUpdate para llamar al nuevo método upgrade_4 y crear así la nueva tabla al ejecutar la aplicación.

...
...
private static int version = 4;
...
...
@Override
public void onCreate(SQLiteDatabase db)
{
    ...
    ...

    // Aplicamos las sucesivas actualizaciones
    upgrade_2(db);
    upgrade_3(db);
    upgrade_4(db);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
    ...
    ...
    // Actualización a versión 4
    if (oldVersion < 4)
    {
        upgrade_4(db);
    }
}

...
...

 

2: Crear DbAdapter para la nueva tabla

Creamos adaptador para la nueva tabla que utilizaremos desde el formulario para obtener un cursor con el método getLista.

package com.sodenet.hipotecas;

import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;


public class SituacionDbAdapter {

    //
    // Definimos constante con el nombre de la tabla
    //
    public static final String C_TABLA = "SITUACION" ;

    //
    // Definimos constantes con el nombre de las columnas de la tabla
    //
    public static final String C_COLUMNA_ID	= "_id";
    public static final String C_COLUMNA_NOMBRE = "sit_nombre";

	private Context contexto;
	private HipotecaDbHelper dbHelper;
	private SQLiteDatabase db;

    //
    // Definimos columnas para lista
    //
    private String[] lista = new String[]{C_COLUMNA_ID, C_COLUMNA_NOMBRE} ;

	public SituacionDbAdapter(Context context)
	{
		this.contexto = context;
	}

	public SituacionDbAdapter abrir() throws SQLException
	{
		dbHelper = new HipotecaDbHelper(contexto);
		db = dbHelper.getWritableDatabase();
		return this;
	}

	public void cerrar()
	{
		dbHelper.close();
	}

	
    //
    // Devuelve una lista (_id, nombre) con todos los registros
    //
    public Cursor getLista() throws SQLException
    {
        Cursor c = db.query( true, C_TABLA, lista, null, null, null, null, null, null);

        return c;
    }

}

 

3: Incluir campo situación en la Hipoteca

Incluimos el campo situación en la clase HipotecaDbAdapter.java para guardar y recuperar el valor de SQLite.

...
...
//
// Definimos constantes con el nombre de las columnas de la tabla
//
public static final String C_COLUMNA_ID	= "_id";
public static final String C_COLUMNA_NOMBRE = "hip_nombre";
public static final String C_COLUMNA_CONDICIONES = "hip_condiciones";
public static final String C_COLUMNA_CONTACTO = "hip_contacto";
public static final String C_COLUMNA_EMAIL = "hip_email";
public static final String C_COLUMNA_TELEFONO = "hip_telefono";
public static final String C_COLUMNA_OBSERVACIONES = "hip_observaciones";
public static final String C_COLUMNA_PASIVO = "hip_pasivo_sn";
public static final String C_COLUMNA_SITUACION = "hip_sit_id";
...
...
//
// Definimos lista de columnas de la tabla para utilizarla en las consultas a la base de datos
//
private String[] columnas = new String[]{
        C_COLUMNA_ID,
        C_COLUMNA_NOMBRE,
        C_COLUMNA_CONDICIONES,
        C_COLUMNA_CONTACTO,
        C_COLUMNA_EMAIL,
        C_COLUMNA_TELEFONO,
        C_COLUMNA_OBSERVACIONES,
        C_COLUMNA_PASIVO,
        C_COLUMNA_SITUACION} ;
...
...

 

Incluimos el Spinner situación en el layout del formulario /res/layout/activity_hipoteca_formulario.xml.

...
...
<TextView
        android:id="@+id/label_situacion"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/label_situacion"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:layout_below="@+id/nombre"
        android:layout_marginTop="20dp" />

<Spinner
        android:id="@+id/situacion"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/label_situacion"
        android:ems="10"/>

<TextView
        android:id="@+id/label_condiciones"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/label_condiciones"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:layout_below="@+id/situacion"
        android:layout_marginTop="20dp" />
...
...

 

Incluimos la etiqueta situación al recurso de textos /res/values/strings.xml.

 <string name="label_situacion">Situación</string>

 

4: Rellenar Spinner, recuperar y guardar datos

Una vez incluido el campo situación en el layout del formulario y en el DbAdapter, modificaremos la clase HipotecaFormulario.java para declarar y rellenar spinner con los datos de la tabla SITUACION en el método onCreate.

public class HipotecaFormulario extends Activity {
    ...
    ...
    private SituacionDbAdapter dbAdapterSituacion ;
    private Cursor cursorListaSituacion ;
    ...
    ...	
    //
    // Elementos de la vista
    //
    private EditText nombre;
    private EditText condiciones;
    private EditText contacto;
    private EditText telefono;
    private EditText email;
    private EditText observaciones;
    private CheckBox pasivo ;
    private Spinner situacion ;
	
    private Button boton_guardar;
    private Button boton_cancelar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_hipoteca_formulario);
	
	Intent intent = getIntent();
	Bundle extra = intent.getExtras();

	if (extra == null) return;
	
	//
	// Obtenemos los elementos de la vista
	//
	nombre = (EditText) findViewById(R.id.nombre);
	condiciones = (EditText) findViewById(R.id.condiciones);
	contacto = (EditText) findViewById(R.id.contacto);
	telefono = (EditText) findViewById(R.id.telefono);
	email = (EditText) findViewById(R.id.email);
	observaciones = (EditText) findViewById(R.id.observaciones);
	pasivo = (CheckBox) findViewById(R.id.pasivo);
	situacion = (Spinner) findViewById(R.id.situacion);

	boton_guardar = (Button) findViewById(R.id.boton_guardar);
	boton_cancelar = (Button) findViewById(R.id.boton_cancelar);
	
	//
	// Creamos el adaptador de Situacion
	//
	dbAdapterSituacion = new SituacionDbAdapter(this) ;
	dbAdapterSituacion.abrir();

	cursorListaSituacion = dbAdapterSituacion.getLista();


	SimpleCursorAdapter adapterSituacion = new SimpleCursorAdapter(this,android.R.layout.simple_spinner_item,cursorListaSituacion,new String[] {SituacionDbAdapter.C_COLUMNA_NOMBRE}, new int[] {android.R.id.text1});

	adapterSituacion.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

	situacion.setAdapter(adapterSituacion);

        ...
        ...

 

Modificamos los métodos setEdicion para condicionar la modificación de la situación igual que el resto de campos, consultar para activar el elemento del spinner correspondiente al valor recuperado de la tabla apoyándonos en el nuevo método getItemPositionById que devolverá la posición en el spinner del identificador de situación que le pasemos y guardar que incluye el identificador seleccionado en el registro para guardar en la base de datos.

...
...
private void consultar(long id)
{
    //
    // Consultamos el centro por el identificador
    //
    cursor = dbAdapter.getRegistro(id);

    nombre.setText(cursor.getString(cursor.getColumnIndex(HipotecaDbAdapter.C_COLUMNA_NOMBRE)));
    condiciones.setText(cursor.getString(cursor.getColumnIndex(HipotecaDbAdapter.C_COLUMNA_CONDICIONES)));
    contacto.setText(cursor.getString(cursor.getColumnIndex(HipotecaDbAdapter.C_COLUMNA_CONTACTO)));
    telefono.setText(cursor.getString(cursor.getColumnIndex(HipotecaDbAdapter.C_COLUMNA_TELEFONO)));
    email.setText(cursor.getString(cursor.getColumnIndex(HipotecaDbAdapter.C_COLUMNA_EMAIL)));
    observaciones.setText(cursor.getString(cursor.getColumnIndex(HipotecaDbAdapter.C_COLUMNA_OBSERVACIONES)));
    pasivo.setChecked(cursor.getString(cursor.getColumnIndex(HipotecaDbAdapter.C_COLUMNA_PASIVO)).equals("S"));
    situacion.setSelection(getItemPositionById(cursorListaSituacion, cursor.getLong(cursor.getColumnIndex(HipotecaDbAdapter.C_COLUMNA_SITUACION))));

}

private void setEdicion(boolean opcion)
{
    nombre.setEnabled(opcion);
    condiciones.setEnabled(opcion);
    contacto.setEnabled(opcion);
    telefono.setEnabled(opcion);
    email.setEnabled(opcion);
    observaciones.setEnabled(opcion);
    pasivo.setEnabled(opcion);
    situacion.setEnabled(opcion);

    // Controlamos visibilidad de botonera
    LinearLayout v = (LinearLayout) findViewById(R.id.botonera);

    if (opcion)
        v.setVisibility(View.VISIBLE);

    else
        v.setVisibility(View.GONE);
}
	
private void guardar()
{
    //
    // Obtenemos los datos del formulario
    //
    ContentValues reg = new ContentValues();

    //
    // Si estamos en modo edición añadimos el identificador del registro que se utilizará en el update
    //
    if (modo == Hipoteca.C_EDITAR)
        reg.put(HipotecaDbAdapter.C_COLUMNA_ID, id);

    reg.put(HipotecaDbAdapter.C_COLUMNA_NOMBRE, nombre.getText().toString());
    reg.put(HipotecaDbAdapter.C_COLUMNA_CONDICIONES, condiciones.getText().toString());
    reg.put(HipotecaDbAdapter.C_COLUMNA_CONTACTO, contacto.getText().toString());
    reg.put(HipotecaDbAdapter.C_COLUMNA_TELEFONO, telefono.getText().toString());
    reg.put(HipotecaDbAdapter.C_COLUMNA_EMAIL, email.getText().toString());
    reg.put(HipotecaDbAdapter.C_COLUMNA_OBSERVACIONES, observaciones.getText().toString());
    reg.put(HipotecaDbAdapter.C_COLUMNA_PASIVO, (pasivo.isChecked())?"S":"N");
    reg.put(HipotecaDbAdapter.C_COLUMNA_SITUACION, situacion.getSelectedItemId());

    if (modo == Hipoteca.C_CREAR)
    {
        dbAdapter.insert(reg);
        Toast.makeText(HipotecaFormulario.this, R.string.hipoteca_crear_confirmacion, Toast.LENGTH_SHORT).show();
    }
    else if (modo == Hipoteca.C_EDITAR)
    {
        Toast.makeText(HipotecaFormulario.this, R.string.hipoteca_editar_confirmacion, Toast.LENGTH_SHORT).show();
        dbAdapter.update(reg);
    }

    //
    // Devolvemos el control
    //
    setResult(RESULT_OK);
    finish();
}
...
...
private int getItemPositionById(Cursor c, long id)
{
    for(c.moveToFirst(); !c.isAfterLast(); c.moveToNext())
    {
        if (c.getLong(c.getColumnIndex(SituacionDbAdapter.C_COLUMNA_ID)) == id)
        {
            return c.getPosition() ;
        }
    }

    return 0 ;
}
...
...

 

5: Ejecución de la aplicación

¡Ya está todo listo! Ahora basta con ejecutar la aplicación en el emulador.

android_sqlite_spinner_1


 
Si tienes alguna pregunta o algo que aportar no lo dudes … ¡tienes los comentarios a tu disposición!