lunes, 26 de julio de 2010

TUTORIAL: Desarrollo de aplicaciones para Android (XV)


Seguimos con el tutorial de desarrollo de aplicaciones para Android. Hoy veremos el segundo ejercicio del bloc de notas.

Ejercicio 2


En este segundo ejercicio vamos a añadir una nueva actividad a nuestra aplicación que permita al usuario crear y editar notas, así como eliminarlas a través de un menú contextual.
Aprenderemos a:

- Crear una actividad y añadirla al fichero AndroidManifest.xml
- Invocar otra actividad asíncronamente mediante el método startActivityForResult()
- Pasar datos entre actividades en objetos Bundle
- Utilizar layouts más avanzados
- Crear un menú contextual


Paso 1

Crea un proyecto Android a partir de los archivos fuente de la carpeta Notepadv2 que nos bajamos, como hicimos en el anterior ejercicio.
Echa una ojeada al proyecto.


Paso 2

Primero crearemos el menú contextual que permitirá al usuario eliminar notas.
Abre la clase Notepadv2.

1. Para que cada elemento de la lista del ListView aparezca en el menú contextual llamamos a registerForContextMenu() y le pasamos nuestro ListView. Por tanto, al final del método onCreate() añade la siguiente línea:
registerForContextMenu(getListView());
Como que nuestra Activity hereda de ListActivity, getListView() devolverá el objeto ListView local de la actividad. Añadiendo esta línea cada elemento de la lista de este ListView activará el menú contextual.

2. Ahora rellena el método onCreateContextMenu(). Añade la siguiente línea, que añadirá el elemento de menú para eliminar una nota:

menu.add(0, DELETE_ID, 0, R.string.menu_delete);

A la llamada onCreateContextMenu() se le pasa, además del objeto Menu, la vista que se ha activado para el menú e información extra del objeto seleccionado. Sin embargo, no nos importa mucho ya que sólo tenemos un tipo de objeto en la actividad que use menús contextuales. En el próximo paso gestionaremos la selección de elementos del menú.


Paso 3

Ahora necesitamos gestionar la llamada que se realizará cuando seleccionemos un elemento de la lista. Para ello, necesitamos identificar el ID en la lista del elemento seleccionado, y luego borrarlo. Rellena el método onContextItemSelected() con el siguiente código (clic para ampliar, se puede copiar del paso 3 de esta página):


Como podemos ver, creamos un AdapterContextMenuInfo mediante el método getMenuInfo(). El atributo id de este objeto nos dice la posición del elemento en la ListView. Entonces se le pasa al método deleteNote() de nuestro NotesDbAdapter y la nota es eliminada.


Paso 4

Rellenaremos ahora el método createNote().
Crea un objeto Intent para crear una nota (ACTIVITY_CREATE) mediante la clase NoteEdit. Entonces activa el Intent mediante el método startActivityForResult():
Intent i = new Intent(this, NoteEdit.class);
startActivityForResult
(i, ACTIVITY_CREATE);
Como que la clase Intent necesitará comunicarse con el sistema operativo de Android, tenemos que proporcionar un Context (this).

No te preocupes por el hecho de que no exista NoteEdit todavía, pronto lo solucionaremos.


Paso 5

Ahora hay que rellenar el método onListItemClick().
Se trata de un método invocado cuando el usuario selecciona un elemento de la lista, al que se le pasan cuatro parámetros: el objeto ListView desde el que se invoca, la View dentro de la ListView en la que se hizo clic, y el mRowId del elemento clicado. En nuestro caso podemos ignorar los dos primeros (sólo tenemos una ListView) y el mRowId. Lo que nos interesa es la posición (position) que el usuario ha seleccionado. Lo utilizamos para obtener los datos de la fila correcta.

En nuestra implementación, el método crea un Intent para editar la nota mediante la clase NoteEdit. Luego añade información extra que se pasará a la actividad invocada: título, texto y el mRowId de la nota que editemos. Finalmente, lanza el Intent mediante una llamada al método startActivityForResult(). Éste es el código de onListItemClick():
super.onListItemClick(l, v, position, id);
Cursor c = mNotesCursor;
c
.moveToPosition(position);
Intent i = new Intent(this, NoteEdit.class);
i
.putExtra(NotesDbAdapter.KEY_ROWID, id);
i
.putExtra(NotesDbAdapter.KEY_TITLE, c.getString(
c
.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)));
i
.putExtra(NotesDbAdapter.KEY_BODY, c.getString(
c
.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY)));
startActivityForResult
(i, ACTIVITY_EDIT);


Paso 6

