Debido a que son más archivos y más líneas de código que en el primer ejercicio os he comprimido los archivos iniciales y finales en un archivo zip (Descargar). Asi siempre tenéis código dónde comparar por si no os funciona algo. Pero como siempre, es mejor que vayáis siguiendo el tutorial y veáis la evolución del código en vez de mirar directamente la solución.

Conexión a la base de datos

Una vez que habéis creado el proyecto Android seleccionando un proyecto existente (Notas), podéis ver la estructura de archivos. Hay ya algunos archivos creados pero el único que he dejado con información es /src/com.android.demo.notepad1/NotasDbAdapter.java. Este archivo se va a encargar de conectarse a la base de datos para que no tengamos que estar lidiando con esto en otras partes del código. Recordad que estamos en un esquema MVC. Este va a ser nuestro primer modelo. Miremos el código en general:

public class NotasDbAdapter {

    // variables varias
	...

    private static class DatabaseHelper extends SQLiteOpenHelper {

        DatabaseHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {...}

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

    /**
     * Constructor - pasa el contexto para poder abrir o crear la DB
     */
    public NotasDbAdapter(Context ctx) {...}

    /**
     * Abre la base de datos de notas. Si no puede abrirla, la crea. Si no se puede
     * lanza una excepcion
     */
    public NotasDbAdapter open() throws SQLException {...}

    public void close() {...}

    /**
     * Inserta una nueva nota con el t’tulo y el texto dados. Si se crea correctamente
     * devuelve el rowId, en caso contrario, devuelve -1 para indicar que ha habido
     * un error..
     */
    public long createNote(String title, String body) {...}

    /**
     * Borra la nota con el rowId dado
     */
    public boolean deleteNote(long rowId) {...}

    /**
     * Devuelve un Cursor apuntando a la lista de todas las notas
     */
    public Cursor fetchAllNotes() {...}

    /**
     * Devuelve un Cursor apuntando a la nota con el rowId dado
     */
    public Cursor fetchNote(long rowId) throws SQLException {...}

    /**
     * Actualiza la nota con los detalles dados.
     */
    public boolean updateNote(long rowId, String title, String body) {...}
}

Tenemos métodos para crear, actualizar, abrir y cerrar la base de datos. Y luego los métodos CRUD para crear una nueva nota, devolver su información (o la de varias notas), actualizarla y borrarla. Esta clase es muy importante ya que estará en todas vuestras apps de una forma u otra asi que miremos método a método.

    private static class DatabaseHelper extends SQLiteOpenHelper {

        DatabaseHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL(DATABASE_CREATE);
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
                    + newVersion + ", which will destroy all old data");
            // sentencia drop
            onCreate(db);
        }
    }

Esta clase auxiliar nos permitirá abrir la base de datos, crearla si no existe y actualizarla al lanzar una nueva versión. La sentencia SQL para crear es:

// sentencia create

 private static final String DATABASE_CREATE = "create table notes (_id integer primary key autoincrement," + "title text not null, body text not null);";

Cómo podéis haber observado ya antes, estamos utilizando la base de datos SQLite, es parecida a MySQL pero tiene pequeños detalles diferentes. Con esto ya tenemos creada la base de datos. Veamos el método para insertar notas:

   public long createNote(String title, String body) {
        ContentValues initialValues = new ContentValues();
        initialValues.put(KEY_TITLE, title);
        initialValues.put(KEY_BODY, body);

        return mDb.insert(DATABASE_TABLE, null, initialValues);
    }

A través de la clase ContentValues guardamos la información con el método put y la insertamos. Para actualizar es casi igual pero usamos el método update en vez de insert. Hay que acordarse de indicar el rowId:

public boolean updateNote(long rowId, String title, String body) {
        ContentValues args = new ContentValues();
        args.put(KEY_TITLE, title);
        args.put(KEY_BODY, body);

        return mDb.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowId, null) > 0;
    }

El método borrar es muy obvio asi que pasamos a recuperar una nota de la base de datos:

    public Cursor fetchNote(long rowId) throws SQLException {

        Cursor mCursor =

                mDb.query(true, DATABASE_TABLE, new String[] {KEY_ROWID,
                        KEY_TITLE, KEY_BODY}, KEY_ROWID + "=" + rowId, null,
                        null, null, null, null);
        if (mCursor != null) {
            mCursor.moveToFirst();
        }
        return mCursor;

    }

Hacemos una query indicando que queremos un array de tres strings con los tres campos que queremos, y con la condición de rowId. El resto de los parámetros se usan si queremos hacer groupBy, having u orderBy. Después comprobamos que no está vacío y si es así movemos el cursor a la primera posición que apunta a la nota que queremos.

Insertando notas

Vamos a modificar el layout de la actividad inicial /res/layout/notepad_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="wrap_content"
    android:layout_height="wrap_content">

  <ListView android:id="@android:id/list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
  <TextView android:id="@android:id/empty"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/no_notes"/>

</LinearLayout>

