sábado, 1 de diciembre de 2007

Manos a la obra - Tutorial - Ejercicio #1

Tutorial: Ejercicio #1

[Título original: Tutorial: Notepad Exercise 1]

Documento original: Tutorial: Notepad Exercise 1

En este ejercicio construiremos una simple lista de anotaciones que permitirá al usuario agregar una nueva nota, pero por ahora no le permitirá editarla. Este ejercicio demostrará:

  • Conceptos básicos de una "ListActivity". Además, la creación y manipulación de opciones de un menú.

  • Recuperación y almacenamiento de las notas en una base de datos "SQLite".

  • Cómo enlazar datos dentro de una "ListView" usando un "ArrayAdapter" (una de las formás más simples de enlazar datos a un "ListView").

  • Los conceptos básicos de diseño de una pantalla, incluyendo cómo diseñar una lista, cómo agregar elementos al menú de una "Activity" y cómo manipular la selección de esos elementos.

  • Paso #1

    [Título original: Step 1]

    Importar el proyecto "Notepadv1" a nuestro "workspace" de Eclipse.

    NOTA: "Notepadv1" es un proyecto que provee el punto de partida a este ejercicio y nos ahorrará el tiempo de hacer las cosas que ya aprendiste al desarrollar la aplicación "Hola Mundo".

    A continuación están las instrucciones para esta sección:

  • Dentro del "Package Explorer" haz clic con el botón derecho y selecciona "Import... > General > Existing Projects into Workspace. Luego presiona el botón "Next > ".

  • A continuación, presiona el botón "Browse" y navega hasta donde extrajiste los directorios de los ejercicios y selecciona "Notepadv1". Finalmente, presiona el botón "OK".

  • Después del paso anterior deberías ver el elemento "Notepadv1" en la lista de proyectos con una marca de seleccionado al lado de él.

  • Presiona el botón "Finish".

  • El proyecto debería haberse abierto en el "Package Explorer" y estar listo para trabajar con él.

  • Si vieras un error relacionado con el archivo "AndroidManifest.xml", o con un archivo ".zip" de Android, has clic con el botón derecho sobre el proyecto y selecciona desde el menú de contexto "Android Tools > Fix Project Properties".

  • Paso #2

    [Título original: Step 2]

    Dale un vistazo a la clase "DBHelper". Esta clase encapsula el acceso y actualización de los datos de nuestras anotaciones almacenadas en una base de datos "SQLite" local en nuestro dispositivo.

    Normalmente, el acceso a datos debería ser implementado usando un "ContentProvider", y de hecho la aplicación "Notepad" que está incluida en el SDK (en el directorio "samples/") implamenta un "ContentProvider". Sin embargo, no hay ninguna restricción en utilizar directamente tu propia base de datos tal como lo haremos en este ejercicio. Lo más importante a destacar de la clase "DBHelper" es que toma la responsabilidad de todos los detalles relacionados con el almacenamiento, recuperación y actualización de datos de una base de datos "SQLite". Provee métodos para: (A) recuperar todas los registros, (B) recuperar un registro basado en un "rowIds", (C) crear un nuevo registro, (D) borrar un registro (E) actualizar un registro.

    Accesando y modificando datos

    [Título original: Accessing and modifying data]

    En este ejercicio, vamos a usar directamente una base de datos SQLite para almacenar nuestros datos, pero en una aplicación real sería mucho más conveniente escribir un "ContentProvider" para encapsular este comportamiento.

    Si tú estás interesado en aprender más sobre "ContentProvider" o el tema "Almacenamiento, recuperación y exposición de datos".

    Paso #3

    [Título original: Step 3]

    Abre el archivo "notepad_list.xml" que está localizado en "res/layout" y dale un vistazo.

    Este archivo contiene la definición del diseño de la pantalla y contiene el punto de inicio para dibujar la pantalla.

  • Todos los archivos de diseño de pantalla comienzan con un encabezado XML.

  • Con frecuencia, pero no siempre, la raíz del documento de diseño es una definición del tipo de distribución que tendrán los elementos en la pantalla, en este caso es"LinearLayout".

  • El "xml namespace" de Android (xmlns:android="http://schemas.android.com/apk/res/android") debería ser siempre definido como un atributo del elemento de mayor jerarquía en el documento de diseño de pantalla, de esa forma el espacio de nombre "android:" podrá ser utilizado por los elementos que estén anidados.

  • Esquemas y actividades

    [Título original: Layouts and activities]

    Muchas actividades tendrán un esquema asociado a ellas. El esquema será la "cara" que el usuario verá de la actividad. En este ejemplo, el esquema ocupará completamente la pantalla y proveerá una lista de anotaciones.

    Los esquema de pantalla completa no son la única opción para una actividad. Tú también podrías usar un esquema flotante (por ejemplo para un mensaje de alerta) o quizá ni siquiera necesites un esquema (en este caso la actividad es invisible al usuario a menos que quieras asociar un esquema a la actividad).

    Paso #4

    [Título original: Step 5]

    Ahora, necesitamos modificar el diseño de la pantalla para que contenga nuestra lista. Para hacer esto agregaremos dentro de la etiqueta "LinearLayout" las etiquetas "ListView" y "TextView".

    El siguiente archivo corresponde a la nueva versión después de realizar las modificaciones:

    <?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 id="@+id/android:list"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

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

    </LinearLayout>

  • Los elementos "ListView" y "TextView" pueden ser entendidos como dos vistas alternativas, es decir, en cualquier momento sólo una de ellas será desplegada. El elemento "ListView" será usado sólo cuando tengamos anotaciones para ser mostradas y "TextView" será usado para desplegar el mensaje "No Notes Yet!" cuando aún no tengamos anotaciones para mostrar. (El valor inicial de "TextView" está definido como un recurso de tipo "string").

  • El caracter "@" en los atributos "id" de "ListView" y "TextView" le indican al "XML parser" que debe reemplazar estos valores por valores definidos en el archivo de recursos o en la "Android platform".

  • Los textos "android:list" y "android:empty" son identificadores que son proporcionados por la "Android platform". El elemento identificado con "empty" será usado automáticamente cuando no hayan datos para ser desplegados en la lista.

  • En general, la clase "android.R" es un conjunto predefinido de recursos que la plataforma te provee, por el contrario, la clase "R" de tu proyecto es el conjunto de recursos que tu proyecto a definido. Los recursos que se encuentran definidos en la clase "android.R" pueden ser usados en los archivos XML anteponiendo el espacio de nombre "android:", como se ve en el ejemplo anterior.

    Paso #5

    [Título original: Step 5]

    Para crear una vista con una lista, también debemos definir la vista para cada una de las filas:

  • Crearemos un nuevo archivo dentro de "res/layout" llamado "notes_row.xml".

  • Agregarenos el siguiente contenido al archivo:

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

  • Esta es la vista que será usada para el título de cada anotación. Es sólo un texto.

  • En este caso, hemos creado un nuevo identificador llamado "text1". El caracter "+" que está a continuación del caracter "@" indica que el identificador deberá ser creado automáticamente si es que no existe, es decir, estamos definiendo "text1" dinámicamente y luego empleándolo.

  • Después de grabar el archivo, si abrimos el archivo "R.java" deberemos ver una nueva definición para "notes_row" y "text1", lo que significa que ahora tendremos acceso a esos elementos desde nuestro código.

  • Recursos y la clase "R"

    [Título original: Resources and the R class]

    En los proyectos Eclipse, los subdirectorios de "res/" son especiales. Hay un estructura específica para los directorios y archivos bajo este directorio.

    En particular, los recursos definidos en estos directorios y archivos tendrán sus correspondientes entradas en la clase "R" lo que permitirá que sea muy fácil referenciarlos y usarlos desde tus aplicaciones. Adicionalmente, estos archivos serán empaquetados y distribuidos como parte de tu aplicación.

    Paso #6

    [Título original: Step 6]

    A continuación, abre la clase "Notepadv1"; ya que la vamos a modificar para convertirla en el adaptador de nuestra lista y así desplegar las anotaciones y agregar nuevas.

    La clase "Notepadv1" es una subclase de "Activity" llamada "ListActivity", la cual tiene un funcionalidad adicional que es proporcionar todo tipo de operaciones que comúnmente desearíamos realizar con una lista. Por ejemplo, desplegar un número arbitrario de filas en la lista y permitir seleccionarlas.

    Dale un vistazo al código de la clase "Notepadv1". Hay algunas definiciones de constantes, seguidas de propiedades privadas y finalmente algunos métodos que sobreescriben métodos de la super clase.

    Paso #7

    [Título original: Step 7]

    Cambia la herencia de "Notepadv1" de "Activity" a "ListActivity":

    public class Notepadv1 extends ListActivity

    NOTA: Debes importar la clase "ListActivity" en el archivo "Notepadv1". En Eclipse puedes precionar ctrl-shift-O (Windows y Linux).

    Paso #8

    [Título original: Step 8]

    Debemos agregar código a los siguientes métodos: "onCreate", "onCreateOptionsMenu" y "onOptionsItemSelected".

  • onCreate() es llamado cuando la "Activity" es activada. Es una especie de método "main" en una aplicación "C". Este método es utilizado para definir recursos y estados de la "Activity" que se está ejecutándo.

  • onCreateOptionsMenu() es usado para poblar el menú de la "Activity". Este menú es mostrado cuando el usuario presiona el botón de menú.

  • onOptionsItemSelected() es usado para controlar los eventos generados por el menú. Por ejemplo, cuando el usuario selecciona un elemento del menú.

  • Paso #9

    [Título original: Step 9]

    A continuación modificaremos el método "onCreate()".

    Aquí definiremos el título de la actividad (el título es mostrado en el borde superior de la pantalla). Usaremos el diseño indicado en "notepad_list.xml" para desplegar los contenidos de la actividad. Además, definiremos una instancia de "DBHelper" para tener acceso de los datos de las anotaciones.

  • Llamamos al método "super.onCreate()" con el parámetro "icicle" que entrega nuestro método.

  • Llamamos al método "setContentView()" para definir "R.layout.notepad_list" como el esquema que usaremos.

  • Creamos una propiedad privada llamada "dbHelper", la cual es una instancia de la clase "DBHelper" (esto lo hacemos antes del código del método "onCreate()").

  • En el método "onCreate()", instanciamos la propiedad "dbHelper" (debemos pasar el valor "this" como argumentos del constructor de la clase "DBHelper").

  • Finalmente, llamamos al método "fillData()" (será definido más adelante) para llenar nuestra lista con datos.

  • Él método "onCreate()" debería verse así:

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle icicle)
    {
    super.onCreate(icicle);
    setContentView(R.layout.notepad_list);
    dbHelper = new DBHelper(this);
    fillData();
    }
    Recuerda agregar la definición de la propiedad (justo debajo de la definición de "private int noteNumber = 1;"):
    private DBHelper dbHelper;

  • Paso #10

    [Título original: Step 10]

    A continuación modificaremos el método "onCreateOptionsMenu()".

    Por ahora sólo agregaremos una opción al menú de la aplicación: "Add Item". El texto para esta opción es parte de los recursos de la aplicación y está definido en "strings.xml".

  • En el archivo "strings.xml" (localizado en "res/values"), agrega una nueva etiqueta para "menu_insert" con el texto "Add Item".

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

  • Necesitamos agregar una constante para la posición del menú: public static final int INSERT_ID = Menu.FIRST;

  • En el método "onCreateOptionsMenu()" agregar el ítem del menú. También nos hacemos cargo del resultado retornado por el llamado del método "super.onCreateOptionsMenu(menu)".

  • La nueva versión del método debe verse de la siguiente forma:

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

    Más información sobre menús

    [Título original: More on menus]

    La aplicación "Notepad" que estamos construyendo sólo muestra la superficie de los menús.

    Podemos usar teclas para acelarar el uso de menús, crear submenús e incluso agregrar items de menús a otras aplicaciones.

    Paso #11

    [Título original: Step 11]

    Este método va a responder cuando se seleccione en el menú la opción "Add Note". Cuando la opción sea seleccionada, este método será llamado con el valor de "item.getId()" en "INSERT_ID" (la constante que nosotros definimos para identificar este elemento del menú). Realizaremos las siguientes acciones:

  • El llamado al método "super.onOptionsItemSelected(item)" va al final del método porque primero nos interesa capturar los eventos.

  • Codificamos una sentencia "switch" para evaluar "item.getId()".

  • El "case INSERT_ID:" llama al método "createNote()".

  • Retornamos el valor devuelto por "super.onOptionsItemSelected(item)".

  • La nueva versión del método debe verse de la siguiente forma:

    @Override
    public boolean onOptionsItemSelected(Item item)
    {
    switch (item.getId()) {
    case INSERT_ID:
    createNote();
    fillData();
    break;
    }

    return super.onOptionsItemSelected(item);
    }

    Paso #12

    [Título original: Step 12]

    Creación del método "createNote()".

    En esta primera versión de "createNote()" simplemente crearemos una nueva nota con un título basado en un contador: "Note 1", "Note 2"... y tendrá un cuerpo en blanco. Algunas observaciones sobre el código:

  • La sentencia: String noteName = "Note " + noteNumber++; es usada para construir el título de la nota.

  • El llamado a "dbHelper.createRow()" creará la nota usando "noteName" como título y la el texto vacío ("") como cuerpo.

  • La nueva versión del método debe verse de la siguiente forma:

    private void createNote() 
    {
    String noteName = "Note " + noteNumber++;
    dbHelper.createRow(noteName, "");
    fillData();
    }

    Paso #13

    [Título original: Step 13]

    Creación del método "fillData()".

    Este método usa un "ArrayAdapter", el cual es la forma más fácil de poner datos en un "ListView". Un "ArrayAdapter" toma una "List" o un arreglo de "String" y los enlaza a una vista de texto provista en el diseño de una fila de la lista (esto es el campo "text1" de nuestro archivo "notes_row.xml"). Este método simplemente obtiene una lista de anotaciones desde el "database helper" y construye una "List" de "String" usando el título de cada registro y entonces crea un "ArrayAdapter" con todos esos ítems y los asocia con el diseño "notes_row".

    Algunas observaciones sobre el ccódigo:

  • "ArrayAdapter" necesita un "List" conteniendo los ítems a desplegar.

  • Los datos son recuperados como registros, y el título del registro es utilizado para poblar la lista.

  • Nosotros indicamos que "notes_row" es la vista que usaremos como receptora de los datos.

  • Para importar automáticamente las clases utilizadas presiona ctrl-shift-O.

  • La nueva versión del método debe verse de la siguiente forma:

    private void fillData() 
    {
    // We need a list of strings for the list items
    List<String> items = new ArrayList<String>();

    // Get all of the rows from the database and create the item list
    List<Row> rows = dbHelper.fetchAllRows();
    for (Row row : rows)
    {
    items.add(row.title);
    }

    // Now create an array adapter and set it to display using our row
    ArrayAdapter<String> notes = new ArrayAdapter<String>(this, R.layout.notes_row, items);
    setListAdapter(notes);

    return;
    }

    NOTA: En este ejercicio hemos utilizado "ArrayAdapter", pero este objeto no es una solución escalable. Generalmente, "SimpleCursorAdapter" podría ser empleado en combinación con "ContentProvider" o al menos un "Cursor" retornado desde el "query".

    Adaptadores de listas

    [Título original: List adapters]

    Nuestro ejemplo utiliza un adaptador de colección muy simple, el cual asocia una colección o lista de items con una "ListView". En Android es más común que los adaptadores de listas vayan mano a mano con "ContentProviders" y esta es una forma muy fácil de usar listas.

    Para asociar un "ContentProvider" con una "ListView" podemos usar un "SimpleCursorAdapter" que asociará los datos desde un "ContentProvider" con una "ListView".

    Paso #14

    [Título original: Step 14]

    Ejecución de la aplicación.

  • Paso #14

  • http://2.bp.blogspot.com/__WZr8mbWg3o/R17iAnUfWNI/AAAAAAAAAB0/qKkKGa88g1Q/s320/figura-07.jpg

    Solución y próximos pasos

    [Título original: Solution and Next Steps]

    Puedes ver la solución a este clase en "Notepadv1Solution" para comparar su contenido con el tuyo.

    Una vez que estés listo, continúa con el documento Tutorial: Ejercicio #2, en el cual agregaremos la capacidad de crear, editar y borrar anotaciones.

    1 comentario:

    Anónimo dijo...

    Cuando ejecuto el programa libre de errores y tras comprobar que coincide con la solución me aparece siempre el mensaje No notes yet. A que puede ser debido¿???