onActivityResult() es el método que se llamará cuando una Activity devuelva un resultado (una Activity devolverá un resultado sólo si es lanzada usando startActivityForResult.). Los parámetros pasados son los siguientes:
  • requestCode — puede ser ACTIVITY_CREATE o ACTIVITY_EDIT en nuestro caso.
  • resultCode — el resultado (o código de error) de la llamada, puede ser 0 si todo fue correcto, o diferente de 0 si algo falló. Hay códigos de resultado estándar disponibles, incluso nos podemos crear nuestras constantes para indicar los diferentes tipos de error.
  • intent — Intent creado por los resultados devueltos. Se puede usar para devolver datos en los "extras." del Intent.

La combinación de startActivityForResult() y onActivityResult() se puede tomar como una RPC (remote procedure call) asíncrona y es la manera recomendada de una actividad para invocar y compartir servicios con otra.

Éste es el código del método (clic para ampliar, se puede copiar del paso 6 de esta página):


Paso 7

Abre el fichero res/layout/note_edit.xml y échale una ojeada. Es el código de la interfaz de usuario del Editor de Notas.

Esta vez se trata de una interfaz sofisticada, por lo que se proporciona el fichero ya creado.
Se utiliza un nuevo parámetro que no hemos visto antes: android:layout_weight

layout_weight se utiliza en LinearLayouts para asignar "importancia" a las diferentes Views del layout. Todas las Views tienen un layout_weight por defecto con valor 0, lo que significa que sólo toman el espacio justo que necesitan en pantalla para ser visibles. Si asignamos un valor superior la pantalla dividirá el resto del espacio disponible de la View padre, de acuerdo al valor layout_weight de cada View y su proporción respecto al layout_weight general especificado.

Por ejemplo: supongamos que tenemos una etiqueta de texto y dos elementos de edición de texto en una fila horizontal. la etiqueta no tiene ningún layout_weight especificado, por lo que toma el espacio mínimo para ser mostrada. Si asignamos el valor 1 al layout_weight de cada uno de los dos elementos de edición de texto, el ancho restante del layout padre será dividido en partes iguales entre ellos (porque consideramos que son igual de importantes). Si el primero tuviera un layout_weight de 1 y el segundo de 2, un tercio del espacio sobrante sería asignado al primero, y dos tercios al segundo (porque estamos diciendo que es más importante).

Este layout también demuestra como anidar múltiples layouts uno dentro del otro para conseguir un layout más complejo. En este caso tenemos un linear layout horizontal dentro de otro vertical para que la etiqueta de título y el texto estén una al lado del otro horizontalmente.


Paso 8

Ahora crearemos la clase NoteEdit que heredará de android.app.Activity.

Es la primera vez que creamos una actividad sin que el plugin de Eclipse lo haga por nosotros. Cuando lo hagas, verás que el método onCreate() no es generado automáticamente. Tendrás que hacerlo tú:
  1. Haz clic derecho en el paquete com.android.demo.notepad2 y selecciona New > Class
  2. Escribe NoteEdit en el camp Name:
  3. En el campo Superclass: escribe android.app.Activity
  4. Haz clic en Finish.
  5. En la clase NoteEdit creada, haz clic derecho y selecciona Source > Override/Implement Methods...
  6. Desplázate hacia abajo a través de la lista hasta que veas onCreate(Bundle)y selecciónalo.
  7. Haz clic en OK.

    Ahora el método debería aparecer.


Paso 9

Vamos ahora a rellenar el método onCreate() de NoteEdit.

Esto establecerá el título de la actividad a "Editar nota", uno de los strings definidos en strings.xml. Si quieres puedes traducirlo como yo):

También establecerá el uso del fichero de layout note_edit.xml.

  1. Dentro de onCreate(), establece el layout:
    setContentView(R.layout.note_edit);
  2. Cambia el título de la Activity por el de "Editar Nota"
    setTitle(R.string.edit_note);
  3. Encuentra los componentes EditText y Button necesarios. Se encuentran por los IDs asociados a ellos en la clase R:
    mTitleText = (EditText) findViewById(R.id.title);
    mBodyText
    = (EditText) findViewById(R.id.body);
    Button confirmButton = (Button) findViewById(R.id.confirm);

    Recuerda declarar los atributos mTitleText and mBodyText.

  4. En la parte superior de la clase declara un atributo privado Long mRowId para guardar el mRowId que se esté editando (si se está editando alguno).
  5. Continuando con onCreate(), añade el siguiente código para inicializar title, body y mRowId de los Bundle extra del Intent (si está presente):
    mRowId = null;
    Bundle extras = getIntent().getExtras();
    if (extras != null) {
    String title = extras.getString(NotesDbAdapter.KEY_TITLE);
    String body = extras.getString(NotesDbAdapter.KEY_BODY);
    mRowId
    = extras.getLong(NotesDbAdapter.KEY_ROWID);

    if (title != null) {
    mTitleText
    .setText(title);
    }
    if (body != null) {
    mBodyText
    .setText(body);
    }
    }
  6. Crea un onClickListener() para el botón:

    Queremos que se llame a un método onClick() cuando el usuario presione el botón de confirmación, y utilizarlo para hacer algo y devolver los valores de la nota editada al invocador del Intent. Añade el siguiente listener vacío al método onCreate():

    confirmButton.setOnClickListener(new View.OnClickListener() {

    public void onClick(View view) {

    }

    });