Tenemos dos elementos: una lista y un campo de texto. El TextView sólo se muestra si la lista está vacía. Android sabe esto gracias a empty, que está unido al valor de list. A través de android:id="@android:id/list" indicamos que queremos que esa lista se rellene con los valores de la variable list de la actividad.

Para mostrar la información que aparece en cada fila de la lista necesitamos crear un nuevo template. Creamos un nuevo archivo /src/layout/notes_row.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView android:id="@+id/text1"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

En el campo de texto (TextView) vamos a mostrar solo el contenido de la variable text1 de la actividad. Antes de ir a la actividad mirad la clase R.java que os dije en el anterior tutorial que Android la manejaba automáticamente. Podéis ver cómo ha añadido el archivo nuevo y la variable text1.

Vayamos ahora con Notas.java, miremos su estructura:

public class Notas extends Activity {
    
    public void onCreate(Bundle savedInstanceState) {...}

    public boolean onCreateOptionsMenu(Menu menu) {...}

    public boolean onOptionsItemSelected(MenuItem item) {...}
}

onCreate() es el método principal de la actividad donde ejecutaremos todo nuestro código. onCreateOptionsMenu() creará el menú de opciones y onOptionsItemSelected() estará atento a qué botón del menú presionamos para ejecutar la acción correspondiente. Repito que es sólo sobre acciones sobre el menú, las acciones de otros botones o enlaces van en otros métodos, exactamente con Listeners, pero eso ya lo veremos más adelante.

Lo primero que vamos a hacer es convertir a Notas en una actividad de lista, para ellos hacemos que extienda a ListActivity:

public class Notas extends ListActivity

Una combinación de teclas muy útil para arreglar los imports de los paquetes es ctrl-shift-O en Windows o Linux, o cmd-shift-O en el Mac.

Queremos que en la pantalla principal de Notas muestre la lista de notas, para ello tiene que conectarse a la base de datos, coger la información y mostrarla en el layout que hemos hecho antes:

	private NotasDbAdapter mDbHelper;
    
	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.notepad_list);
        // new notas
        mDbHelper.open();
        fillData();
    }

Declaramos el objeto fuera para que esté disponible a todos los métodos, fijamos que el layout sea notepad_list (fijaros como referenciamos aquí al layout, no usamos urls o directorios, si no referencia mediante la clase R.java). Vamos a coger la información en un método privado para que quede todo más ordenado:

 private void fillData() { 
	// Recuperamos todas las notas de la DB y creamos una lista Cursor 
	c = mDbHelper.fetchAllNotes(); 
	startManagingCursor(c); 
	
	String[] from = new String[] { NotasDbAdapter.KEY_TITLE }; 
	int[] to = new int[] { R.id.text1 }; 
	
	/ Y creamos un adaptador array para que vaya mostrando las filas 
	SimpleCursorAdapter notes = 
		new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to); 
	setListAdapter(notes); 
}

Ya empezamos a ver más métodos internos de Android, os animo a entrar a la API de Android e ir viendo lo que hacen exactamente, es la mejor forma de aprender. En fillData() recuperamos todas las notas de la DB con el método fetchAllNotes que hicimos al principio, y luego creamos un adaptador que relaciona el campo del objeto que queremos (KEY_TITLE) con la variable text1 (otra vez referenciada por R.java) e indica que queremos que use el layout notes_row.xml que hemos creado antes. Por último avisa a ListActivity que notes va a ser la lista que queremos mostrar.

Con estas modificaciones ya podríamos ver las notas pero no tenemos ninguna asi que vamos a crear la actividad en un momento para que podamos probarlo más a fondo. En Notas.java añadamos el botón de añadir nota. Basta con llamar a la clase superior para crear el menu y añadir el botón de Add Item en la primera posición.

	public static final int INSERT_ID = Menu.FIRST;
	
	@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        boolean result = super.onCreateOptionsMenu(menu);
        menu.add(0, INSERT_ID, 0, R.string.menu_insert);
        return result;
    }

Y añadimos otra variable de texto a strings.xml

	<string name="menu_insert">Add Item</string>

Perfecto, ahora hagamos que al hacer click en este nuevo botón ejecute la nueva actividad para mostrar el formulario. Para eso modificamos el método onOptionsItemSelected() también de Notas.java

	@Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case INSERT_ID:
            createNote();
            return true;
        }
       
        return super.onOptionsItemSelected(item);
    }

Capturamos el Id de la opción del menú y si es la primera ejecutamos el método createNote(). En un primer paso vamos a hacer que inserte una nota con un título y texto fijos. En el siguiente paso ya crearemos el formulario:

    private void createNote() {
        String noteName = "Note " + mNoteNumber++;
        mDbHelper.createNote(noteName, "");
        fillData();
    }

La razón de llamar a fillData() de nuevo es para que recargue la lista una vez que se ha terminado de ejecutar el evento de hacer click en la opción del menú Add Item.

Es hora de ver en acción lo que estamos viendo. Comprueba que no hay errores en ningún archivo, arregla los imports y ejecuta como un proyecto de Android. Si das a menu podrás añadir una nota e irás viendo como se crea una nota nueva cada vez que le das. Enhorabuena, hemos conseguido comunicarnos correctamente con la base de datos.