Creando la nota con el formulario

Antes de eso vamos a permitir borrar notas, para ello vamos a Notas.java. Añadimos la siguiente línea al final del método onCreate()

       registerForContextMenu(getListView());

Queremos que al hacer click en un elemento de lista nos salga un menú desde donde podamos borrarlo. Añadimos un nuevo método que sobreescribe a uno de Android:

    @Override
	public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
		super.onCreateContextMenu(menu, v, menuInfo);
	    menu.add(0, DELETE_ID, 0, R.string.menu_delete);
	}

Hace falta insertar más variables en Notas.java

    private static final int ACTIVITY_CREATE=0;
    private static final int ACTIVITY_EDIT=1;
    
    private static final int INSERT_ID = Menu.FIRST;
    private static final int DELETE_ID = Menu.FIRST + 1;

y en strings.xml

    <string name="menu_delete">Delete Note</string>
    <string name="title">Title</string>
    <string name="body">Body</string>
    <string name="confirm">Confirm</string>
    <string name="edit_note">Edit Note</string>

Ahora que ya podemos seleccionar la nota a borrar, vamos a crear el método que efectivamente lo borra:

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        switch(item.getItemId()) {
        case DELETE_ID:
            AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
            mDbHelper.deleteNote(info.id);
            fillData();
            return true;
        }
        return super.onContextItemSelected(item);
    }

Volvemos a hacer un switch para comprobar la posición del botón del menú y si es la segunda borramos la nota y recargamos la lista. Conseguimos el ID de la nota gracias a getMenuInfo() y al método registerForContextMenu() que hemos insertado antes al final de onCreate().

Volvamos ahora al método auxiliar para crear nuevas notas, borramos lo que teníamos y vamos a cargar una nueva actividad:

    private void createNote() {
    	Intent i = new Intent(this, NoteEdit.class);
    	startActivityForResult(i, ACTIVITY_CREATE);
    }

Lo que hacemos es crear un nuevo Intent, una nueva actividad, en este caso NoteEdit que todavía no hemos creado. Ejecuta su comienzo y espera el resultado. Con el resultado luego llamará a la función onActivityResult() que implementaremos más tarde. Si no nos interesa saber el resultado basta con llamar a la función startActivity()

Vamos a hacer algo parecido para editar las notas que ya tenemos. Pero antes de nada vamos a permitir utilizar el Cursor en toda la clase, para eso añadimos la variable de clase:

    private Cursor mNotesCursor;

refactorizamos onCreate() que quedaría ahora así:

 	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.notepad_list);
        mDbHelper = new NotasDbAdapter(this);
        mDbHelper.open();
        fillData();
        registerForContextMenu(getListView());
    }

y nuestro método privado fillData()

    private void fillData() {
        // Recupera todas las notas de la DB y las guarda en el cursor
        mNotesCursor = mDbHelper.fetchAllNotes();
        startManagingCursor(mNotesCursor);
        
        // Array con los campos que queremos mostrar en la lista
        String[] from = new String[]{NotasDbAdapter.KEY_TITLE};
        
        // array con las variables asociadas para esos campos
        int[] to = new int[]{R.id.text1};
        
        SimpleCursorAdapter notes = 
        	    new SimpleCursorAdapter(this, R.layout.notes_row, mNotesCursor, from, to);
        setListAdapter(notes);
    }

Ahora ya podemos fijarnos en el método que se ejecute al hacer click sobre un elemento de la lista. Se llama onListItemClick() y lo sobreescribimos:

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);
        Cursor c = mNotesCursor;
        c.moveToPosition(position);
        Intent i = new Intent(this, NoteEdit.class);
        i.putExtra(NotasDbAdapter.KEY_ROWID, id);
        i.putExtra(NotasDbAdapter.KEY_TITLE, c.getString(
                c.getColumnIndexOrThrow(NotasDbAdapter.KEY_TITLE)));
        i.putExtra(NotasDbAdapter.KEY_BODY, c.getString(
                c.getColumnIndexOrThrow(NotasDbAdapter.KEY_BODY)));
        startActivityForResult(i, ACTIVITY_EDIT);   
    }

Lo que estamos haciendo es mover el Cursor a la posición en la que estamos, crear el Intent y pasarle como argumentos el Id, título y texto. Para pasar como argumentos utilizamos la función putExtra(). Otra función muy útil. Por último lo ejecutamos esperando el resultado.

Va siendo hora de implementar el método onActivityResult() que es el que se ejecuta cuando recibimos el resultado de haber creado o editado una nota con startActivityForResult()

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    	super.onActivityResult(requestCode, resultCode, intent);
    	Bundle extras = intent.getExtras();

    	switch(requestCode) {
    	case ACTIVITY_CREATE:
    	    String title = extras.getString(NotasDbAdapter.KEY_TITLE);
    	    String body = extras.getString(NotasDbAdapter.KEY_BODY);
    	    mDbHelper.createNote(title, body);
    	    fillData();
    	    break;
    	case ACTIVITY_EDIT:
    	    Long mRowId = extras.getLong(NotasDbAdapter.KEY_ROWID);
    	    if (mRowId != null) {
    	        String editTitle = extras.getString(NotasDbAdapter.KEY_TITLE);
    	        String editBody = extras.getString(NotasDbAdapter.KEY_BODY);
    	        mDbHelper.updateNote(mRowId, editTitle, editBody);
    	    }
    	    fillData();
    	    break;
    	}
    }