Paso 10

Ahora rellenaremos el método onClick() del OnClickListener.

Este es el código que se ejecutará cuando el usuario haga clic en el botón de confirmación:

  1. Crea un Bundle y ponle título y texto utilizando las constantes definidas en Notepadv2:
    Bundle bundle = new Bundle();

    bundle
    .putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString());
    bundle
    .putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString());
    if (mRowId != null) {
    bundle
    .putLong(NotesDbAdapter.KEY_ROWID, mRowId);
    }
  2. Pasa el resultado (el Bundle) a un nuevo Intent y finaliza la actividad:
    Intent mIntent = new Intent();
    mIntent
    .putExtras(bundle);
    setResult
    (RESULT_OK, mIntent);
    finish
    ();

El código completo de la clase NoteEdit debería tener el siguiente aspecto (clic para ampliar, se puede copiar del paso 10 de esta página):


Paso 11

Finalmente, tenemos que definir la nueva actividad en el fichero AndroidManifest.xml.

Para que una Android vea una actividad es necesario que ésta tenga una entrada en AndroidManifest.xml.

El plugin de Eclipse incluye un editor de Manifest que facilita mucho la edición del archivo, y será el que utilizaremos:

  1. Abre el fichero AndroidManifest.xml
  2. Selecciona la pestaña Application en el editor de Manifest editor
  3. Haz clic en Add... en la sección Application Nodes
  4. Asegúrate de que está seleccionado "(A) Activity" en el panel de selección del diálogo y haz clic en OK.
  5. Haz clic en el nuevo nodo "Activity" en la sección "Application Nodes", y escribe .NoteEdit en el campo Name* de la derecha. Presiona Enter

Paso 12

Ejecuta la aplicación.
Ahora deberías poder añadir notas desde el menú, así como eliminar una existente. Fíjate que para eliminar, primero tienes que utilizar los controles direccionales del aparato para resaltar la nota y luego hacer un clic largo en ella. Además, el hecho de seleccionar una nota desde la lista debería hacer aparecer el editor de notas para editarla. Confirma cuando hayas acabado de editar la nota para guardar los cambios en la base de datos.


Solución

Puedes ver la solución abriendo el proyecto Notepadv2Solution para compararla con la tuya.
En el ejercicio 3 corregiremos acabaremos de pulir la aplicación añadiendo un ciclo de vida correcto que solucionará algunos errores.

7 comentarios:

Belmar Santanilla dijo...

Gracias por los tutoriales, me han sido de mucha ayuda... Pero en especial con este tengo un problema, no me está generando la clase R.java automáticamente y no se como solucionarlo, si puedes ayudarme lo agradecería

Byron dijo...

Hola Belmar
De nada.
La clase R se debe generar automáticamente.
Si no lo hace, algo está mal configurado.
Comprueba que la opción "Project->Build automatically" está habilitada. Luego prueba a modificar un fichero xml (por ejemplo AndroidManifest.xml) y dejarlo como estaba para forzar a eclipse a reconstruir la clase R.
Puedes acompañarlo de un reinicio de Eclipse.

Frezeer dijo...
Este comentario ha sido eliminado por el autor.
Frezeer dijo...

Hola probe lo que dijiste pero en realidad debe ser algo con la forma de realizar el código, porque para el ejemplo uno si funciona perfecto, de todas manera lo hice manual y aqui les dejo link de descarga http://www.megaupload.com/?d=G7YYWL31 del ejemplo base lo extraen y lo pegan en el workspace y simplemente le dan a importar proyecto existente en el espacio de trabajo y ya. Gracias por los tutoriales estan muy buenos.

Byron dijo...

Gracias a ti por tu colaboración :)

Mauma dijo...

Para hacer el onActivityResult() este debe hacerse en otra activity diferente a la del startActivityforResult(), tengo esa duda gracias

Byron dijo...

Fíjate en el proyecto Notepadv2Solution:

https://github.com/hone/NotepadCodeLab/tree/43aa0d725cb18a412e677d45e5cd46e1cc70ced8/Notepadv2Solution/src/com/android/demo/notepad2

Publicar un comentario