Si estamos creando la nota, la guarda en la base de datos. Si la estamos editando, la actualiza y en ambos casos recarga la lista de notas.

Ya tenemos la lógica más o menos, vamos ahora a crear el layout del formulario. Para eso creamos el archivo /res/layout/note_edit.xml y lo rellenamos con:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical" android:layout_width="fill_parent"
	android:layout_height="fill_parent">
	
	<LinearLayout android:orientation="horizontal"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content">

		<TextView android:layout_width="wrap_content"
			android:layout_height="wrap_content" 
			android:text="@string/title" />
		<EditText android:id="@+id/title" 
		  android:layout_width="wrap_content"
			android:layout_height="wrap_content" 
			android:layout_weight="1"/>
	</LinearLayout>

	<TextView android:layout_width="wrap_content"
		android:layout_height="wrap_content" 
		android:text="@string/body" />
	<EditText android:id="@+id/body" android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:layout_weight="1"
		android:scrollbars="vertical" />
	
	<Button android:id="@+id/confirm" 
	  android:text="@string/confirm"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content" />

</LinearLayout>

Este es un layout ya más complicado. Lo que hace es crear dos campos de texto, uno para el título y otro para el texto; y el botón que guarda lo que hemos escrito. Utilizando una mezcla de configuraciones de alturas conseguimos la apariencia que queremos. En el próximo tutorial entraremos más en detalle en los layouts.

Vamos a crear la clase NoteEdit.java ahora que ya tenemos su layout. Voy a copiar el código final comentado y explico detalles al final:

/*
 * Copyright (C) 2008 Google Inc.
 */

package com.android.demo.notepad1;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class NoteEdit extends Activity {

	private EditText mTitleText;
    private EditText mBodyText;
    private Long mRowId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // Fijamos el layout de esta actividad
        setContentView(R.layout.note_edit);
       
        // Objetos que referencian a los campos editables del layout
        mTitleText = (EditText) findViewById(R.id.title);
        mBodyText = (EditText) findViewById(R.id.body);
        Button confirmButton = (Button) findViewById(R.id.confirm);
       
        // Si hay argumentos, los cogemos 
        mRowId = null;
        Bundle extras = getIntent().getExtras();
        if (extras != null) {
            String title = extras.getString(NotasDbAdapter.KEY_TITLE);
            String body = extras.getString(NotasDbAdapter.KEY_BODY);
            mRowId = extras.getLong(NotasDbAdapter.KEY_ROWID);
            
            // Insertamos los valores actuales de la nota en los campos
            if (title != null) {
                mTitleText.setText(title);
            }
            if (body != null) {
                mBodyText.setText(body);
            }
        }
       
        // Listener para el botón de confirmar
        confirmButton.setOnClickListener(new View.OnClickListener() {

            public void onClick(View view) {
                Bundle bundle = new Bundle();
               
                // Guardamos los nuevos valores en un Bundle
                bundle.putString(NotasDbAdapter.KEY_TITLE, mTitleText.getText().toString());
                bundle.putString(NotasDbAdapter.KEY_BODY, mBodyText.getText().toString());
                if (mRowId != null) {
                    bundle.putLong(NotasDbAdapter.KEY_ROWID, mRowId);
                }
                
                // y los mandamos de vuelta al método que los está esperando
                Intent mIntent = new Intent();
                mIntent.putExtras(bundle);
                setResult(RESULT_OK, mIntent);
                finish();
            }
            
        });
    }
}

Hay que fijarse en cómo cogemos la referencia al campo de texto (mediante la clase R.java por supuesto)

mTitleText = (EditText) findViewById(R.id.title);

En cómo cogemos los argumentos que hemos pasado antes con el método putExtra()

y cómo asignamos los valores actuales

mTitleText.setText(title);

El último paso tras crear una nueva actividad es declararla en el archivo AndroidManifest.xml. Recordarlo bien porque es un típico fallo que nos hace perder muchas horas.

    	<activity android:name=".NoteEdit"></activity>

También se puede introducir la nueva actividad con los paneles de control en vez de directamente sobre el código (Application -> Add Application Node -> Activity y rellenamos el nombre)

Es hora de ejecutar el programa de nuevo para ir viendo cómo evoluciona. Ya podemos editar las notas que creamos antes y crear nuevas con la opción Add Item del menú. Probad a dar hacia atrás cuando estéis en el formulario. Veréis cómo salta una excepción.

Resumen

Hoy hemos tocado muchos temas y muy variados:

  • Hemos creado nuestro primer modelo con su conexión a la base de datos (adaptador de por medio para facilitarnos la vida).
  • Hemos visto más layouts para mostrar listas y formularios.
  • Hemos creado una actividad de lista que os puede servir de modelo para vuestras apps. Permite crear nuevas notas, editarlas y borrarlas. Aunque mejor esperad a la semana que viene para tomarla como modelo ya que haremos algunos cambios importantes.
  • Hemos creado una segunda actividad por lo que ya podemos decir que nuestra app es medianamente compleja, al menos tiene 2 pantallas!

El próximo día arreglaremos los pequeños fallos que nos hemos dejado, mejoraremos el código para hacerlo más óptimo, y aprenderemos muchas más vistas en otros ejemplos, como por ejemplo cómo mostrar tu posición en el mapa. Por hoy creo que es suficiente. Repasad el tutorial, ved la API de Android y si tenéis alguna duda preguntad en Foros del Web o en los comentarios de esta entrada e intentaré responder.