Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Curso Android Aula Mentor
Curso Android Aula Mentor
> Oo Oo OOOO Oo Oo
AULA
MENTOR
978-84-369-5431-9
030-12-332-2
NDICE
1.1 INTRODUCCIN .................................................................... 3
1.1.1 Qu es Android .................................................................3
1.1.2 Proyecto libre (Open Source) ..........................................3
1.1.3 Su historia ..........................................................................3
1.1.4 Inconvenientes de Android ..............................................4
1.2 QU ES ECLIPSE.................................................................. 5
1.2.1 El Consorcio Eclipse .........................................................5
1.2.2 Instalacin de Java Developmente Kit (JDK)................ 6
1.2.3 Instalacin de Eclipse .......................................................7
1.2.4 Instalacin de las libreras de Android ........................... 9
1.2.5 Aadir versiones y componentes de Android ............. 19
1.2.6 Definicin del dispositivo virtual de Android ............... 25
2
Introduccin
1.1 INTRODUCCIN
1.1.1 Qu es Android
1.1.3 Su historia
Android era un sistema operativo para mviles prcticamente desconocido hasta que
en 2005 lo compr Google.
3
El despegue del sistema operativo fue lento porque se lanz antes el sistema operativo
que el primer terminal mvil, aunque rpidamente se ha colocado como el sistema operativo
de mviles ms vendido del mundo.
Versiones disponibles:
Android ha sido criticado muchas veces por la fragmentacin que sufren sus
terminales (con versiones distintas de Android), ya que las actualizaciones no se despliegan
automticamente en estos terminales una vez que Google lanza una nueva versin. Cada
fabricante debe crear su propia versin. Sin embargo, esa situacin cambiar ya que los
fabricantes se han comprometido a aplicar actualizaciones al menos durante los 18 meses
siguientes desde que empiezan a vender un terminal en el mercado.
Disponer del cdigo fuente del sistema operativo no significa que se pueda tener
siempre la ltima versin de Android en un determinado mvil, ya que el cdigo para soportar
el hardware de cada fabricante normalmente no es pblico; as que faltara un trozo bsico del
firmware (controladores) para poder hacerlo funcionar en dicho terminal.
Hay que tener en cuenta que las nuevas versiones de Android suelen requerir ms
recursos, por lo que, en los modelos ms antiguos, no se puede instalar la ltima versin por
insuficiente memoria RAM, velocidad de procesador, etctera.
4
Introduccin
1.2 QU ES ECLIPSE
Usando distintas libreras es posible servirse de este entorno de desarrollo para otros
lenguajes de programacin, como Ada, C, C + +, COBOL, Perl, Delphi, PHP, Python, R. Ruby,
Scala, Clojure y Scheme.
Esta lista de lenguajes aumenta con los aos, ya que este IDE se est convirtiendo en
el entorno de desarrollo de muchos programadores por su simplicidad y facilidad de uso.
5
1.2.2 Instalacin de Java Developmente Kit (JDK)
Es muy importante tener en cuenta que, para poder ejecutar el entorno de desarrollo
Eclipse y las libreras de Android, es necesario tener instaladas en el ordenador las libreras de
desarrollo de Java. Aunque ya est disponible la versin 1.7, para poder compilar
aplicaciones Android, es necesario instalar la versin 6 de Java (tambin conocida como
Java 1.6).
http://java.sun.com/javase/downloads
6
Introduccin
Nota: en el caso de Linux o Mac, es posible tambin instalar Java usando los
programas habituales del sistema operativo que permiten la actualizacin de paquetes.
Nota: si vas a instalar Eclipse y las libreras de Android en Linux, lee las notas que se
encuentran en Preguntas y Respuestas de esta Introduccin en la mesa del curso.
http://www.eclipse.org/downloads/
7
Hay que tener en cuenta que debemos descargar la versin 32 bits o 64 bits en funcin
del sistema operativo de que dispongamos.
En el caso de Windows podemos ver el tipo de sistema operativo haciendo clic con el
botn derecho del ratn en el icono "Equipo" o Mi PC del Escritorio y haciendo clic de nuevo
en "Propiedades":
$ uname -m
x86_64
En el caso de Apple Mac, utiliza el Perfil de Sistema para determinar si ests utilizando
un kernel de 64 bits.
8
Introduccin
Una vez descomprimido el fichero, Eclipse est listo para ser utilizado; no es necesario
hacer ninguna operacin adicional.
9
Para descargar el fichero necesario, accedemos a la pgina de descarga del SDK de
Android y nos bajamos la versin que corresponda en funcin del sistema operativo donde
vayamos a instalarlo. Recomendamos que hay que descargar el archivo .zip, ya que con ste
la instalacin es ms sencilla y rpida:
Si usas otro directorio, toma nota del mismo, ya que, ms adelante, tendrs que usar
el nombre de este directorio para acabar de configurar el plugin de Android en Eclipse.
Ahora vamos a instalar las libreras necesarias en Eclipse. Estas libreras se denominan
Android Development Tools (ADT). Para ello, arrancamos Eclipse haciendo doble clic sobre
el acceso directo que hemos creado anteriormente. A continuacin, Eclipse pedir que
seleccionemos el "workspace", es decir, el directorio donde queremos guardar los proyectos.
10
Introduccin
Si no podemos seleccionar "jre6" debemos usar el botn "Add" para aadir las
libreras del JRE 6. Por ejemplo, en Windows, se pueden encontrar en el directorio:
"C:\Program Files\Java\jre6".
11
Para finalizar, en esta ventana hay que seleccionar la versin de Java utilizada para
compilar los proyectos de Android. Para ello hacemos clic en Java->Compiler y elegimos
1.6 en el campo Compiler compliance settings:
12
Introduccin
Importante: Es necesario disponer de conexin a Internet para poder continuar con los
siguientes pasos y poder descargar las libreras necesarias.
https://dl-ssl.google.com/android/eclipse/
Y pulsamos la tecla Intro. A continuacin, marcamos todas las opciones tal y como se
muestra en la siguiente captura de pantalla:
13
Hacemos clic en el botn "Next".
14
Introduccin
15
Al arrancar de nuevo Eclipse ya dispondremos de las libreras necesarias para
empezar a trabajar con Android:
16
Introduccin
17
Pulsamos el botn Finish para finalizar la configuracin. A continuacin, aparece el
siguiente mensaje indicando que no hemos instalado ninguna versin del sistema operativo
Android:
18
Introduccin
El SDK utiliza una estructura modular que separa las distintas versiones de Android,
complementos, herramientas, ejemplos y la documentacin en un nico paquete que se puede
instalar por separado. Para desarrollar una aplicacin en Android, es necesario descargar, al
menos, una versin. En este curso vamos a usar la versin 2.3, por ser la ms extendida en
el momento de redaccin de la documentacin. No obstante, vamos a emplear sentencias
compatibles y recompilables en otras versiones.
Para aadir esta versin hay que hacer clic en la opcin Android SDK Manager del
men principal Window de Eclipse:
19
Se abrir la ventana siguiente:
20
Introduccin
Nota: la revisin de las versiones de Android puede ser superior cuando al alumno o
alumna instale el SDK.
Una vez hemos pulsado el botn Install 6 packages, aparece esta ventana y
seleccionamos la opcin Accept All y, despus, hacemos clic en Install:
21
Para acabar, reiniciamos el ADB (Android Debug Bridge):
Ahora vamos a ver la estructura que tiene el SDK de Android. Para ello, abrimos el
explorador en el directorio C:\cursos_Mentor\Android\android-sdk-windows o en el
directorio donde lo hayamos descomprimido. La siguiente tabla describe los subdiretorios que
contiene esta carpeta:
22
Introduccin
NOMBRE
DESCRIPCIN
CARPETA
Contiene los paquetes add-on del SDK de Android que permiten desarrollar
add-ons/ aplicaciones usando libreras externas disponibles para algunos dispositivos
o terminales.
samples/ Contiene los ejemplos de cdigo para esa versin especfica de Android.
SDK
Archivo que explica cmo realizar la configuracin inicial del SDK de Android.
Readme.txt
23
Finalmente, vamos a incluir el directorio donde hemos instalado las libreras de
Android en el PATH del sistema operativo. En concreto, vamos a incluir los directorios tools y
platform-tools.
C:\cursos_Mentor\Android\android-sdk-windows\tools
y
C:\cursos_Mentor\Android\android-sdk-windows\platform-tools
24
Introduccin
En OS X (Mac) y Linux, puedes agregar la ruta a la variable PATH con el comando SET
o estableciendo la variable correspondiente en un script de inicio.
Para poder hacer pruebas de las aplicaciones Android que desarrollemos sin
necesidad de disponer de un telfono Android, el SDK incluye la posibilidad de definir un
Dispositivo Virtual de Android (en ingls, AVD, Android Virtual Device). Este dispositivo
emula un terminal con Android instalado.
Para definir el AVD, hacemos clic en la opcin Android AVD Manager del men principal
Window de Eclipse:
25
Aparecer la siguiente ventana:
26
Introduccin
Importante: En el curso hemos creado un dispositivo virtual que no guarda el estado porque
puede producir problemas de ejecucin con Eclipse. En todo caso, el alumno o alumna puede
usar la opcin Edit del AVD cuando crea necesario que los ltimos cambios sean
almacenados para la siguiente sesin de trabajo
27
En esta Introduccin puedes encontrar el vdeo Cmo instalar Eclipse y el plugin
Android, que muestra de manera visual los pasos seguidos en las explicaciones anteriores
28
INTRODUCCIN AL ENTORNO
ANDROID
NDICE
1.1 INTRODUCCIN AL ENTORNO DE ANDROID ....................... 31
1.1.1 Introduccin ....................................................................................31
1.1.2 Caractersticas de Android ...........................................................31
1.1.3 Arquitectura de Android ................................................................33
1.1.4 Creacin de un proyecto por lneas de comando ..................... 35
1.1.1 Introduccin
Diseado para
El sistema operativo es compatible con pantallas VGA (y mayores),
dispositivo
grficos 2D y grficos 3D presentes en muchos telfonos tradicionales.
pequeos
31
Android soporta los siguientes formatos multimedia: WebM, H.263,
H.264 (en 3GP o MP4), MPEG-4 SP, AMR, AMR-WB (en un contenedor
Soporte multimedia
3GP), AAC, HE-AAC (en contenedores MP4 o 3GP), MP3, MIDI, Ogg
Vorbis, WAV, JPEG, PNG, GIF y BMP.
32
Introduccin al entorno Android
Los componentes principales de la arquitectura del sistema operativo Android son los
siguientes:
Libreras: Android incluye tambin un conjunto de libreras de C/C++ usadas por varios
componentes del sistema. Entre ellas, se encuentran: System C library (implementacin
de la librera C estndar), libreras de medios, bibliotecas de grficos, 3D y SQLite,
entre otras. El programador puede hacer uso de estas libreras.
Ncleo Linux: Android est basado en Linux para los servicios base del sistema, como
seguridad, gestin de memoria, procesos y controladores.
33
DIAGRAMA DE LA ARQUITECTURA ANDROID
Android usa Java como lenguaje base para el desarrollo de las aplicaciones. Por lo tanto,
hace uso de los Paquetes Java (Package en ingls). Estos paquetes son contenedores de
clases que permiten agrupar las distintas partes de un programa cuya funcionalidad tienen
elementos comunes.
o Reutilizacin de cdigo
34
Introduccin al entorno Android
para crear los ficheros bsicos de un proyecto Android. Fjate en qu la orden anterior es una
nica lnea que debes ejecutar en la lnea de comandos de tu sistema operativo.
C:\>cd C:\cursos_Mentor\Android\proyectos
Created directory
C:\cursos_Mentor\Android\proyectos\bienvenido\src\es\mentor\eje1\unidad1\bienvenido
Added file
C:\cursos_Mentor\Android\proyectos\bienvenido\src\es\mentor\eje1\unidad1\bienvenido\Bienv
enido.java
C:\cursos_Mentor\Android\proyectos>
35
Ahora no vamos a examinar en detalle los ficheros que hemos creado y que conjuntamente
forman el proyecto Android. En el siguiente apartado de esta Unidad los detallaremos con un
ejemplo real en Eclipse.
/src: en este directorio es donde se almacenan los archivos de cdigo fuente Java (con
extensin .java).
/res/values: de igual forma que separamos el cdigo Java y la interfaz grfica, Android
separa tambin las cadenas constantes de texto (Internacionalizacin de la aplicacin),
las matrices, la paleta de colores, etctera.
Nota: una vez analizado los ficheros bsicos del proyecto, podramos importarlo en
Eclipse. No obstante, por simplificacin, no lo vamos a hacer, pues es ms sencillo
crear un proyecto nuevo directamente desde Eclipse.
36
Introduccin al entorno Android
Una vez instalada una aplicacin en un dispositivo, Android la aloja en su propia caja
de arena (sandbox). Sandbox, palabra del ingls que significa caja de arena (Sand+box), es
un sistema informtico que asla los procesos; de esta manera se pueden ejecutar
aplicaciones de forma independiente. Se utiliza para evitar la corrupcin de datos del sistema
donde stos se ejecutan.
Los Componentes de las aplicaciones son los elementos esenciales de una aplicacin
Android. Cada componente es un punto diferente de entrada por el que el sistema operativo
puede interaccionar con la aplicacin. No todos los componentes son verdaderos puntos de
entrada, sino que algunos dependen unos de otros, aunque cada uno exista como una entidad
separada en Android y desempee un papel especfico que define el comportamiento general
de la aplicacin.
37
Existen los siguientes tipos de componentes de la aplicacin:
Actividades (Activities): una actividad representa una pantalla nica con una interfaz
de usuario. Por ejemplo, una aplicacin de correo electrnico puede tener una
actividad que muestra una lista de correo electrnico nuevo, otra actividad que
compone un correo y otra actividad que lee los mensajes. Aunque las actividades
trabajan conjuntamente para dar la sensacin de una nica aplicacin, cada una de
ellas es independiente de las otras. Por lo tanto, otra aplicacin externa diferente
podra iniciar cualquiera de estas actividades (si la aplicacin de correo electrnico lo
permite). Por ejemplo, una aplicacin que gestiona los contactos podra iniciar la
actividad que compone nuevos mensajes de correo indicando como destinatario del
mensaje al contacto seleccionado en la primera aplicacin.
Puedes pensar en una actividad de Android como si fuera una ventana en una
aplicacin de escritorio o una pgina HTML en una aplicacin Web. Android est
diseado para cargar muchas actividades pequeas, por lo que se permite al usuario
abrir nuevas actividades y pulsar el botn Atrs para ir a un estado anterior, al igual
que se hace en un navegador web.
38
Introduccin al entorno Android
Los proveedores de contenidos se utilizan tambin para escribir y leer datos que son
privados de la aplicacin y no se comparten. Por ejemplo, una aplicacin de Notas
puede utilizar un proveedor de contenidos para guardar las notas.
Otros componentes de Android son las Carpetas animadas (Live Folders) y los
Fondos de pantalla animados (Live Wallpapers) en la Pantalla de Inicio. Las
carpetas animadas permiten a Android mostrar informacin en la pantalla inicial sin
necesidad de lanzar la aplicacin correspondiente. Igualmente, al tratarse de
programacin avanzada, no los estudiaremos en este curso de iniciacin a Android.
Un aspecto nico del diseo del sistema Android es que cualquier aplicacin puede iniciar un
componente de otra aplicacin. Por ejemplo, si es necesario para la aplicacin abierta
capturar una imagen con la cmara de fotos, seguramente ya exista otra aplicacin que hace
eso y que la aplicacin inicial puede reutilizar en lugar de desarrollar el cdigo necesario para
capturar la foto. nicamente hay que iniciar la actividad de la aplicacin de la cmara de fotos
y capturar la imagen. La sensacin del usuario es como si la cmara formara parte de la
aplicacin inicial.
39
Por ejemplo, si una aplicacin inicia la actividad de la aplicacin de la cmara que hace fotos,
la actividad se ejecuta en el proceso que pertenece a la aplicacin de la cmara, no en el
proceso de la aplicacin original que ha hecho la llamada a la otra aplicacin. Por lo tanto, a
diferencia de otros sistemas operativos, las aplicaciones de Android no tienen un punto de
entrada nico (no hay funcin main()).
La primera vez que se ejecuta Eclipse se puede ver una pantalla muy similar a la que
se muestra a continuacin.
40
Introduccin al entorno Android
1.3.1.1 Editores
Es posible tener varios ficheros de cdigo fuente abiertos a la vez, apilados uno encima
de otro. En la parte superior de la ventana del Editor se muestran las pestaas que permiten
acceder a cada uno de los ficheros abiertos (o bien cerrarlos directamente).
Editor
1.3.1.2 Vistas
Adems del Editor, existe un segundo tipo de ventanas secundarias, que se llaman
Vistas.
Las Vistas son ventanas auxiliares para mostrar informacin, introducir datos, etctera.
Las Vistas se usan con mltiples propsitos, desde navegar por un rbol de directorios, hasta
mostrar el contenido de una consulta SQL.
41
Vistas
Si deseamos cambiar las Vistas, se puede usar la opcin Show View en el men de la
pestaa Window.
42
Introduccin al entorno Android
Adems de la barra de herramientas principal (imagen anterior), cada Vista puede tener
su propia barra de herramientas secundaria.
1.3.1.4 Perspectivas
Por ejemplo, existe una Perspectiva "Java Browsing" que facilita el desarrollo de
aplicaciones Java y que incluye, adems del Editor, Vistas para navegar por las clases, los
paquetes, etctera.
43
Tambin existe un botn en la barra de herramientas principal para cambiar de
Perspectiva:
Si el alumno tiene dudas sobre el uso avanzado de Eclipse, en Internet existen muchos
tutoriales que indican cmo utilizarlo.
Adems, es posible usar el men "Help" o la tecla [F1] para solicitar ayuda.
Desgraciadamente, a da de hoy, esta ayuda slo se encuentra en ingls.
Importante: para importar en Eclipse el cdigo fuente de los ejemplos del curso hay que
usar la opcin del men principal: File -> Import.
44
Introduccin al entorno Android
Despus, hay que marcar Existing Proyects into Workspace en la ventana emergente y
pulsar en el botn Next:
45
Nota: en esta Unidad 1 puedes encontrar el vdeo Cmo cargar los ejemplos en Eclipse,
que muestra cmo se importan los ficheros con el cdigo fuente de los proyectos que son los
ejemplos del curso.
A continuacin, vamos a describir cmo crear un proyecto usando Eclipse y las libreras de
Android que hemos instalado con anterioridad.
Se trata del primer proyecto que el alumno va a crear, por lo que es muy importante prestar
atencin a los pasos seguidos, ya que los proyectos siguientes se generan de manera similar.
46
Introduccin al entorno Android
47
Pulsamos el botn Finish para crear los ficheros del proyecto.
Build Target: indica la versin del Android SDK que vamos a usar para compilar
la aplicacin. Por ejemplo, si seleccionas Android 2.3, la aplicacin se compilar
para funcionar en esta versin de Android y las siguientes. La versin
seleccionada aqu no tiene que coincidir con la versin del Emulador de Android
(AVD), ya que las aplicaciones de Android estn diseadas de manera que se
48
Introduccin al entorno Android
Importante: El nombre del paquete debe ser nico en relacin con todos los paquetes
instalados en Android. Por esta razn, es importante utilizar el estndar de dominio para
nombrar los paquetes de las aplicaciones. En el ejemplo anterior se utiliza el nombre de
paquete "es.mentor". A la hora de desarrollar tus propias aplicaciones y distribuirlas en el
Android Market (Mercado de aplicaciones Android) debes utilizar nombres propios de
paquetes.
En ningn caso debes utilizar el nombre es.mentor para distribuir aplicaciones en el Android
Market, pues slo es vlido para los ejemplos del curso.
Para ver los ficheros del proyecto Android creados por Eclipse, en la barra lateral
Package Explorer, desplegamos las entradas haciendo clic en los ficheros marcados con
flechas rojas de los diferentes paquetes:
49
A continuacin, vamos a explicar la estructura y contenido de los ficheros del proyecto.
Carpeta/src/
Carpeta /res/
Contiene todos los ficheros de recursos necesarios para el proyecto: imgenes, vdeos,
cadenas de texto (para internacionalizacin de la aplicacin), etctera. Los diferentes tipos de
recursos se deben distribuir entre las siguientes subcarpetas:
Carpeta /gen/
Esta clase R contiene un conjunto de constantes con los ID de todos los recursos de la
aplicacin incluidos en la carpeta /res/, de forma que el programador pueda acceder
fcilmente a estos recursos desde el cdigo fuente a travs de esta clase. As, por ejemplo, la
constante R.drawable.icon define el ID de la imagen icon.png contenida en la
carpeta /res/drawable/. Veamos como ejemplo la clase R creada por defecto para el
proyecto nuevo:
package es.mentor.unidad1.eje1.bienvenido;
Importante: Esta clase la crea automticamente Android por lo que no debemos modificarla.
51
Carpeta /assets/
Alberga el resto de ficheros auxiliares necesarios para que aplicacin funcione, como
los ficheros de configuracin, de datos, etctera.
Carpeta /bin/
Carpeta /libs/
Es el directorio donde se almacenan las libreras de tipo JAR que amplan las
funcionalidades de la aplicacin.
Fichero AndroidManifest.xml:
Importante: Haciendo doble clic sobre estos ficheros podemos abrirlos en el Editor de
Eclipse. Es importante que el alumno se familiarice con este entorno de desarrollo y pruebe
las distintas opciones del mismo.
52
Introduccin al entorno Android
Una vez hemos creado el proyecto, vamos a explicar cmo ejecutamos esta aplicacin
de prueba con Eclipse y el emulador de Android (AVD: Android Virtual Device).
o en la opcin "Run" de men Run. Tambin disponemos del atajo del teclado
[Ctrl+F11]
53
Si no aparece ningn problema de compilacin, entonces aparecer la siguiente
ventana:
Eclipse inicia el emulador de Android AVD en el ordenador que ests utilizando, para
que puedas probar el proyecto que has desarrollado. Ten en cuenta que el dispositivo
virtual tarda un rato en cargar cada vez que lo inicias. Hay que tener un poco de paciencia
hasta que parezca la ventana de inicio.
54
Introduccin al entorno Android
Cuando accedemos por primera vez al emulador, aparece la pantalla de bienvenida con
el terminal bloqueado:
Para desbloquear la pantalla hay que arrastrar el icono "candado" con el ratn hacia la
derecha.
55
Importante: En general, cada vez que modifiquemos el cdigo fuente y deseemos probar de
nuevo nuestro proyecto no es necesario parar el emulador de aplicaciones y arrancarlo de
nuevo; simplemente hacemos clic de nuevo en el botn Run y Eclipse compilar, reinstalar
y ejecutar la aplicacin modificada.
Una vez hayamos acabado de probar nuestro proyecto, es necesario parar el emulador de
Android.
Nota: En esta Unidad 1 puedes encontrar el vdeo Cmo ejecutar un proyecto Android, que
muestra cmo usar Eclipse para compilar y ejecutar los proyectos que son los ejemplos del
curso.
Si no conoces Android, lee las siguientes instrucciones para ver cmo se maneja.
Cambiaremos el idioma del sistema operativo.
56
Introduccin al entorno Android
57
Si lo hacemos, veremos las aplicaciones instaladas. A esta pantalla se la denomina
Pantalla de lanzamiento (en ingls se denomina Launcher Screen):
58
Introduccin al entorno Android
Para movernos en esta pantalla, podemos usar el ratn como si fuera el dedo de tu
mano. Es decir, para ver los iconos que estn abajo hay que hacer clic en la pantalla y, sin
soltar el botn del ratn, arrastrar la ventana hacia arriba:
Haciendo clic con el ratn sobre el icono de una de las aplicaciones, el emulador la
ejecutar.
A continuacin, vamos a modificar el idioma del sistema operativo. Para ello, haciendo
clic en el icono Settings aparece la siguiente pantalla:
59
Desplazando con el ratn hacia arriba esta ventana hacemos clic en Language &
keyboard:
60
Introduccin al entorno Android
Para acabar, desplazamos de nuevo la pantalla hacia arriba hasta que veamos el idioma
en el que deseamos configurar Android:
61
Despus, debemos desmarcar la opcin Japanese IME de esta pantalla:
Si usamos el botn Volver atrs, veremos que el idioma del sistema operativo ha
cambiado en la Pantalla Inicial (Home Screen):
62
Introduccin al entorno Android
A continuacin, vamos a explicar cmo crear un proyecto sencillo usando Eclipse y las
libreras Android.
Vamos a partir del proyecto de ejemplo que hemos creado en el punto anterior.
El primer proyecto Android consiste en una pantalla muy sencilla que muestra un
mensaje de bienvenida.
63
package es.mentor.unidad1.eje1.bienvenido;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
super.onCreate(savedInstanceState);
// de forma programada.
setContentView(tv);
// setContentView(R.layout.main);
65
Nota:
Al ejecutar varias veces una aplicacin desde Eclipse puede ocurrir que aparezcan los
siguientes mensajes de error en la consola:
Los Layout son elementos no visibles que establecen cmo se distribuyen en la interfaz
del usuario los componentes (widgets) que incluyamos en su interior. Podemos pensar en
estos elementos como paneles donde vamos incorporando, de forma diseada, los
componentes con los que interacciona el usuario.
66
Introduccin al entorno Android
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/bienvenido"
/>
</LinearLayout>
Esta estructura XML hace que sea ms fcil y rpido crear las interfaces de usuario.
Este modelo se basa en el modelo de desarrollo web, donde se separa la presentacin
(interfaz de usuario) de la lgica de la aplicacin (encargada de leer y escribir la informacin).
En el ejemplo de XML anterior slo hay un elemento Vista: TextView, que tiene tres
atributos y un elemento de diseo Layout: LinearLayout, que tiene cuatro atributos. A
continuacin, mostramos una descripcin de los atributos:
67
Atributo Descripcin
Define el largo que debe ocupar la Vista. En este caso, indicamos que
android:layout_width el TextView debe ocupar toda la pantalla con "fill_parent.
El SDK de Android permite definir los ficheros de tipo XML de dos formas: a travs de
un editor visual o directamente en el archivo XML. Se puede cambiar entre las dos formas
haciendo clic en la pestaa de la parte inferior de la ventana. Por ejemplo, en el Package
Explorer, seleccionamos res/layout/main.xml y hacemos clic en Graphical Layout:
68
Introduccin al entorno Android
Usando en esta ventana el botn Add podemos aadir visualmente los diferentes
tipos de recursos de Android.
Para crear la primera aplicacin hemos usado componentes (Widgets o Vistas) usuales
de Android. En el siguiente apartado de teora explicaremos en detalle el tipo de componentes
disponibles por defecto y cmo usarlos para disear las pantallas que servirn de interfaz
grfica al usuario.
En este fichero hay que declarar la Actividad para que Android tenga acceso a la misma.
Si abres el archive manifest, vers que existe el siguiente elemento <activity>:
69
<?xml version="1.0" encoding="utf8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
...
<activity android:name=".BienvenidoActivity"
android:label="@string/app_name">
...
</manifest>
Adems, si no has usado nunca el entorno de desarrollo Eclipse - Android, adquirirs soltura
utilizndolo.
Nota: Como en la literatura inglesa de Android se habla genricamente de Vista (View) para
referirse a estos componentes visuales y en Internet siempre aparecen referencias a esta
palabra, vamos a usar esta nomenclatura a partir de ahora.
Es importante no confundir los widgets (componentes o Vistas) que usamos al desarrollar las
interfaces de usuario en Android con Widget de la pantalla principal (Screen Home), que son
pequeas aplicaciones que el usuario del telfono puede aadir a esta pantalla, tales como un
calendario dinmico, previsin meteorolgica, etctera.
70
Introduccin al entorno Android
Como hemos visto en el apartado anterior, las Vistas visibles deben situarse dentro de
otro tipo de Vista denominada Layout (Panel de diseo).
Estos paneles de diseo (Layout) de Android se usan para disear la interfaz grfica del
usuario de la aplicacin. Estos paneles se usan para separar simblicamente el rea de la
aplicacin. Dentro de estos paneles se incluye la mayora de las Vistas, como botones,
cuadros de texto, etctera. Adems, dentro de un panel se pueden incluir otros paneles
para hacer diseos complejos.
Nota: Cuando se describan los mtodos ms importantes de cada Vista (View), slo se
incluirn aqullas que no se hayan sealado con anterioridad o se invoquen con diferentes
argumentos.
71
Tipos de paneles (Layout)
fill_parent para que el componente hijo tenga la dimensin del layout que lo
contiene.
Ejemplo
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<EditText android:id="@+id/TextoNombre"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</FrameLayout>
72
Introduccin al entorno Android
Vertical y Horizontal
Ejemplo
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<EditText android:id="@+id/TextoDato1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1" />
<EditText android:id="@+id/TextoDato2"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="2" />
</LinearLayout>
73
Panel Tabla (TableLayout)
Por norma general, el ancho de cada columna corresponde al ancho del mayor
componente de dicha columna, pero existen una serie de propiedades pueden modificar este
comportamiento:
Todas estas propiedades del TableLayout pueden establecerse con una lista
de ndices de las columnas separados por comas, por ejemplo:
android:stretchColumns=1,2,3 o un asterisco para indicar que se debe
aplicar a todas las columnas, de esta forma: android:stretchColumns=*.
Ejemplo
<TableLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stretchColumns="1">
<TableRow>
<TextView android:text="Celda 1.1">
<TextView android:text="Celda 1.2">
</TableRow>
<TableRow>
74
Introduccin al entorno Android
<TableRow>
<TextView android:text="Celda 3" android:layout_span="2" />
</TableRow>
</TableLayout>
Desde Eclipse puedes abrir el proyecto Ejemplo 2 (Panel tabla) de la Unidad 1. Estudia
el cdigo fuente y ejectalo para mostrar en el emulador el resultado del programa anterior,
en el que hemos utilizado el Layout TableLayout.
android:layout_above: arriba.
android:layout_below: debajo.
android:layout_toLeftOf: a la izquierda de.
android:layout_toRightOf: a la derecha de.
android:layout_alignLeft: alinear a la izquierda.
android:layout_alignRight: alinear a la derecha.
android:layout_alignTop: alinear arriba.
android:layout_alignBottom: alinear abajo.
android:layout_alignBaseline: alinear en la base.
Ejemplo
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<EditText android:id="@+id/TextoNombre"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button android:id="@+id/BtnAceptar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/TextoNombre"
android:layout_alignParentRight="true" />
</RelativeLayout>
76
Introduccin al entorno Android
Ejemplo
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="center"
android:src="@drawable/imagen" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dip"
android:layout_gravity="center_horizontal|bottom"
android:padding="12dip"
android:background="#AA000000"
android:textColor="#ffffffff"
android:text="Imagen" />
</FrameLayout>
En este ejemplo, mostramos una imagen que ocupa todo el rea de la pantalla usando
el atributo android:scaleType="center" y una etiqueta superpuesta en la parte inferior de la
pantalla con el atributo android:layout_gravity="center_horizontal|bottom".
77
Componentes Bsicos
Botones
Los botones se usan para que el usuario interacte con la aplicacin Web:
android:id="@+id/boton" android:layout_height="wrap_content"
android:layout_width="135dp">
</Button>
<ToggleButton android:id="@+id/toggleButton"
android:text="ToggleButton" android:layout_width="88dp"
android:textOn="Encendido"
android:textOff="Apagado"
android:layout_height="match_parent">
78
Introduccin al entorno Android
</ToggleButton>
<ImageButton android:layout_height="wrap_content"
android:src="@drawable/stop" android:layout_width="wrap_content"
android:id="@+id/imageButton">
</ImageButton>
Para definir la lgica de este evento hay que definir un nuevo objeto
View.OnClickListener() y asociarlo al botn mediante el mtodo setOnClickListener(). La
forma de hacerlo es la siguiente:
btnBoton1.setOnClickListener(new View.OnClickListener() {
@Override
});
En el caso del botn de tipo ToggleButton suele ser ms til conocer el estado en el
que est el botn tras ser pulsado. Para esto, se usa el mtodo isChecked(). En el siguiente
ejemplo se comprueba el estado del botn despus de ser pulsado y se realizan diferentes
acciones segn su resultado:
79
final ToggleButton btnBoton2 =
(ToggleButton)findViewById(R.id.toggleButton);
btnBoton2.setOnClickListener(new View.OnClickListener() {
@Override
if(btnBoton2.isChecked())
lblEtiqueta.setText("Botn Encendido");
else
lblEtiqueta.setText("Botn Apagado");
});
Etiqueta (TextView)
android:textSize="30dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
android:layout_width="wrap_content"
80
Introduccin al entorno Android
android:layout_height="wrap_content"
android:textSize="25dp"
android:typeface="serif" />
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold" />
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="40dp"
android:textColor="#FF0000"
android:textStyle="italic" />
Adems, podemos modificar estas propiedades desde nuestro cdigo Java usando los
mtodos getText() para recuperar el texto de una etiqueta, setText() para actualizar el texto y
setBackgroundColor() para cambiar el color de fondo. Por ejemplo, as:
lblEtiqueta.setText(texto);
Imagen (ImageView)
81
otras, como las destinadas a establecer el tamao mximo que puede ocupar la imagen:
android:maxWidth y android:maxHeight. Fjate en el cdigo del siguiente ejemplo:
<ImageView android:id="@+id/ImgFoto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/icon" />
img.setImageResource(R.drawable.icon);
<EditText android:id="@+id/editTexto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
Tambin es posible recuperar y establecer este texto mediante los mtodos getText() y
setText(nuevoTexto) respectivamente:
82
Introduccin al entorno Android
texto = txtTexto.getText().toString();
txtTexto.setText("Esto es un texto");
Es decir, el componente EditText permite editar texto plano y texto enriquecido o con
formato; por eso hemos tenido que usar un mtodo para cambiar la cadena perdiendo el
formato enriquecido.
Para poder obtener el texto con el formato correspondiente, podemos usar la clase
Html de Android, que dispone de los mtodos para convertir cualquier objeto de tipo
Spanned en su representacin HTML equivalente. Veamos cmo funciona:
txtTexto.setText(
BufferType.SPANNABLE);
83
Cuadro de Seleccin (CheckBox)
<LinearLayout android:id="@+id/linearLayout4"
android:orientation="vertical" android:layout_width="154dp"
android:layout_height="wrap_content">
</LinearLayout>
android:layout_height="wrap_content"
android:text="Confirmar Seleccin"
android:layout_gravity="center_vertical" />
En cuanto a los posibles eventos interesantes que puede lanzar este componente, el
ms interesante es onCheckedChanged que notifica que la seleccin ha cambiado. Por
ejemplo, as:
CheckBox.OnCheckedChangeListener() {
boolean isChecked)
84
Introduccin al entorno Android
if (isChecked) {
" marcado!");
else {
" desmarcado!");
};
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<RadioButton android:id="@+id/radio1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
85
android:text="Opcin 1" />
<RadioButton android:id="@+id/radio2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
</RadioGroup>
Una vez definida la interfaz, podemos manipular los componentes desde el cdigo java
haciendo uso de los diferentes mtodos del componente RadioGroup como, por ejemplo:
En cuanto a los eventos iniciados por este componente, como los CheckBox, el ms
til es el que informa de los cambios en el elemento seleccionado onCheckedChange. Fjate
en el siguiente ejemplo:
rg.setOnCheckedChangeListener(
new RadioGroup.OnCheckedChangeListener() {
// el ID marcado checkedId
86
Introduccin al entorno Android
});
87
Android es un sistema operativo, inicialmente diseado para telfonos mviles
con sistemas operativos iOS (Apple), Symbian (Nokia) y Blackberry OS.
Android usa Java como lenguaje base para el desarrollo de las aplicaciones; por lo
tanto, emplea Paquetes Java (Package en ingls). Estos paquetes son
contenedores de clases que permiten agrupar las distintas partes de un programa
cuya funcionalidad tienen elementos comunes.
El nombre de los paquetes debe ser nico en relacin con los paquetes
instalados en Android. Por esta razn, es importante utilizar el estndar de dominio
com.dominio para nombrarlos.
Los ficheros que contengan el cdigo fuente de las actividades del alumno han de
guardarse en una carpeta personal. Recomendamos usar el directorio
C:\cursos_Mentor\Android\proyectos para este fin.
88
Introduccin al entorno Android
Para comprobar que una aplicacin funciona, hay que hacer clic en la opcin "Run"
de men "Run" de Eclipse (Atajo del teclado [Ctrl+F11]); despus, arrancar el
Emulador (AVD) de Android para ver el resultado de su ejecucin.
Un Dispositivo Virtual de Android (en ingls, AVD, Android Virtual Device) emula
un terminal instalado con Android en el que podemos probar las aplicaciones
desarrolladas.
Los paneles en Android se usan para disear (en ingls se denomina layout) la
interfaz grfica del usuario de la aplicacin.
89
Las Vistas visibles estn contenidos en los paneles y permiten interaccionar al
usuario con la aplicacin.
90
DISEO DEL INTERFAZ DE
USUARIO
NDICE
2.1 ACTIVIDADES - ANDROID.................................................... 93
2.1.1 Introduccin ..............................................................................93
2.1.2 Creacin de una actividad ......................................................93
2.1.3 Ciclo de vida de una actividad ...............................................94
2.1.4 Cmo se implementa el ciclo de vida de una actividad ..... 94
2.1.1 Introduccin
Por lo general, una aplicacin de Android consta de mltiples actividades que estn
ms o menos ligadas entre s. Habitualmente, se define una actividad "principal", que es la que
se presenta al usuario cuando se inicia la aplicacin por primera vez. Una actividad puede
iniciar otra actividad con el fin de realizar diferentes operaciones. Cada vez que comienza una
nueva actividad, la actividad anterior se detiene y la enva a una pila de retroceso ("back
stack"). Esta pila usa el mecanismo de cola LIFO ("last in, first out"), por lo que, cuando el
usuario pulsa la tecla Volver atrs del mvil, se extrae de la pila la actividad anterior
(destruyndose la pila) y se reanuda. En la Unidad 3 veremos en detalle cmo funciona esta
pila.
Cuando una actividad se para porque se inicia una nueva actividad, se le notifica este
cambio de estado a travs de los mtodos de llamada callback del ciclo de vida de la
actividad.
Una funcin de llamada (en ingls callback) es una funcin que se remite a Android
cuando se inicia una Actividad, para que el sistema operativo la llame durante la ejecucin
de esta Actividad.
Existen varios mtodos de llamada callback que una actividad puede recibir debido a
un cambio en su estado; por ejemplo, cuando el sistema crea la Actividad, cuando se reactiva
o cuando se destruye. El programador puede aprovechar estos mtodos para ejecutar
sentencias especficas apropiadas para el cambio de estado. Por ejemplo, cuando una
Actividad se suspende es recomendable liberar de la memoria todos los objetos grandes.
Cuando la actividad se reanuda, se puede volver a reservar los recursos necesarios y
continuar con las acciones que se interrumpieron. Estos cambios de estado forman parte
del ciclo de vida de la actividad.
Para crear una Actividad debemos usar la clase Activity de Android. En la subclase
creada es necesario implementar los mtodos callback que el sistema puede invocar cuando
hay un cambio en su ciclo de vida: la actividad se est creando, se detiene, se reanuda o se
destruye. Los dos mtodos callback ms importantes son:
93
onCreate(): es obligatorio implementar este mtodo ya que el sistema lo invoca
cuando crea su actividad. Dentro de su implementacin debemos iniciar los
componentes esenciales de la actividad, como dibujar la interfaz de usuario
empleado la funcin setContentView().
Es posible utilizar otros mtodos de tipo callback para proporcionar una experiencia de
usuario fluida entre actividades y manejar el paso de una a otra. Lo veremos ms adelante en
este apartado.
Paused: la actividad es visible, pero hay otra actividad en primer plano que tiene el
foco. La actividad pausada sigue viva ya que se mantiene en memoria y conserva
toda la informacin de estado, si bien el sistema operativo puede eliminarla en caso
de memoria disponible muy baja.
Stopped: la actividad se oculta completamente por una nueva actividad (la actividad
anterior se ejecuta en "background"). Una actividad detenida tambin se mantiene
en memoria con toda la informacin de estado. Sin embargo, el usuario ya no la ve
visible y el sistema operativo puede eliminarla cuando se necesita memoria para otra
tarea.
Cuando una actividad cambia entre los diferentes estados descritos anteriormente, el
sistema operativo le notifica el cambio mediante diferentes mtodos callback. El programador
puede usar todos estos mtodos callback para ejecutar las rdenes apropiadas. El ejemplo
siguiente incluye la estructura de cada uno de estos mtodos fundamentales del ciclo de vida
de una Actividad:
94
Diseo del interfaz de usuario
@Override
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
@Override
super.onPause();
@Override
super.onRestart();
@Override
super.onResume();
@Override
super.onStart();
95
// Aqu deberamos leer los datos de la ltima sesin para seguir la
@Override
super.onDestroy();
@Override
super.onStop();
A continuacin, vamos a ver el ciclo de vida de una Actividad siguiendo sus estados y
los mtodos callback que desencadena cada uno de ellos:
El ciclo de vida de una Actividad ocurre entre las llamadas a los mtodos onCreate()
y OnDestroy(). En el primer mtodo la Actividad debe realizar la reserva de memoria,
el diseo de la interfaz de usuario y recuperar el estado de la sesin anterior. En el
segundo mtodo, hay que liberar todos los recursos usados con anterioridad.
El ciclo de vida "visible" de una Actividad ocurre entre las llamadas a los mtodos
OnStart() y OnStop(). Durante este tiempo el usuario puede ver e interactuar con la
pantalla de la Actividad. El sistema invoca el mtodo OnStop()cuando se inicia una
nueva actividad y la actual ya no est visible al usuario. Entre estos dos mtodos, el
programador debe definir y destruir respectivamente los recursos necesarios para
mostrar la Actividad para el usuario.
96
Diseo del interfaz de usuario
El ciclo de vida "en ejecucin" de una Actividad sucede entre las llamadas a los
mtodos OnResume() y OnPause(). Durante este tiempo la Actividad se ejecuta en
primer plano y tiene el foco del usuario. A menudo, el sistema operativo invoca estos
mtodos, por ejemplo, cuando el telfono se queda en modo espera o cuando
aparece una ventana con un mensaje de la aplicacin. Por esto, es conveniente incluir
en estos mtodos pocas y sencillas sentencias para que la aplicacin no se pare
cuando el usuario intenta hacer operaciones con ella.
97
Resumen de los mtodos callback del ciclo de vida de una Actividad
Siguiente
Mtodo Descripcin Kill
mtodo
98
Diseo del interfaz de usuario
Siguiente
Mtodo Descripcin Kill
mtodo
La columna "Kill" de esta tabla indica si el sistema puede matar (kill) el proceso de la
Actividad cuando finaliza la ejecucin del mtodo sin ejecutar ninguna sentencia ms de la
Actividad. Hay tres mtodos as: onPause(), onStop() y onDestroy().
Los mtodos que se han marcado con "No" en la columna "Kill" estn, a priori,
protegidos. El sistema operativo slo los "mata" en una situacin de inestabilidad con falta de
memoria.
En la Unidad 3 veremos cmo usar esos eventos para guardar el estado de una
Actividad y poder recuperarlo cuando sta se reinicia.
99
cdigo fuente y ejectalo para mostrar en el emulador una aplicacin en la que hemos
utilizado los mtodos del ciclo de vida de una Actividad.
En Android el programador puede capturar los eventos especficos del objeto Vista
(View) con la que el usuario interacta y ejecutar sentencias.
Por ejemplo, cuando el usuario toca con el dedo una Vista (por ejemplo, un botn), el
sistema operativo invoca el mtodo onTouchEvent() de ese objeto. Para interceptar este
mtodo deberamos extender (crear una nueva clase heredada del botn) la clase y reemplazar
el cdigo del mtodo de la clase original. Sin embargo, extender cada objeto Vista para
gestionar un evento no es prctico. Por esta razn, Android dispone en todas las clases View
de una coleccin de interfaces con funciones callback que se pueden utilizar con mucha ms
facilidad. Estas interfaces, que se denominan Escuchadores de eventos (Event listeners),
permiten controlar la interaccin del usuario con la interfaz de usuario.
Esto no quiere decir que no podamos extender una clase Vista para crear una clase
nueva que herede el comportamiento de la clase anterior y redefinir los eventos de la misma
directamente.
Un Event Listener es una interfaz de la clase Vista (View) que contiene un nico
mtodo de tipo callback. Android invoca estos mtodos cuando la Vista detecta que el usuario
est provocando un tipo concreto de interaccin con este elemento de la interfaz de usuario.
100
Diseo del interfaz de usuario
El siguiente ejemplo muestra cmo especificar los mtodos de los eventos sobre un
EditText:
texto.addTextChangedListener(new TextWatcher() {
int after) {
int count) {
101
}
diseo qu mtodo debe invocar Android cuando el usuario haga clic sobre
l -->
android:text="@string/calcular" android:onClick="miClickHandler"
android:layout_gravity="center_horizontal"
android:layout_width="104dp">
</Button>
switch (view.getId()) {
if (texto.getText().length() == 0) {
return;
102
Diseo del interfaz de usuario
if (kilometrosButton.isChecked()) {
} else {
break;
} // end switch
} // end miClickHandler
onLongClick(): este mtodo devuelve true para indicar que se han llevado a cabo
las operaciones necesarias para manejar el evento clic, por lo que ya no debe
lanzarse cualquier otro mtodo de tipo clic. En caso contrario, si el mtodo
devuelve el valor false, Android puede invocar a continuacin otro mtodo diferente
de tipo clic.
onKey(): este mtodo devuelve true o "false" para avisar si se han llevado a cabo
las operaciones necesarias para manejar el evento de teclado, por lo que ya no debe
lanzarse cualquier otro mtodo de tipo teclado.
onTouch(): en este mtodo ocurre como en los dos casos anteriores, segn devuelva
"true" o "false" para sealar si Android debe invocar los siguientes mtodos.
Recuerda que los eventos de tipo teclado siempre afectan a la Vista que est activa en
ese momento.
103
Desde Eclipse puedes abrir el proyecto Ejemplo 2 (Eventos) de la Unidad 2. Estudia el
cdigo fuente y ejectalo para mostrar en el emulador una aplicacin en la que hemos
utilizado varios mtodos de eventos de Android.
En otro apartado de esta Unidad vamos a explicar cmo crear un componente (Vista)
personalizado. En este caso es posible redefinir varios mtodos de tipo callback utilizados
como controladores (handlers) de eventos. A continuacin vamos a hacer un resumen de los
mtodos que podemos redefinir en una clase heredada de una Vista de Android:
104
Diseo del interfaz de usuario
Hay otros mtodos que, aunque no forman parte de la clase Vista, debes conocer, ya
que permiten gestionar eventos de otros componentes de Android. Son los siguientes:
Cuando un usuario interacciona con la interfaz de una aplicacin Android usando las
teclas del dispositivo o una bola de navegacin (trackball) es necesario marcar las Vistas como
activas (tienen el foco sobre ellas) colorendolas, para que el usuario sepa que puede
utilizarlas. Sin embargo, si el dispositivo tiene una pantalla tctil, el usuario puede interactuar
con la interfaz utilizando su propios dedos. En este ltimo caso, ya no es necesario resaltar las
Vistas. Este modo de interaccin se llama "modo tctil".
Cada vez que un usuario pulsa una tecla o desplaza la bola de navegacin (trackball),
el dispositivo sale del modo tctil y busca una Vista donde activar de nuevo el foco de la
aplicacin. As el usuario puede volver a interactuar con la interfaz de usuario sin tocar la
pantalla.
El estado de modo tctil se mantiene a lo largo de todo el sistema operativo (todas las
ventanas y actividades). Para consultar el estado actual, se puede usar el mtodo
isInTouchMode(). De esta forma se puede saber si el dispositivo se encuentra en modo tctil.
105
2.2.5 Controlando la Vista con el foco activo
El sistema Android cambia la Vista activa (con foco) en respuesta a la interaccin del
usuario. Las Vistas indican la posibilidad de recibir el foco a travs del mtodo isFocusable().
Para establecer si una Vista puede recibir el foco, hay que utilizar el mtodo setFocusable().
Como hemos dicho, en el modo tctil, podemos usar el mtodo isFocusableInTouchMode() y
establecer si una Vista puede recibir el foco con setFocusableInTouchMode().
El cambio de foco automtico que hace Android se basa en un algoritmo que busca la
Vista vecina ms cercana en una direccin. Normalmente, este algoritmo establecido por
defecto no es el esperado por el usuario. Cuando esto ocurre, es posible definir explcitamente
en el archivo de diseo res\layout\main.xml cmo se debe cambiar el foco con los siguientes
atributos: nextFocusDown, nextFocusLeft, nextFocusRight y nextFocusUp indicando el id
de la Vista al que se debe saltar. Por ejemplo:
<LinearLayout
android:orientation="vertical"
... >
<Button android:id="@+id/arriba"
android:nextFocusUp="@+id/abajo"
... />
<Button android:id="@+id/abajo"
android:nextFocusDown="@+id/arriba"
... />
</LinearLayout>
Por lo general, en el diseo vertical anterior, intentar ir hacia arriba desde el primer
botn no cambia el foco de la aplicacin. Lo mismo ocurre con el segundo botn al intentar ir
hacia abajo. Sin embargo, al haber definido en el botn superior el nextFocusUp para pasar al
segundo botn, el foco cambia al botn de abajo y viceversa.
Si queremos indicar que una Vista que por defecto no recibe el foco de la aplicacin
pueda recibirlo, podemos usar el atributo XML de la Vista android:focusable al diseo la
interfaz de usuario. Tambin podemos usar el atributo android:focusableInTouchMode
cuando se trate del modo tctil.
Tambin desde el cdigo Java podemos usar el mtodo requestFocus() para indicar
al sistema que una Vista debe tener el foco.
Para poder disear y probar aplicaciones con mayor facilidad, el emulador de Android
utiliza dispositivos virtuales (AVD: Android Virtual Device). Estos AVDs permiten establecer
algunos aspectos del hardware (memoria) y software (versin de Android) del telfono virtual.
En la Unidad 1 ya hemos visto cmo se inicia este emulador de Android; ahora vamos
a estudiar cmo se utiliza.
107
NOTA: es importante que el dispositivo virtual se encuentre configurado en el idioma Espaol
para que todos los ejemplos del curso funcionen correctamente. Se explica cmo hacerlo en
el apartado Cmo crear un proyecto Android de la Unidad 1.
Cuando accedemos por primera vez al emulador, aparece la pantalla de bienvenida del
terminal bloqueado:
Para desbloquear la pantalla hay que arrastrar con el ratn el icono "candado" hacia la
derecha.
La tabla siguiente resume las relaciones entre las teclas del emulador y las teclas del
teclado de tu ordenador:
108
Diseo del interfaz de usuario
principal de Android.
Muestra un men
desplegable con las
F2 o RePg
Men (tecla izquierda) opciones de la pantalla
actual.
Buscar informacin.
F5
Buscar
F7
Botn encender/apagar
BLOQUE_NUM_MAS (+),
Subir volumen Ctrl+5
BLOQUE_NUM_MENOS (-)
Bajar volumen Ctrl+F6
Habilita y deshabilita la
conexin de datos del
F8 mvil.
Conexin datos
El emulador ocupa la
Pantalla completa Alt+Intro pantalla completa del
monitor del ordenador.
Inicia el modo de
F6 navegacin con bola
Bola navegacin
(trackball)
109
Arrastrar izq/arriba/dcha/abajo BLOQUE_NUM_4/8/6/2
Ten en cuenta que para usar las teclas del bloque numrico es necesario desactivar el
bloqueo numrico en tu ordenador.
NOTA: es recomendable familiarizarse con el emulador de Android usando las funciones del
dispositivo virtual e ir visitando las diferentes opciones del mismo as como utilizar los atajos
de teclado de la tabla anterior.
Para que se puedan introducir tildes en las cajas de texto de las aplicaciones es muy
importante que el dispositivo virtual se encuentre configurado en el idioma Espaol. En el
apartado Cmo crear un proyecto Android de la Unidad 1 se explica cmo hacerlo.
Para que podamos introducir tildes en las cajas de texto, hay que pulsar un rato con el
ratn en la vocal correspondiente:
Pulsar un rato
110
Diseo del interfaz de usuario
Pulsar un rato
Atencin: tambin es posible pulsar un rato en la tecla de la vocal del teclado del ordenador
donde ests trabajando para poder escribir la tilde correspondiente.
Es posible cambiar el tamao de la ventana de AVD del Emulador para que se vea
correctamente en la pantalla de tu ordenador. Para ellos hacemos clic en el men de Eclipse
Android SDK and AVD Manager:
111
Despus, hacemos clic en el botn Start:
En esta ventana podemos ver la escala (Scale) que se aplicar a la ventana del
emulador (un nmero entre 0,1 y 3). Para ello, podemos especificar la densidad del monitor del
ordenador en puntos por pulgada (Monitor DPI) y el tamao en pulgadas de la pantalla del
dispositivo Android (Screen Size).
Aunque el Emulador tiene muchas posibles opciones configurables para simular casi
todos los casos posibles de un dispositivo real, vamos a describir las que son ms
importantes o usaremos ms adelante en el curso:
112
Diseo del interfaz de usuario
Para acceder a esta herramienta en Eclipse hay que hacer clic en la opcin del men
principal: Window -> Open Perspective -> DDMS.
113
Nota: no todas las opciones posibles del Emulador estn incluidas en la ventana anterior;
algunas deben configurarse mediante rdenes en la lnea de comandos del sistema operativo
de tu ordenador.
En la Unidad 8 veremos cmo usar esta ventana para depurar aplicaciones Android.
Este apartado est dedicado a los componentes de tipo seleccin y vamos a describir
un elemento importante y comn a todos ellos: los adaptadores.
Un Adaptador (Adapter) es un objeto que permite definir el modelo de datos que usan
todos los componentes de seleccin de forma unificada. Es decir, todos los componentes de
seleccin acceden a los datos que contienen a travs de un adaptador.
114
Diseo del interfaz de usuario
// Adaptador que usamos para indicar al Spinner dnde obtiene las opciones
ArrayAdapter<String> adaptador =
new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, datos);
Ejemplo
<Spinner android:id="@+id/ListadoOpciones"
android:prompt="@string/selecciona"
android:drawSelectorOnTop="true"
115
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
Para enlazar el adaptador que define las opciones de este tipo de listado y tratar el
evento que ocurre cuando el usuario selecciona una de las opciones disponibles, escribimos
las siguientes sentencias:
adaptador.setDropDownViewResource(
android.R.layout.simple_spinner_dropdown_item);
listadoOpciones.setAdapter(adaptador);
listadoOpciones.setOnItemSelectedListener(
new AdapterView.OnItemSelectedListener() {
@Override
resultado.setText("");
});
116
Diseo del interfaz de usuario
Para cambiar el aspecto de las opciones de la lista emergente hay que usar el mtodo
setDropDownViewResource(ID_layout). En este caso hemos utilizado el diseo predefinido
de Android para las listas desplegables android.R.layout.simple_spinner_dropdown_item.
Esto provoca que el diseo de la seleccin y el listado desplegable sean diferentes (fjate en el
cambio de color):
117
Lista de seleccin (List View)
La Lista de seleccin (o ListView en ingls) permite al usuario hacer clic sobre una lista
de opciones seleccionables. Estas opciones se muestran directamente sobre el propio
componente; por lo tanto, no se trata de una lista emergente como el Spinner. Si no se
visualizan todas las opciones disponibles en la pantalla del dispositivo porque no caben, se
puede desplazar el listado con el dedo o los botones de navegacin.
En el ejemplo siguiente vamos a usar un Adaptador particularizado para que dibuje las
opciones del men personalizadas.
Fjate en el cdigo del siguiente ejemplo que define un ListView en el fichero main.xml
de layout de la aplicacin:
Ejemplo
<ListView android:id="@+id/ListaOpciones"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Para enlazar el adaptador que define las opciones de este tipo de listado y tratar el
evento que ocurre cuando el usuario selecciona una de las opciones disponibles, escribimos
las siguientes sentencias:
...
listaOpciones.setAdapter(adaptador);
118
Diseo del interfaz de usuario
listaOpciones.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
});
Activity contexto;
AdaptadorOpciones(Activity contexto) {
this.contexto = contexto;
119
VistaTag vistaTag;
If (item == null)
vistaTag.titulo = (TextView)item.findViewById(R.id.LblTitulo);
vistaTag.subtitulo =
(TextView)item.findViewById(R.id.LblSubTitulo);
item.setTag(vistaTag);
else
vistaTag = (VistaTag)item.getTag();
vistaTag.titulo.setText(datos[position].getTitulo());
vistaTag.subtitulo.setText(datos[position].getSubtitulo());
return(item);
120
Diseo del interfaz de usuario
Lo primero que debe hacer este mtodo es inflar el diseo layout XML que hemos
definido en el fichero res\layout\listitem_opcion.xml. Para ello, hemos utilizando la clase
LayoutInflater, que crea la estructura de diseo de los objetos mediante el mtodo
inflate(id_layout). Cada vez que es necesario mostrar un elemento de la lista en la pantalla,
Android invoca este mtodo para disearlo, incluso cuando ya se ha mostrado el elemento y
se ha ocultado en la pantalla al desplazar la lista.
Esto produce que, dependiendo del tamao de la lista y de la complejidad del layout,
se creen y destruyan muchos objetos aumentando el uso de la CPU y dela memoria y, al final,
provocando un mayor consumo de batera.
Sin embargo, Android permite reutilizar una Vista que ya hayamos inflado con
anterioridad y que ya no haga falta por algn motivo; por ejemplo, porque el elemento
correspondiente de la lista haya desaparecido de la pantalla al desplazar el listado. As, se
crean nicamente los objetos necesarios que se pueden visualizar en la pantalla del telfono.
Adems, todos los componentes de Android tienen una propiedad denominada Tag,
que puede almacenar dentro cualquier tipo de objeto. Para asignar y recuperar el objeto
almacenado hay que emplear los mtodos setTag() y getTag() respectivamente.
Esta facilidad de los objetos de Android permite que almacenemos dentro cada Vista
convertView las etiquetas que dibujan el diseo de la opcin del listado. Para ello, vamos a
definir una nueva clase VistaTag donde almacenamos las etiquetas TextView que hemos
inflado para dicho elemento, de forma que, posteriormente, podamos recuperarlo
fcilmente y cambiar su contenido.
Por lo tanto, la clase VistaTag slo contiene una referencia a cada uno de los
componentes del diseo que hay que manipular, en este caso, las dos etiquetas de texto.
Definimos esta clase de la siguiente forma:
TextView titulo;
TextView subtitulo;
121
Desde Eclipse puedes abrir el proyecto Ejemplo 4 (Lista de seleccin) de la Unidad 2.
Estudia el cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa anterior,
en el que hemos utilizado el componente ListView.
Ejemplo
<GridView android:id="@+id/GridOpciones"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:numColumns="auto_fit"
android:columnWidth="80px"
android:horizontalSpacing="5px"
android:verticalSpacing="10px"
android:stretchMode="columnWidth" />
Como en los anteriores ejemplos, para enlazar el adaptador que define las opciones de
este tipo de listado escribimos las siguientes sentencias:
122
Diseo del interfaz de usuario
ArrayAdapter<String> adaptador =
gridOpciones.setAdapter(adaptador);
Ejemplo
<AutoCompleteTextView android:id="@+id/miautocomplete"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:completionThreshold="1"
/>
Como en los anteriores ejemplos, para enlazar el adaptador que define las opciones de
este tipo de listado escribimos las siguientes sentencias:
123
// Debemos implemetar los mtodo de TextWatcher para poder detectar los
// eventos de abajo
@Override
String meses[]={
...
miAutoComplete =
(AutoCompleteTextView)findViewById(R.id.miautocomplete);
miAutoComplete.addTextChangedListener(this);
miAutoComplete.setAdapter(adaptador);
@Override
int after) {
@Override
124
Diseo del interfaz de usuario
@Override
Una Actividad con lista de seleccin (en ingls, ListActivity) es una actividad heredada
de la clase Activity que ya incluye el componente de seleccin ListView.
Ejemplo
<ListView
android:id="@+id/android:list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
125
<TextView android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF0000"
android:text="No hay datos"/>
Para cambiar el diseo de las opciones del ListView interno de la actividad ListActivity
hay que definir un nuevo fichero XML de diseo dentro de la carpeta res\layout. Por ejemplo,
el fichero se puede llamar fila.xml y contener el siguiente cdigo:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView android:id="@+id/texto"
android:textSize="16sp"
android:textStyle="bold"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
El cdigo anterior crea un diseo de una etiqueta en negrita para definir cada opcin
del listado.
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
126
Diseo del interfaz de usuario
R.id.texto, nombres));
}
}
Hasta ahora en el curso hemos estudiado los componentes bsicos que proporciona
Android. Usando estos componentes podemos disear interfaces de usuario. A veces, la
funcionalidad de la aplicacin requiere emplear componentes diseados por el programador.
127
EditText (cuadro de texto) para que muestre el nmero de caracteres que contiene a medida
que se escribe en l.
Se trata de simular un editor de mensajes cortos SMS del propio sistema operativo que
nos avisa del nmero de caracteres que contiene el mensaje. En nuestro caso, como resultado
obtendremos un componente como el que se muestra en la siguiente imagen:
Lo primero que hay que hacer es crear una nueva clase Java que extienda el
componente que utilizamos de partida como base, en este caso EditText. El cdigo de esta
nueva subclase es ste:
pinceles();
super(context, attrs);
128
Diseo del interfaz de usuario
pinceles();
super(context);
pinceles();
// Funcin que inicia las pinceles que usamos para pintar el cuadradito negro
pNegro.setColor(Color.BLACK);
pNegro.setStyle(Style.FILL);
pBlanco.setColor(Color.WHITE);
// Para modificar el aspecto del EditText hay que reescribir este mtodo
@Override
super.onDraw(canvas);
pNegro);
canvas.drawText("" + this.getText().toString().length(),
this.getWidth()-28, 17, pBlanco);
129
que es el lienzo sobre el que podemos dibujar todos los elementos extra necesarios en el
componente.
La clase Canvas proporciona varios mtodos para dibujar lneas, rectngulos, elipses,
texto, imgenes, etctera, sobre el espacio ocupado por el componente. En este caso
nicamente vamos a dibujar un rectngulo que sirve de fondo para el contador y el texto con
el nmero de caracteres actual que ha escrito el usuario.
Para dibujar el grfico es necesario definir dos pinceles (clase Paint). El primero
permite pintar de color negro y con relleno slido, y el segundo pinta de color blanco. Como
slo es necesario crear estos pinceles una vez, los hemos definido como atributos de la clase
y los inicializamos en los tres constructores del componente.
Para aadir el nuevo componente a la interfaz de nuestra aplicacin hay que incluirlo en
fichero res\layout\main.xml que define el diseo de la ventana como cualquier otro
componente, teniendo en cuenta que debemos hacer referencia a l con el nombre completo
de la nueva clase creada: es.mentor.unidad2.eje5.edittextext.EditTextExtendido. El fichero
tiene este aspecto:
<es.mentor.unidad2.eje5.edittextext.EditTextExtendido
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dip"
android:layout_weight="0.25"/>
130
Diseo del interfaz de usuario
Lo primero que hemos hecho es disear la interfaz del componente compuesto a partir
de componentes estndar de Android: etiquetas, cuadros de texto y un botn. Para ello,
definimos un nuevo layout XML en la carpeta \res\layout con el nombre
componente_login.xml. En este fichero vamos a establecer la estructura tpica de una
pantalla que muestra una ventana de login. El fichero es el siguiente:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" android:padding="10dip">
<EditText android:id="@+id/TextoUsuario"
android:layout_height="wrap_content"
android:layout_width="fill_parent" />
android:layout_height="wrap_content" android:text="Contrasea:"
android:textStyle="bold" />
<EditText android:id="@+id/TextoPassword"
android:layout_height="wrap_content"
android:layout_width="fill_parent" />
android:layout_gravity="center_horizontal"
android:layout_height="wrap_content"
android:text="Entrar" android:paddingRight="20dip"
android:layout_width="104dp"></Button>
android:layout_height="wrap_content" android:paddingLeft="10dip"
android:textStyle="bold" />
</LinearLayout>
131
A continuacin, creamos la clase Java asociada a este componente compuesto donde
se define toda su funcionalidad. Como el diseo est basado en la clase LinearLayout, el
nuevo componente debe heredar tambin esta clase Java de Android. Redefiniremos adems
los dos constructores bsicos:
super(context);
inicializar();
super(context, attrs);
inicializar();
a = getContext().obtainStyledAttributes(attrs,
R.styleable.ComponenteLogin);
botonLogin.setText(textoBoton);
// Liberamos memoria
a.recycle();
132
Diseo del interfaz de usuario
LayoutInflater li =
(LayoutInflater)getContext().getSystemService(infService);
internos textoUsuario =
(EditText)findViewById(R.id.TextoUsuario); textoPassword =
(EditText)findViewById(R.id.TextoPassword); botonLogin =
(Button)findViewById(R.id.BotonAceptar); labelMensaje =
(TextView)findViewById(R.id.LabelMensaje);
asignarEventos();
listener = l;
botonLogin.setOnClickListener(new OnClickListener()
@Override
133
listener.onLogin(textoUsuario.getText().toString(),
textoPassword.getText().toString());
});
labelMensaje.setText(msg);
134
Diseo del interfaz de usuario
compLogin.setOnLoginListener(new OnLoginListener()
@Override
else
});
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sgo="http://schemas.android.com/apk/res/es.mentor.unidad2.eje6.logincomp
uesto"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dip" >
<es.mentor.unidad2.eje6.logincompuesto.ComponenteLogin
android:id="@+id/ComponenteLogin"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#0000AA"
135
sgo:texto_boton="Entrar" />
</LinearLayout>
<resources>
<declare-styleable name="ComponenteLogin">
</declare-styleable>
</resources>
136
Diseo del interfaz de usuario
137
Una Actividad (Activity) es un componente de Android que ofrece una pantalla
con la que los usuarios pueden interactuar en una aplicacin.
Una funcin de llamada (en ingls callback) es una funcin que se remite a Android
cuando se inicia una Actividad para que el sistema operativo la llame durante la
ejecucin de esta Actividad.
Un Event Listener es una interfaz de la clase Vista (View) que contiene un nico
mtodo de tipo callback que Android invoca cuando detecta que el usuario est
provocando un tipo concreto de interaccin con este elemento de la interfaz de
usuario.
138
Diseo del interfaz de usuario
139
140
Unidad de Aprendizaje 9
EMS
FECTOS DE TRANSICIN
INFORMACIN SOBREY
ANIMACIN
ANDROID
NDICE
3.1 INTRODUCCIN ................................................................143
3.1.1 Introduccin ...................................................................143
3.1.2 Gestin del botn Hacia atrs de Android .............143
3.1.3 Definicin de una tarea en los proyectos Android...145
3.1 INTRODUCCIN
3.1.1 Introduccin
Una Actividad tambin puede arrancar las actividades que definen otras
aplicaciones. Por ejemplo, si una aplicacin tiene que enviar un correo electrnico, puede usar
el objeto Intencin (en ingls, Intent) de Android para realizar el envo incluyendo alguna
informacin, como la direccin de correo electrnico del destinatario y un mensaje.
Para que se pueda hacer esto, hay que declarar la Actividad que enva mensajes para
que admita este tipo de intenciones. En este caso, la intencin es enviar un correo
electrnico, por lo que la aplicacin disponible para crear correos electrnicos se inicia (si
hubiera varias Actividades que tuvieran la misma intencin, el sistema permite al usuario
seleccionar la que desea utilizar). Finalmente, cuando se enva el mensaje, la actividad inicial
se reanuda y parece que la Actividad de correo electrnico forma parte de la aplicacin
general. A pesar de que las actividades pueden pertenecer a diferentes aplicaciones, Android
mantiene la sensacin de que se trata de una nica aplicacin juntndolas en una sola Tarea
(Task).
Una Tarea es un conjunto de actividades con las que un usuario interacta. Android
organiza las actividades en una pila de ejecucin (en ingls stack) donde se van apilando las
actividades que el usuario va invocando. Cada Tarea tiene asociada su propia pila de
ejecucin independiente.
143
Cuando el usuario toca un icono de esta pantalla, se inicia una tarea asociada a la
aplicacin. Si es la primera vez que arrancamos la aplicacin, el sistema operativo crea una
nueva tarea y su Actividad principal ("main") queda almacenada en primer lugar en la pila de
Android.
Cuando esta Actividad principal arranca otra, la nueva Actividad se aade a la parte
superior de la pila y pasa a primer plano. La Actividad anterior permanece en la pila y se
detiene manteniendo el estado actual de la interfaz de usuario. Si el usuario pulsa la tecla de
144
Ms informacin sobre Android
Si el usuario contina presionando "Volver atrs", se extraer una a una cada actividad
de la pila mostrando la anterior, hasta que aparezca la pantalla de inicio u otra aplicacin que
se haya iniciado antes. Cuando esto ocurre, la tarea se destruye.
Una tarea es una unidad compuesta de actividades que puede pasar a un segundo
plano cuando los usuarios inician una nueva tarea o van a la pantalla de inicio. Cuando una
tarea se encuentra en segundo plano, todas sus actividades se detienen, aunque su pila de
ejecucin se mantiene intacta hasta que el usuario decida que vuelva al "primer plano".
Nota: es posible mantener muchas tareas en segundo plano a la vez; sin embargo, si el
sistema operativo necesita memoria, puede destruirlas perdiendo sus estados de ejecucin.
<activity android:name=".BienvenidoActivity"
android:label="@string/app_name">
<intent-filter>
</intent-filter>
</activity>
</application>
145
Nota: debido a la nomenclatura de Android, es obligatorio escribir un punto al principio del
nombre.
Un punto de entrada hace que Android cree un icono y una etiqueta en la pantalla de
inicio para la actividad correspondiente, ofreciendo a los usuarios una forma de iniciar la
actividad.
146
Ms informacin sobre Android
Por esta razn, es muy importante marcar la actividad principal con la categora
"android.intent.category.LAUNCHER", para que el usuario pueda volver a una aplicacin
que se encuentre en segundo plano y que ya no es visible.
En este apartado vamos a describir lo que hace Android cuando una Actividad se
destruye o se detiene, cmo se conserva su estado en la memoria del sistema operativo,
incluyendo todos los cambios que haya hecho el usuario en la interfaz introduciendo
informacin, seleccionando opciones, etctera.
En el siguiente esquema se muestra los eventos y estados por los que pasa una
Actividad cuando se detiene o se destruye.
147
Como se puede ver en el esquema anterior, una Actividad recupera su estado de
ejecucin si se detiene y vuelve a primer plano; o si se destruye y se vuelve a crear.
El sistema llama a este mtodo justo antes de que la Actividad se destruya y le pasa
como parmetro un objeto de tipo Bundle. Este objeto Bundle es donde podemos almacenar
la informacin del estado de la Actividad como pareja nombre-valor, utilizando el mtodo
putString(). De esta manera, si Android mata el proceso de la Actividad y el usuario vuelve a
ejecutar la misma Actividad, el sistema pasa el objeto Bundle almacenado anteriormente
como parmetro en el mtodo onCreate(), para que pueda restaurar el estado de ejecucin. Si
no hay informacin sobre este estado, el objeto Bundle es nulo.
148
Ms informacin sobre Android
super.onCreate(estadoAlmacenado);
Muy importante: debido a que Android no garantiza que siempre se invoque el mtodo
onSaveInstanceState(), debe usarse nicamente para almacenar el estado transitorio de la
Actividad, es decir, la interfaz de usuario. Nunca debe emplearse para almacenar datos
persistentes con preferencias del usuario o datos de la aplicacin. En la Unidad 4 veremos
cmo se implementa esta funcionalidad.
<LinearLayout android:id="@+id/mainLayout"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
149
<TextView android:id="@+id/nombre" android:layout_width="wrap_content"
android:layout_marginLeft="3dip" android:layout_marginTop="10dip">
</TextView>
<EditText android:id="@+id/nombreEditText"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:textSize="18sp" android:layout_marginLeft="3dip"
android:layout_marginRight="3dip" android:layout_marginTop="3dip">
</EditText>
android:layout_height="wrap_content"
android:layout_marginLeft="3dip" android:layout_marginTop="10dip">
</TextView>
<EditText android:id="@+id/passwordEditText"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:textSize="18sp" android:layout_marginLeft="3dip"
android:layout_marginRight="3dip" android:layout_marginTop="3dip"
android:saveEnabled="false">
</EditText>
</LinearLayout>
150
Ms informacin sobre Android
@Override
super.onCreate(estadoAlmacenado);
setContentView(R.layout.main);
if (estadoAlmacenado != null) {
@Override
super.onSaveInstanceState(estado);
Toast.LENGTH_LONG).show();
@Override
super.onDestroy();
En el cdigo anterior hemos usado los mtodos putString() y getString() del objeto
Bundle para almacenar el contenido de una variable extra en el estado de la Actividad.
151
Desde Eclipse puedes abrir el proyecto Ejemplo 1 (Guardar estado de una actividad) de la
Unidad 3. Estudia el cdigo fuente y ejectalo para mostrar en el emulador una aplicacin en
la que guardamos el estado de ejecucin de la Actividad
A continuacin, introduce algn texto en los campos que aparecen. Despus, haz clic
Si volvemos a ejecutar la aplicacin, veremos que podemos ver tanto los datos tanto
de la caja de texto usuario como de la contrasea. Esto ocurre porque Android no ha
destruido la Actividad.
152
Ms informacin sobre Android
Para que Android pare (kill) la Actividad cuando la dejamos, hay que configurar las
herramientas de desarrollo del dispositivo conocidas como "Dev-Tools". Para acceder a
estas herramientas hay que hacer clic en el icono "Dev Tools" que aparece en el lanzador de
aplicaciones. Despus, hacemos clic en la opcin "Development Settings" y, finalmente,
marcamos la opcin "Immediately destroy activities":
Si ahora ejecutas de nuevo la aplicacin y pulsas otra vez el botn "INICIO", vers el
mensaje "El sistema ha terminado la Actividad", que indica que la aplicacin se destruye:
153
3.3 PROCESOS EN HILOS EN ANDROID
Un hilo es una caracterstica de la informtica que permite a una aplicacin realizar
varias tareas a la vez (concurrentemente). Esencialmente, un hilo es una tarea que se ejecuta
en paralelo con otra tarea. Todos los hilos que se ejecutan a la vez pueden compartir una
serie de recursos, tales como la memoria, los archivos abiertos, etctera. Esta tcnica permite
simplificar el diseo de una aplicacin que puede as llevar a cabo distintas funciones
simultneamente.
Si se inicia una Actividad de una aplicacin que ya se est ejecutando y, por lo tanto,
ya existe un proceso asociado, entonces la nueva Actividad se inicia dentro de ese proceso y
utiliza el mismo hilo de ejecucin. Sin embargo, es posible disear aplicaciones que ejecuten
sus diferentes componentes de la aplicacin en procesos separados y el programador puede
crear subprocesos adicionales para cualquier proceso principal.
En este apartado vamos a estudiar cmo funcionan los procesos e hilos en una
aplicacin de Android.
3.3.1 Procesos
Ya hemos comentado que, por defecto, todos los componentes de una misma
aplicacin se ejecutan en el mismo proceso. La mayora de las aplicaciones deben ejecutarse
de esta manera. Sin embargo, a veces, es necesario controlar en qu proceso se ejecuta un
componente en particular. Para hacer esto, debemos modificar el archivo
AndroidManifest.xml del proyecto de Android.
154
Ms informacin sobre Android
Cuando el sistema Android necesita matar algn proceso para liberar memoria para
otro, tiene en cuenta su importancia en relacin con el usuario. Por ejemplo, se termina antes
un proceso que alberga actividades que ya no son visibles en la pantalla que otro donde sus
actividades son visibles. Por lo tanto, la decisin de terminar un proceso depende del estado
de los componentes que se ejecutan en ese proceso. Las reglas que emplea Android para
acabar con un proceso se discuten a continuacin.
155
3. Proceso de servicio: es un proceso que ejecuta un servicio que se ha iniciado con
el mtodo startService() y no corresponde a ninguna de las dos categoras
anteriores. Aunque los procesos de servicio no estn relacionados directamente
con lo que ve el usuario, por lo general, desempean tareas importantes para el
usuario, como reproducir msica o descargar de datos de Internet.
Hemos visto que un proceso que ejecuta un Servicio se clasifica con mayor prioridad
que un proceso con una Actividad en segundo plano. Si una actividad inicia una operacin que
dura mucho, es recomendable crear un servicio para esa operacin, en lugar de crear un
subproceso, especialmente si la operacin va a durar ms que la propia actividad. Por
ejemplo, si una actividad tiene que subir una foto a un sitio Web, es mejor iniciar un servicio
para llevar a cabo esta tarea, as la carga pueda continuar en segundo plano incluso si el
usuario sale de la actividad. El uso de servicios garantiza de que la operacin tendr, por lo
menos, la prioridad de un proceso de servicio, independientemente de lo que ocurra con la
actividad.
Por esta misma razn, los receptores de mensajes (broadcast receivers) deben
emplear servicios en lugar de tareas con un hilo de ejecucin.
156
Ms informacin sobre Android
Cuando el usuario ejecuta una aplicacin, Android crea un hilo de ejecucin que se
denomina principal (main). Este hilo es muy importante porque es el encargado de gestionar
los eventos que dispara el usuario a los componentes adecuados e incluye tambin los
eventos que dibujan la pantalla. Por esta razn, a este hilo principal tambin se le llama hilo de
la interfaz de usuario (UI thread).
Si un usuario interacciona mucho con una aplicacin, este modelo de ejecucin con un
nico hilo puede dar lugar a poca fluidez en ella a menos que implementemos adecuadamente
la aplicacin. Es decir, como todo lo que ejecuta la aplicacin se hace en un nico hilo
principal, llevar a cabo operaciones que van a tardar cierto tiempo, como acceder a Internet o
consultar una base de datos, bloquearn la interfaz de usuario. Cuando el hilo principal est
bloqueado la pantalla de aplicacin se bloquea (ni siquiera se dibuja) y, desde el punto de vista
del usuario, la aplicacin se bloquea. Adems, si el hilo principal est bloqueado durante 5
segundos, Android muestra al usuario una ventana con el mensaje "La aplicacin no
responde" (en ingls, ANR: Application Not Responding):
157
datos a los que accede dicha funcin (o mtodo) sean corrompidos por alguno de los hilos ya
que se asegura la atomicidad de la operacin, es decir, la funcin se ejecuta de forma
serializada sin interrupciones
A primera vista, este cdigo parece funcionar bien ya que crea un nuevo hilo en
segundo plano para descargar la imagen. Sin embargo, no cumple la segunda regla del
modelo de un hilo nico: no acceder a la interfaz de usuario de Android desde un hilo
exterior a esta interfaz de usuario. Esto puede producir un comportamiento inesperado en la
aplicacin y muy difcil de localizar.
Para solucionar este inconveniente, Android ofrece varias formas de acceder al hilo de
la interfaz de usuario desde otros hilos en segundo plano. A continuacin se muestra la lista de
estos mtodos:
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
158
Ms informacin sobre Android
Para usar esta funcionalidad de Android hay que extender la clase AsyncTask e
implementar los siguientes mtodos callback:
159
// Mtodo asociado al botn "Descargar"
imagen.setVisibility(View.INVISIBLE);
cargando.setVisibility(View.VISIBLE);
tarea.execute(imagenURL);
// Clase que descarga una imagen de Internet como una tarea asncrona.
return loadImageFromNetwork(urls[0]);
cargando.setVisibility(View.INVISIBLE);
if (resultado!=null) imagen.setImageBitmap(resultado);
else imagen.setImageResource(R.drawable.error);
imagen.setVisibility(View.VISIBLE);
} // end onPostExecute
}
El primer parmetro (String) define el tipo de variable que usamos como parmetro al
invocar doInBackground(). El segundo parmetro (Void) define el tipo de variable que
pasamos como parmetro al invocar onProgressUpdate(). Finalmente, el ltimo parmetro
(Bitmap) define el tipo de variable que pasamos como parmetro al invocar onPostExecute()
160
Ms informacin sobre Android
Aunque ms adelante en el curso veremos cmo se indican los permisos que necesita
una aplicacin para ejecutarse, para que este ejemplo funcione y se descargue una imagen de
Internet es necesario indicar el permiso en el fichero AndroidManifest.xml de la aplicacin as:
<uses-permission android:name="android.permission.INTERNET">
</uses-permission>
Es recomendable que el alumno o alumna lea el manual de la clase AsyncTask para conocer
ms funcionalidad, entre ella se encuentra:
Para entender mejor esta aplicacin que usa un proceso en segundo plano, inicia este
ejemplo en el emulador. Vers la pantalla siguiente:
161
Si pulsas en el botn "Descargar" comprobars que puedes seguir usando el resto de
Vistas de la pantalla mientras se descarga la imagen.
Adems, es posible acceder a un hilo en segundo plano desde el proceso de otra aplicacin,
para ello hay que desarrollar este hilo de manera thread-safe.
Mens Principales: son los usados con ms frecuencia. Aparecen en la zona inferior
Submens: son los mens secundarios que aparecen al elegir una opcin de un men
principal.
Mens Contextuales: son muy tiles y se muestran al realizar una pulsacin larga
sobre algn elemento de la pantalla. Es el equivalente al botn derecho del ratn en un
PC.
Como es habitual en Android, existen dos formas de crear un men en una aplicacin
Android: definiendo el men en un fichero XML e "inflndolo" despus o creando el men
directamente mediante cdigo Java. En este apartado veremos ambas formas.
Veamos en primer lugar cmo crear un men principal con un submen a partir de su
diseo en XML. Estos ficheros XML con el diseo del men se deben guardar en la carpeta
res\menu del proyecto y tienen una estructura de este tipo (archivo menu_principal.xml):
162
Ms informacin sobre Android
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
android:icon="@drawable/menu_estrella"></item>
android:icon="@drawable/menu_brujula"></item>
android:icon="@drawable/menu_direccion">
<menu>
<item android:id="@+id/SubMenuOp1"
<item android:id="@+id/SubMenuOp2"
</menu>
</item>
</menu>
Podemos ver en el cdigo anterior que la estructura bsica del diseo del men es
muy sencilla. Aparece un elemento principal <menu> que contiene los elementos <item> que
corresponden con las diferentes opciones del men.
De igual forma que otros archivos XML de un proyecto Android, podemos editarlo
visualmente haciendo clic en la pestaa Layout del archivo que define el men:
163
Una vez definido el men en el fichero XML, hay que implementar el mtodo
onCreateOptionsMenu() de la Actividad para que se cree en la pantalla. En este mtodo
debemos inflar el men de forma parecida a como ya hemos hecho con otro tipo de
componentes layouts. Primero obtenemos una referencia al objeto "inflador" mediante el
mtodo getMenuInflater() y, despus, generamos la estructura del men usando el mtodo
inflate() y pasndole como parmetro el ID del archivo XML de diseo del men. Finalmente,
el mtodo debe devolver el valor true para indicar a la Actividad que debe mostrar el men.
@Override
inflater.inflate(R.menu.menu_principal, menu);
return true;
164
Ms informacin sobre Android
Veamos cmo queda el cdigo utilizando esta otra forma de implementarlo que genera
un men exactamente igual al del ejemplo anterior:
...
@Override
return true;
}
Una vez construido el men, es necesario implementar las sentencias que se ejecutan
cuando el usuario selecciona una de las opciones, Para ello, usamos el evento
165
onOptionsItemSelected() de la Actividad. Este evento recibe como parmetro el elemento de
men (MenuItem) que ha sido elegido por el usuario y cuyo ID podemos obtener con el
mtodo getItemId(). En funcin de este ID podemos saber qu opcin ha sido pulsada y
ejecutar unas sentencias u otras. En nuestro ejemplo, lo nico que hacemos es modificar el
texto de la etiqueta labelResultado que hemos colocado en la pantalla principal de la
aplicacin:
switch (item.getItemId()) {
case R.id.MenuOp1:
return true;
case R.id.MenuOp2:
return true;
case R.id.MenuOp3:
return true;
case R.id.SubMenuOp1:
return true;
case R.id.SubMenuOp2:
return true;
default:
return super.onOptionsItemSelected(item);
} // end onOptionsItemSelected
Desde Eclipse puedes abrir el proyecto Ejemplo 4 (Mens) de la Unidad 3. Estudia el cdigo
fuente y ejectalo para mostrar en el emulador una aplicacin en la que usamos un men
principal y un submen,
166
Ms informacin sobre Android
Para ver cmo funciona esta aplicacin que usa un men y un submen inicia este ejemplo en
el emulador. Vers la pantalla siguiente:
La creacin y utilizacin de este tipo de mens contextuales son muy parecidas a las
de los mens y submens bsicos que hemos visto anteriormente, aunque presentan algunas
particularidades que vamos a tratar a continuacin.
Vamos a partir del ejemplo 4 de esta Unidad, al que vamos a aadir un men
contextual que aparece al pulsar sobre la etiqueta de texto donde mostramos la opcin
seleccionada y un ListView con elementos sobre los que pulsar seguidamente y mostrar
opciones de edicin.
167
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
labelResultado = (TextView)findViewById(R.id.labelResultado);
listadoPrincipal = (ListView)findViewById(R.id.ListadoPrincipal);
listadoPrincipal.setAdapter(adaptador);
registerForContextMenu(labelResultado);
registerForContextMenu(listadoPrincipal);
} // end onCreate
A continuacin, de igual forma que hicimos con los mens bsicos para crear las
opciones disponibles con el mtodo onCreateOptionsMenu(), vamos a construir los mens
contextuales asociados a los diferentes componentes de la aplicacin con el mtodo
onCreateContextMenu(). A diferencia del mtodo onCreateOptionsMenu() Android invoca
este mtodo cada vez que es necesario mostrar un men contextual. Este mtodo lo
implementaremos de misma forma que los mens bsicos, inflndolo con un archivo de
diseo XML o crendolo con sentencias Java. En este ejemplo hemos decidido disear los
mens en XML. El men contextual que aparece en la etiqueta se define en el fichero
menu_context_etiqueta.xml:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/ContextLabelOp1"
android:title="Opcin 1 de etiqueta"></item>
<item android:id="@+id/ContextLabelOp2"
android:title="Opcin 2 de etiqueta"></item>
</menu>
168
Ms informacin sobre Android
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/EditTextOp"
android:title="Editar texto opcin"></item>
<item android:id="@+id/ReiniciaTextOp"
ContextMenuInfo menuInfo)
super.onCreateContextMenu(menu, v, menuInfo);
if(v.getId() == R.id.labelResultado)
inflater.inflate(R.menu.menu_context_etiqueta, menu);
AdapterView.AdapterContextMenuInfo info =
(AdapterView.AdapterContextMenuInfo)menuInfo;
169
// Definimos la cabecera del men contextual
menu.setHeaderTitle(
listadoPrincipal.getAdapter().getItem(info.position).toString());
inflater.inflate(R.menu.menu_context_lista, menu);
En el caso del men contextual para el listado hemos personalizado el ttulo del men
contextual mediante el mtodo setHeaderTitle(), para que muestre el texto del elemento
seleccionado en el listado.
Por ltimo, para implementar las acciones que hay que ejecutar cuando el usuario
selecciona una opcin determinada del men contextual vamos a implementar el mtodo
onContextItemSelected() de manera similar a cmo hacamos con onOptionsItemSelected()
para los mens bsicos:
AdapterContextMenuInfo info =
(AdapterContextMenuInfo) item.getMenuInfo();
switch (item.getItemId()) {
case R.id.ContextLabelOp1:
return true;
170
Ms informacin sobre Android
case R.id.ContextLabelOp2:
return true;
case R.id.EditTextOp:
adaptador.notifyDataSetChanged();
return true;
case R.id.ReiniciaTextOp:
adaptador.notifyDataSetChanged();
return true;
default:
return super.onContextItemSelected(item);
Fjate en el cdigo anterior que se puede mantener pulsado el dedo sobre la etiqueta
azul o sobre una opcin del listado y seleccionar una de las opciones del men contextual.
171
ocasin, lo obtenemos llamando al mtodo getMenuInfo() de la opcin de men MenuItem
recibida como parmetro.
Para ver cmo funciona esta aplicacin que usa un men contextual inicia este
ejemplo en el emulador. Vers la pantalla siguiente:
172
Ms informacin sobre Android
1. AlertDialog: puede contener hasta tres botones (incluso ninguno) o mostrar una lista
de elementos seleccionables como CheckBox o RadioButton. Es recomendable
utilizar este tipo de ventana de dilogo en la mayora de las aplicaciones. Se hereda de
la clase Dialog de Android.
Una ventana de dilogo siempre se crea y se muestra como parte de una Actividad.
En el caso de que creemos una ventana de dilogo fuera del mtodo anterior,
debemos indicar qu Actividad es la que alberga la ventana de dilogo mediante
setOwnerActivity(Activity).
Para mostrar un dilogo desde cualquier parte del cdigo, hay que usar el mtodo
showDialog() indicando como parmetro un entero nico en la actividad que identifica el
dilogo que queremos mostrar. En el ejemplo 5 de esta unidad definimos estos identificadores
as:
...
173
3.5.3.1 Ventanas de dilogo con mensaje
Es una ventana de dilogo que obliga a que el usuario vea un mensaje bloqueando la
pantalla hasta que pulse la tecla volver . Sus mtodos ms importantes son stos:
Si hacemos clic sobre el primer botn del ejemplo del curso, veremos que aparece el
siguiente mensaje:
ventana.setTitle("Atencin");
ventana.setIcon(android.R.drawable.ic_dialog_email);
ventana.show();
174
Ms informacin sobre Android
Si hacemos clic sobre el segundo o el tercer botn del ejemplo del curso, veremos que
aparecen los siguientes mensajes:
ventana.setIcon(android.R.drawable.ic_dialog_info);
ventana.setTitle("Encuesta");
ventana.setCancelable(false);
});
});
175
});
ventana.show();
Si hacemos clic sobre el sexto botn del ejemplo del curso, veremos que aparece el
siguiente mensaje:
ventana.setIcon(android.R.drawable.ic_dialog_info);
ventana.setSingleChoiceItems(telefonos, 0, new
DialogInterface.OnClickListener() {
});
176
Ms informacin sobre Android
});
});
ventana.show();
Si hacemos clic sobre el octavo botn del ejemplo del curso, veremos que aparece el
siguiente mensaje:
177
LayoutInflater li
=(LayoutInflater)getApplicationContext().getSystemService(infService);
ventana.setView(inflador);
});
});
ventana.show();
En el Ejemplo 5 de esta Unidad puedes ver otros tipos de ventanas de dilogo muy
178
Ms informacin sobre Android
similares a las estudiadas en este apartado. Recomendamos al alumno o alumna que estudie a
fondo el cdigo fuente de este ejemplo.
179
Una aplicacin puede estar compuesta de una o varias Actividades.
Android organiza las actividades en una pila de ejecucin (en ingls stack)
donde se van apilando las actividades que el usuario invoca.
180
El sistema operativo Android sigue el modelo de ejecucin de aplicaciones
denominado Modelo de ejecucin de un nico hilo (en ingls Single Thread Model).
Si una aplicacin Android tiene que realizar operaciones que no son instantneas y
llevan cierto tiempo, hay que ejecutarlas en hilos separados en segundo plano.
Un Men es una serie de opciones que el usuario puede elegir para realizar una
determinada tarea.
181
Unidad de Aprendizaje 9
EFECTOS
TRABAJANDO
DE TRANSICIN
CON Y
FICHEROS
ANIMACIN
NDICE
4.1 FICHEROS EN ANDROID .......................................................... 185
4.1.1 Introduccin ...................................................................... 185
4.1.2 Gestin de informacin en Android .................................. 185
4.1.3 Gestin del sistema de archivos en Android .................... 185
4.1.4 Clase Fichero File ............................................................. 186
4.1.4.1 Constructores ms importantes ............................... 186
4.1.4.2 Mtodos ms importantes ........................................ 187
4.1.5 Ficheros en la memoria interna del diapositivo ................ 188
4.1.6 Fichero de recurso de la aplicacin .................................. 190
4.1.7 Fichero en almacenamiento externo ................................ 191
4.1.8 Aadir datos a un fichero .................................................. 196
4.1.9 Gestionando las excepciones en la gestin de ficheros . 196
4.1.1 Introduccin
En Android existen tres formas de almacenar informacin, para poder usarla en las
aplicaciones:
Preferencias de la aplicacin
Ficheros locales en el sistema de archivos del sistema operativo
Base de datos SQLite
En esta Unidad 4 trataremos las dos primeras formas, y en la Unidad 6 veremos las
bases de datos.
185
En Android, por defecto, los ficheros son privados y nicamente puede acceder a ellos
la aplicacin que los crea. Para compartir informacin de estos ficheros se utilizan los Content
Providers que veremos en la Unidad 7.
Lo primero que hay debemos tener en cuenta es dnde queremos almacenar estos
ficheros y la manera en que vamos a acceder a ellos: lectura o escritura.
La clase File de Android se usa para identificar y gestionar archivos y directorios del
sistema operativo. Un archivo en Android puede estar identificado por su ruta absoluta,
relativa al directorio raz del sistema de archivos, o por su ruta relativa, que es directorio actual
en el que se ejecuta la aplicacin. Esta clase File est basada en la clase de Java.
El acceso a los ficheros es similar al Java estndar: se deben crear inputs y outpus
streams.
La clase File puede hacer referencia a un archivo que ya exista o a uno que vayamos a
crear. Aunque el nombre de esta clase sea File, tambin puede, referirse a un directorio o un
enlace (link) de Linux.
Esta clase proporciona una funcionalidad limitada para obtener y establecer permisos
del archivo, cambiar el tipo de archivo o establecer su fecha de ltima modificacin.
186
4.1.4.2 Mtodos ms importantes
String[] list(): devuelve un listado con los nombres de todos los ficheros y
subdirectorios contenidos en un directorio.
File[] listFiles(): devuelve un listado de tipo File con todos los ficheros y
subdirectorios contenidos en un directorio.
187
boolean mkdirs(): crea recursivamente un subdirectorio incluyendo todos los
subdirectorios superiores que completan el camino.
Crear ficheros en la memoria interna es muy sencillo. Android dispone del mtodo
openFileOutput(), que recibe como parmetros el nombre del fichero y el modo de acceso al
mismo. Este modo de acceso puede ser:
try
188
OutputStreamWriter fileout=
new OutputStreamWriter(openFileOutput("fichero_interno.txt",
Context.MODE_PRIVATE));
fileout.close();
/data/data/paquete_java/files/nombre_del_fichero
/data/data/es.mentor.unidad4.eje1.ficheros/files/fichero_interno.txt
189
Leer ficheros desde la memoria interna es igual de sencillo. Procedemos de forma
anloga utilizando esta vez el mtodo openFileInput(), para abrir el fichero y usamos los
mtodos de lectura mostrados anteriormente para leer el contenido.
try
new InputStreamReader(openFileInput("fichero_interno.txt")));
filein.close();
Una vez creada la carpeta raw, podemos aadir en ella cualquier fichero que
necesitemos incluir con la aplicacin en tiempo de compilacin en forma de recurso. En el
Ejemplo 1 de esta Unidad hemos incluido el fichero de texto prueba_raw.txt.
Posteriormente, en tiempo de ejecucin, podemos acceder a este fichero, slo en modo de
lectura, de una forma similar a la que ya hemos visto anteriormente para el resto de ficheros en
la memoria interna del dispositivo.
Estos ficheros de tipo recurso tambin pueden ser binarios: por ejemplo, imgenes,
vdeos, etctera.
En primer lugar, para acceder a este fichero, obtenemos los recursos de la aplicacin
con el mtodo getResources() y sobre ste utilizamos el mtodo
190
openRawResource(id_del_recurso) para abrir el fichero en modo lectura. Este mtodo
devuelve un objeto de tipo InputStream que podemos manipular.
try
while (true) {
texto = brin.readLine();
if (texto==null) break;
resultado.append("\n"+Html.fromHtml(texto));
} // end while
ficheroraw.close();
La mayora de los dispositivos Android disponen de una tarjeta SD externa para que el
sistema tenga un espacio extra de almacenamiento de informacin. Normalmente en esta
tarjeta se guardan las fotos, los vdeos y, en las versiones ltimas de Android, incluso se
instalan aplicaciones.
191
MEDIA_MOUNTED: indica si la memoria externa est disponible y es posible leer y
escribir en ella.
Otros valores que indicarn que existe algn tipo de problema y que, por lo tanto, no
podemos usar la memoria externa (MEDIA_UNMOUNTED, MEDIA_REMOVED, etctera).
Se puede consultar todos estos valores en la documentacin oficial de la clase
Environment.
if (Environment.MEDIA_MOUNTED.equals(estado)) {
} else
if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(estado)) {
hayAlmacenamientoExt = true;
almacenamientoExtEscritura = false;
} else {
if (hayAlmacenamientoExt) {
192
// Mostramos el directorio donde est el almacenamiento externo
dirAlmacExt=dir.getAbsolutePath();
}
} // end compruebaTarjetaSD
Hemos aprovechado el mtodo anterior para obtener la ruta al directorio raz de esta
memoria. Para ello utilizamos el mtodo getExternalStorageDirectory() de la clase
Environment, que devuelve un objeto File con la ruta de dicho directorio.
try
{
// Creamos un directorio de prueba
directorio.mkdirs();
193
fout.write("Caminante no hay camino se hace camino al andar...");
fout.close();
}
} else resultado.append("No hay almacenamiento externo disponible o no
se puede escribir en l.");
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="es.mentor.unidad4.eje1.ficheros"
android:versionCode="1"
android:versionName="1.0">
<activity android:name=".ficherosActivity"
android:label="@string/app_name">
<intent-filter>
</intent-filter>
</activity>
194
</application>
</manifest>
try
resultado.append(texto);
fin.close();
195
}
Por defecto, Android sobrescribe siempre un fichero si ste ya existe perdiendo los
datos almacenados anteriormente. Esto ocurre en los ejemplos anteriores.
Fjate en que todo el cdigo fuente que gestiona el fichero est contenido dentro de un
bloque try-catch. De esta manera se controlan los errores de ejecucin de las sentencias
mediante el manejo de excepciones estndar de Java.
Por ejemplo, Android puede lanzar las siguientes excepciones cuando ocurre un error
al manipular un fichero:
196
try {
En el caso del Ejemplo 1 del curso hemos capturamos todas las excepciones en un
nico bloque para mostrar al usuario y en la consola de Eclipse un mensaje sencillo de error:
Las preferencias de una aplicacin son datos que una aplicacin guarda y
recupera para personalizar la experiencia del usuario. Por ejemplo, se debe almacenar
informacin personal, configuracin de la aplicacin, opciones de presentacin, etctera.
197
Cada preferencia se almacena siguiendo la estructura clave-valor. Es decir, cada una
de ellas est compuesta por un identificador nico (por ejemplo, email) y un valor asociado a
dicho identificador (por ejemplo, correo@email.com). Estos datos se guardan en un fichero
XML.
Para gestionar las preferencias de una aplicacin hay que usar la clase
SharedPrefences. Una misma aplicacin Android puede gestionar varias colecciones de
preferencias que se diferencian por un identificador nico. Para obtener la referencia a una
coleccin determinada utilizamos el mtodo getSharedPrefences() indicando el identificador
de la coleccin y el modo de acceso a la misma. El modo de acceso indica qu aplicaciones
tienen acceso a esta coleccin de preferencias y qu operaciones podrn realizar sobre ellas.
SharedPreferences preferencias =
getSharedPreferences("MisPreferencias",Context.MODE_PRIVATE);
Una vez hemos creado el objeto que nos permite acceder a la coleccin de
preferencias, podemos leer, insertar o modificar claves de preferencias utilizando los mtodos
get() o put() correspondientes al tipo de dato de cada preferencia. Por ejemplo, para obtener
el valor de la preferencia email de tipo String escribimos la siguiente setencia:
198
Actualizar o aadir nuevas claves de preferencias es muy parecido. En lugar de usar el
objeto SharedPreferences, utilizaremos SharedPreferences.Editor para editar preferencias.
Accedemos a este objeto mediante el mtodo edit() de la clase SharedPreferences.
Una vez obtenida la referencia al editor, utilizamos los mtodos put() oportunos en
funcin del tipo de dato de cada clave de preferencia para actualizar o insertar su valor.
Por ejemplo, putString(clave, valor) actualiza una preferencia de tipo String. De igual
forma, existen mtodos get() para todos los tipos de datos bsicos de Java: putInt(),
putFloat(), putBoolean(), etctera.
SharedPreferences preferencias =
getSharedPreferences("MisPreferencias", Context.MODE_PRIVATE);
editor.putString("email", "correo@email.com");
editor.commit();
/data/data/paquetejava/shared_prefs/nombre_coleccion.xml
/data/data/es.mentor.unidad4.eje2.preferencias/shared_prefs/MisPreferencias.xml
<map>
<string name="email">correo@email.com</string>
</map>
199
En este fichero XML observamos cmo se almacenan las dos preferencias del ejemplo
anterior con sus claves y valores correspondientes.
Si nos fijamos en la imagen anterior, vemos que las distintas opciones se organizan
dentro de una pantalla de opciones que incluye varias categoras (General, Llamadas
entrantes, etctera).
Dentro de cada categora, aparecen varios tipos de opciones. Por ejemplo, de tipo
CheckBox (Modo Silencio) o de tipo lista de seleccin (Vibrar).
Para definir la pantalla de opciones vamos a usar un fichero de diseo (layout) XML,
que guardamos en la carpeta /res/xml/pantallapreferencias.xml del proyecto.
Dentro de esta pantalla podemos incluir una lista de opciones organizadas por
categoras, que se representan mediante el elemento <PreferenceCategory>, al que
200
aadimos un texto descriptivo utilizando el atributo android:title. Dentro de cada categora
podemos incluir varias opciones. stas pueden ser de distintos tipos, por ejemplo:
CheckBoxPreference
Se usa para introducir una opcin de preferencia que slo puede tener dos valores:
activada (marcada) o desactivada (desmarcada). Es el equivalente al componente de tipo
CheckBox. En este caso, hay que especificar los atributos: nombre interno de la opcin
(android:key), texto que muestra (android:title) y descripcin de la opcin
(android:summary). Veamos un ejemplo:
<CheckBoxPreference android:key="opcion1"
android:title="Bsqueda automtica"
android:summary="Iniciar bsqueda automtica en Internet" />
EditTextPreference
Se utiliza para introducir una opcin de preferencia que contiene una cadena de texto.
Al pulsar sobre una opcin de este tipo, se muestra un cuadro de dilogo sencillo que solicita
al usuario un texto. Para este tipo de opcin, adems de los tres atributos comunes a la
opcin anterior, tambin hay que indicar el texto que aparece en el cuadro de dilogo
mediante el atributo android:dialogTitle. Por ejemplo:
<EditTextPreference android:key="opcion2"
android:title="Texto de la bsqueda"
ListPreference
Se emplea para que el usuario seleccione una nica opcin de preferencia de una lista
de valores predefinida. Adems de los cuatro atributos anteriormente comentados, hay que
201
aadir dos ms: uno para indicar la lista de valores que se visualizan en la lista, y otro para
sealar los valores internos que guardaremos para cada uno de los valores de la lista anterior.
Por ejemplo, al usuario podemos mostrarle una lista de buscadores con el texto Google y
Bing, pero internamente almacenarlos como www.google.es y www.bing.com.
<resources>
<string-array name="nombre">
<item>Google</item>
<item>Bing</item>
<item>Yahoo</item>
</string-array>
<string-array name="url">
<item>www.google.es</item>
<item>www.bing.com</item>
<item>www.yahoo.es</item>
</string-array>
</resources>
<ListPreference
android:key="opcion3"
android:title="Buscadores"
android:dialogTitle="Selecciona buscador"
android:entries="@array/nombre"
android:entryValues="@array/url" />
202
MultiSelectListPreference
Se emplea para que el usuario seleccione una o varias opciones de preferencia de una
lista de valores predefinida. Los atributos que debemos establecer son, por lo tanto, los
mismos que para el tipo ListPreference. Vemos un ejemplo a continuacin:
<MultiSelectListPreference
android:key="opcion4"
android:title="Varios buscadores"
android:dialogTitle="Selecciona buscadores"
android:entries="@array/nombre"
android:entryValues="@array/url" />
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<CheckBoxPreference
android:key="opcion1"
android:title="Bsqueda automtica"
<EditTextPreference
android:key="opcion2"
android:title="Texto de la bsqueda"
</PreferenceCategory>
<ListPreference
android:key="opcion3"
android:title="Buscadores"
203
android:summary="Indica el buscador por defecto"
android:dialogTitle="Selecciona buscador"
android:entries="@array/nombre"
android:entryValues="@array/url" />
</PreferenceCategory>
</PreferenceScreen>
Una vez est definida la estructura de la pantalla de opciones, hay que implementar
una nueva Actividad que la llamaremos cuando queramos mostrar la pantalla de preferencias,
y que se encargar internamente de gestionar todas las opciones, guardarlas, modificarlas,
etctera, a partir de la definicin XML.
@Override
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pantallapreferencias);
Toast.makeText(getBaseContext(), "Pulsa la tecla 'Volver atrs' para
guardar las preferencias y volver a la aplicacin.", 1).show();
Para que la aplicacin pueda llamar a esta nueva Actividad, hay que incluirla en el
fichero AndroidManifest.xml como otra actividad ms de la aplicacin:
204
<activity android:name=".PantallaPreferencias"
android:label="@string/app_name">
</activity>
Para acabar la aplicacin, hay que aadir algn mecanismo que nos permita mostrar la
pantalla de preferencias. Esta opcin suele estar en un men, pero para simplificar, en el
Ejemplo 2 vamos a incluir el botn preferenciasBtn en la interfaz del usuario, para mostrar la
ventana de preferencias.
preferenciasBtn.setOnClickListener(new OnClickListener() {
@Override
startActivity(new Intent(PreferenciasActivity.this,
PantallaPreferencias.class));
}
});
205
Podemos marcar o desmarcar directamente la primera opcin pulsando sobre el
CheckBox. Si pulsamos sobre la segunda opcin de tipo texto, se muestra una pequea
ventana que permite introducir el valor de la opcin:
Por ltimo, la tercera opcin de tipo lista muestra una ventana emergente con la lista
de valores posibles, donde nicamente podemos seleccionar uno:
Una vez hayamos establecidos los valores de las preferencias, podemos salir de esta
ventana de opciones pulsando el botn Atrs del dispositivo o del emulador . La Actividad
PantallaPreferencias se habr ocupado por nosotros de guardar correctamente los valores
de las opciones.
206
obtenerPreferenciasBtn.setOnClickListener(new OnClickListener() {
@Override
SharedPreferences prefe =
PreferenceManager.getDefaultSharedPreferences(PreferenciasActivity.this);
lblResultado.setText("");
lblResultado.append("\n\nBuscador: " +
prefe.getString("opcion3", ""));
}
});
Los Recursos de Android son archivos almacenados en el directorio /res del proyecto.
Estos ficheros de recursos pueden ser de tipo audio, vdeo, imgenes, texto, XML,
etctera, y se pueden integrar en la aplicacin.
207
1. Los recursos se integran en la aplicacin de manera transparente y se pueden
cambiar sin necesidad de modificar el cdigo fuente en Java.
2. Android genera un identificador nico (ID) para cada archivo de recurso para
acceder a ellos directamente desde el cdigo fuente en Java. Todos estos
identificadores de recursos se incluyen automticamente en el archivo
/gen/nombre_paquete/R.Java.
Para ello, hay que hacer doble clic en el fichero de recursos que queramos editar; por
ejemplo, en el fichero res/values/string.xml y hacer clic en el botn "Add":
208
Despus, basta con seleccionar el tipo de recursos que necesitamos dar de alta y
rellenar el nombre y el valor que debe contener:
Un recurso de tipo Cadena (String) permite definir cadenas de texto para usarlas en la
aplicacin Android: Incluso podemos cambiar su estilo y formato. Hay tres recursos de tipo
Cadena:
Quantity Strings (Plurals): recurso que incluye dos cadenas segn una
cantidad sea singular o plural.
209
Ms adelante veremos cmo se puede modificar el estilo y formatear el contenido de
todos estos recursos de tipo cadena.
Se trata de una Cadena de texto simple que se puede utilizar dentro del cdigo fuente
Java o en otro fichero (layout) de diseo XML, como los que se usan para definir las pantallas.
En el Ejemplo 3 de esta Unidad puedes ver cmo se hace referencia a esta etiqueta
desde el fichero XML que define el diseo de la pantalla principal main.xml:
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/etiqueta1"
android:text="@string/etiqueta1"
android:layout_margin="1dip"/>
etiqueta2.setText(getString(R.string.etiqueta2));
210
4.3.4.2 Matriz de cadenas (String Array)
<string-array name="nombre_matriz">
<item>texto elemento 1</item>
<item>texto elemento 2</item>
...
</string-array>
<string-array name="horoscopo">
<item>Cancer</item>
<item>Capricornio</item>
<item>Aries</item>
<item>Leo</item>
<item>Libra</item>
</string-array>
Para hacer referencia a este recurso de cadena en el cdigo fuente Java debemos
escribir R.array.horoscopo.
En el Ejemplo 3 de esta Unidad puedes ver cmo se usa este recurso de tipo matriz
para cargar las opciones de una caja de seleccin:
s.setPrompt("Elige el horscopo");
ArrayAdapter<CharSequence> adapter =
ArrayAdapter.createFromResource(this, R.array.horoscopo,
android.R.layout.simple_spinner_item);
211
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
;
s.setAdapter(adapter);
Para obtener los elementos de la matriz desde el cdigo fuente en Java podemos usar
la funcin getStringArray:
Cada idioma tiene diferentes reglas de concordancia gramatical con las cantidades. En
espaol por ejemplo, escribimos "un libro" y, para cualquier otra cantidad, escribimos "3
libros". Esta distincin entre el singular y el plural es muy comn en los idiomas. Incluso hay
idiomas que hacen distinciones ms sutiles. Android incluye un conjunto completo y distingue
entre cero (zero), uno (one), dos (two), pocos (few), muchos (many) y otros (other).
Las reglas de concordancia pueden ser muy complejas dependiendo del idioma, por lo
que Android dispone del mtodo getQuantityString(), que selecciona el recurso apropiado
segn la cantidad numrica que estemos tratando.
<plurals name="nombre_plural">
<item quantity=["zero" | "one" | "two" | "few" | "many" | "other"]>
texto del literal</item>
</plurals>
<plurals name="numeroDeContactos">
Dentro de la etiqueta <plurals> incluimos varias etiquetas <item quantity> con los
literales que forman las distintas opciones de plurales. Podemos escribir los siguientes tipos
de plurales en el atributo quantity:
212
Valor Descripcin
two Cuando el idioma requiere un tratamiento especial del nmero 2 (como el gals).
Para hacer referencia a este recurso de cadena en el cdigo fuente Java debemos
escribir R.plurals.numeroDeContactos.
En el Ejemplo 3 de esta Unidad puedes ver cmo se usa este recurso de tipo matriz
para cambiar el literal en funcin del nmero que escribe el usuario en una caja de texto:
A la hora de escribir comillas simples o dobles dentro del literal de una cadena de
texto debemos "escaparlas", para que Android no las interprete como parte del fichero XML y
muestre errores de sintaxis.
213
Es necesario incluir siempre las comillas simples dentro de las dobles o usar el
carcter "\" para escaparlas. Adems, no se permite incluir entidades de HTML para los
caracteres singulares como "á".
tiene dos argumentos: %1$s, que es una cadena y %2$d, que es un nmero decimal.
En el Ejemplo 3 de esta Unidad hemos usado as esta cadena con formato para formatear un
String strFormat=getString(R.string.FormatoCadena);
Es posible definir una cadena de recurso que contenga cambios de estilo HTML en el
texto. As, en el Ejemplo 3 de esta Unidad definimos:
214
Si ejecutamos la aplicacin en el emulador, veremos que aparece la siguiente pantalla:
Para completar este ejemplo hemos incluido tambin un recurso de tipo imagen en el
directorio drawable y hemos definido un color para cambiar el aspecto de una de las
etiquetas.
Importante: Al definir recursos de tipo cadena hay que tener cuidado en no escribir caracteres
que Android no sepa interpretar. Cuando esto ocurra Eclipse mostrar el siguiente mensaje de
error y no permitir compilar el proyecto:
Android incluye la biblioteca Apache de cliente HTTP (Apache HttpClient library) que
permite a las aplicaciones conectar con servidores Web de Internet.
215
Android tambin permite usar la librera estndar Red de Java Java Networking API
(paquete java.net), aunque, si usamos este paquete java.net, Android utiliza internamente la
librera de Apache.
Para que una aplicacin Android acceda a Internet, es necesario declararlo en el fichero
AndroidManifest.xml, que requiere el permiso "android.permission.INTERNET".
Desde Eclipse puedes abrir el proyecto Ejemplo 4 (Acceso Internet) de la Unidad 4. Estudia el
cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa anterior.
216
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
cargaPreferencias();
pagWeb.setText(ultimaUrl);
...
}
@Override
super.onPause();
preferenceEditor.putString(URL, pagWeb.getText().toString());
preferenceEditor.commit();
Para descargar la pgina de Internet hemos usado una tarea asncrona que hemos
estudiado en la Unidad 3. En este caso hemos definido el mtodo onProgressUpdate() para
ir actualizando la pantalla segn se va descargando la pgina de Internet:
217
// Mtodo onClick del botn Descargar
switch (view.getId()) {
case R.id.descargPagWeb:
ResultadoLabel.setText("");
else {
cargando.setVisibility(View.VISIBLE);
tarea.execute(pagWeb.getText().toString());
break;
} // end onClick
// Clase que descarga una pgina de Internet como una tarea asncrona.
try {
// Obtenemos la respuesta
respuesta.getEntity().getContent()));
if (resultado.length()>1024) {
publishProgress(resultado);
resultado="";
} // end while
catch (Exception e) {
return null;
ResultadoLabel.append(values[0]);
cargando.setVisibility(View.INVISIBLE);
} // end onPostExecute
Es evidente que un dispositivo Android no tiene siempre conexin a Internet. Por esto,
es bueno comprobar que tiene acceso a Internet a travs del siguiente cdigo:
ConnectivityManager cm = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
219
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
return true;
return false;
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE">
</uses-permission>
Para configurarlo, haz clic en el icono "Ajustes" y establece las siguientes opciones:
220
4.4 QU SON JSON (JavaScript Object Notation?
JSON es ms fcil de utilizar como formato de intercambio de datos que XML, porque
es mucho ms sencillo escribir un analizador semntico de JSON.
El formato JSON se basa en los tipos de datos y sintaxis del lenguaje JavaScript. Es
compatible con cadenas, nmeros, boolean y valores nulos. Tambin se pueden combinar
valores en matrices y objetos.
{
"producto": {
"nombre": "Widget",
"compania": "ACME, Inc",
"numero": "7402-129",
"precios": [
{ "cantMin": 1, "precio": 12.49 },
{ "cantMin": 10, "precio": 9.99 },
{ "cantMin": 50, "precio": 7.99 }
]
}
}
221
Puedes ver ms ejemplos en el siguiente enlace: json.org/example.html.
Twitter es una fuente muy grande que usa el formato JSON. En el Ejemplo 5 de esta
Unida vamos a cargar el Twitter de Mentor en una aplicacin Android. La direccin es la
siguiente:
http://twitter.com/statuses/user_timeline/MinisterioEduc.json
Android incluye la biblioteca JSON, que permite tratar este formato de dato. Las
clases ms importantes de este paquete son:
Desde Eclipse puedes abrir el proyecto Ejemplo 5 (JSON) de la Unidad 4. Estudia el cdigo
fuente y ejectalo para mostrar en el AVD el resultado del programa anterior.
222
Vamos a mostrar con un ejemplo prctico cmo usar una fuente de datos en formato
JSON en una aplicacin Android. Si abrimos el fichero de cdigo fuente del Ejemplo 5,
veremos las siguientes sentencias:
@Override
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if (datosCuentaTwitter!= null)
try {
datosAdaptador.add(jsonObjeto.getString("text"));
} // end for
} catch (Exception e) {
e.printStackTrace();
setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, datosAdaptador));
}
223
// String que permite ir aadiendo lneas
try {
if (statusCode == 200) {
String line;
builder.append(line);
} else {
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
224
e.printStackTrace();
return builder.toString();
} // end leeCuentaTwitter
Una vez hemos obtenido los datos JSON del servidor, nicamente hay que crear un
objeto de la clase JSONArray cuyo parmetro sean los datos anteriores. Despus, slo hay
que recorrer esta matriz de elementos de tipo JSONObject y usar el mtodo getString() para
obtener el contenido del mismo.
NOTA: Para que una aplicacin Android acceda a Internet es necesario declararlo en el fichero
AndroidManifest.xml, que requiere el permiso "android.permission.INTERNET".
Escribir datos en formato JSON es muy sencillo. Basta con crear un objeto del tipo
JSONObject o del tipo JSONArray y utilizar el mtodo toString() para transformar este objeto
en datos JSON.
try {
objeto.put("ciudad", "Avila");
} catch (JSONException e) {
e.printStackTrace();
System.out.println(objeto);
225
En Android existen tres formas de almacenar informacin para usarla en las
aplicaciones:
En Android, por defecto, los ficheros son privados y nicamente puede acceder
a ellos la aplicacin que los crea.
Las preferencias de una aplicacin son datos que una aplicacin guarda y
recupera para personalizar la experiencia del usuario.
Para gestionar las preferencias de una aplicacin hay que usar la clase
SharedPrefences, donde cada preferencia se almacena siguiendo la estructura
clave-valor.
226
Las preferencias se almacenan en formato XML en un fichero en la memoria del
dispositivo.
Un recurso de tipo Cadena (String) permite definir cadenas de texto para usarlas
en la aplicacin Android; incluso podemos cambiar su estilo y formato. Son muy
tiles para el programador, pues facilita la Internacionalizar las aplicaciones.
Android incluye la biblioteca JSON que permite leer y escribir este formato de
dato.
227
INTENTS EN ANDROID
NDICE
5.1 INTENTS EN ANDROID ...............................................................231
5.1.1 Introduccin ...........................................................................231
5.1.2 Intenciones (Intents) ...............................................................231
5.1.3 Ficheros Manifest ...................................................................232
5.1.4 Declarar capacidades de los componentes de las aplicaciones233
5.1.5 Uso de intenciones .................................................................234
5.1.6 Arranque explcito de una actividad .......................................234
5.1.7 Arranque implcito de una actividad .......................................235
5.1.7.1 Ejecutar subactividades ......................................235
5.1.8 Filtros de intenciones .............................................................239
5.1.9 Resolucin de intenciones implcitas ......................................240
5.1.10 Uso de intenciones para extender aplicaciones ....................241
5.1.1 Introduccin
En esta Unidad vamos a explicar cmo usar Intenciones (Intents) en Android para
arrancar Actividades o servicios.
De los cuatro componentes de Android, las Actividades, los Servicios y los Receptores
de mensajes de difusin se activan con un mensaje asncrono que se denomina Intencin. Los
Proveedores de contenidos quedan excluidos.
Para crear una Intencin hay que usar el objeto Intent de Android.
Implcita: invocando la accin y los datos sobre los que aplicar dicha accin.
Android selecciona, en tiempo de ejecucin, la actividad receptora que cumple
mejor con la accin y los datos solicitados.
Para las Actividades y Servicios, una intencin define la accin que queremos realizar
(por ejemplo, "ver" o "enviar" algo) y puede especificar el identificador URI de los datos que
va a utilizar esa accin. Por ejemplo, una intencin podra hacer una peticin para arrancar una
actividad que muestre una imagen o abra una pgina Web.
231
Las Intenciones son mensajes asncronos entre componentes de aplicaciones que se usan
para realizar acciones e intercambiar datos, tanto en la peticin como en la respuesta,.
De esta manera el usuario tiene la sensacin de estar usando una nica aplicacin cuando, en
realidad, son componentes de varias.
Uno de los usos principales de las intenciones es arrancar, parar y cambiar entre las
actividades y los servicios de una aplicacin.
Para que Android pueda iniciar un componente de una aplicacin, el sistema debe
conocer que existe este componente. Ya hemos visto que, para ello, se declaran los
componentes de una aplicacin en el fichero AndroidManifest.xml. Este fichero se encuentra
en el directorio raz del proyecto Android.
232
Intents
Hay que declarar todos los componentes de la aplicacin de esta forma usando las
siguientes etiquetas:
<activity>: Actividades
<service>: Servicios
233
Android identifica qu componentes pueden responder a una Accin determinada
buscndola en los filtros de intencin (intent filters) que se declaran en el archivo
"AndroidManifest" de todas las aplicaciones del dispositivo.
Veamos un ejemplo: una aplicacin de correo electrnico con una Actividad que
componga un nuevo correo electrnico puede declarar el filtro de intencin ACTION_SEND
que responda a la intencin "send" (enviar) que, lgicamente, enva un mensaje. Una actividad
de otra aplicacin puede entonces simplemente iniciar una intencin con la accin
(ACTION_SEND) que provocar que Android busque la actividad o actividades que
concuerdan con esta accin en iniciar la intencin "send". Es decir, el programador no tiene
que conocer el nombre de la intencin, nicamente, debe invocar su accin. Ms adelante
veremos otro ejemplo.
Para arrancar una Actividad sin esperar una respuesta de la subactividad iniciada,
debemos usar la siguiente funcin:
startActivity(anIntent);
startActivityForResult(anIntent, INTENT_COD);
Ambos mtodos se pueden usar tanto en las invocaciones explcitas como implcitas.
La diferencia radica en que el primero inicia la subactividad y no espera respuesta de sta; el
segundo mtodo espera recibir una respuesta de la ejecucin de la subactividad.
startActivity(intent);
234
Intents
Una intencin implcita especifica la accin requerida y los datos sobre los que acta.
Es importante insistir en que las aplicaciones de Android deben publicar las acciones que
ofrecen.
if (...) {
startActivity(intent);
El cdigo anterior inicia una llamada de telfono al nmero indicado como parmetro
en la intencin. Como hemos usado el mtodo startActivity(), no trataremos la respuesta de
su ejecucin.
Veamos un ejemplo sencillo que inicia una subactividad desde una actividad principal:
startActivityForResult(intent, SUBACTIVIDAD);
235
En la Intencin hemos incluido una coleccin de datos Extras con informacin
adicional. Para ello, usamos el mtodo putExtra() de la clase Intent para incorporar la
informacin adicional.
super.onCreate(bundle);
setContentView(R.layout.subactividad);
okButton.setOnClickListener(new View.OnClickListener() {
resultado.putExtra(TODO_CORRECTO, correcto);
resultado.putExtra(DATO_SELECCIONADO, datoSeleccionado);
setResult(RESULT_OK, resultado);
finish();
});
236
Intents
cancelarButton.setOnClickListener(new View.OnClickListener() {
setResult(RESULT_CANCELED, null);
finish();
});
@Override
switch(requestCode) {
case (SUB_ACTIVIDAD_UNA) : {
if (resultCode == Activity.RESULT_OK) {
break;
case (SUB_ACTIVIDAD_DOS) : {
if (resultCode == Activity.RESULT_OK) {
break;
238
Intents
Intencin con datos devueltos: identificador URI con la intencin con los
resultados devueltos por la subactividad. Esta segunda actividad reciben
datos de la primera a travs de la clase Bundle que pueden ser recuperados a
travs de dos formas:
Datos: permite especificar mediante atributos los tipos de datos sobre los que
puede actuar el componente. Por ejemplo, android:host, android:mimetype,
android:path, etctera.
<activity android:name=".EjemploActividad"
android:label="Ver Texto">
<intent-filter>
<action android:name="es.mentor.intent.action.VER_TEXTO">
</action>
<category android:name="android.intent.category.DEFAULT"/>
<category
android:name="android.intent.category.CATEGORY_ALTERNATIVE"
/>
<data android:mimeType="vnd.visualizador.cursor.item/*"/>
</intent-filter>
</activity>
Puede ocurrir que exista ms de una actividad que haya registrado un filtro de
intencin (Accin) con el mismo nombre. Por ejemplo, el dispositivo Android puede disponer
de varios programas para navegar por Internet; si el usuario solicita abrir una pgina HTML,
entonces se le muestra un listado con las opciones posibles que completan las acciones.
Android elige la intencin implcita que se resuelve con varias actividades posibles as:
1. Android genera una lista interna en el sistema operativo con todos los filtros de
intenciones posibles incluyendo los de las aplicaciones nativas preinstaladas.
@Override
super.onCreate(bundle);
setContentView(R.layout.main);
if (!noHayConexionInternet) startNextMatchingActivity(intent);
241
El mtodo addIntentOptions() de la clase Menu permite especificar los datos sobre
los que puede operar una accin futura. Se especifican nicamente los datos, no la accin en
s. Cuando Android resuelve la intencin y devuelve una lista de acciones apropiadas para el
dato, se crea una nueva opcin en el men de la aplicacin.
Muchas aplicaciones del sistema operativo emplean este mecanismo para extender su
funcionalidad a medida que nuevas actividades van implementando las acciones previstas.
Por ejemplo:
<activity android:name=".ActividadExtra">
<action android:name="es.mentor.LANZAR"/>
<data android:mimeType="es.mentor.cursor.item/*"/>
<category android:name="android.intent.category.ALTERNATIVE"/>
<category
android:name="android.intent.category.SELECTED_ALTERNATIVE"
/>
</intent-filter>
</activity>
El mtodo addIntentOptions() del objeto men recibe como parmetro una intencin
que especifica los datos para los que se quiere proporcionar una accin. Se invoca este
mtodo desde los mtodos onCreateOptionsMenu() o onCreateContextMenu(), que ya
hemos estudiado. La intencin slo especifica los datos y la categora
CATEGORY_ALTERNATIVE o CATEGORY_SELECTED_ALTERNATIVE. No se debe
especificar ninguna accin ya que es lo que buscamos.
@Override
super.onCreateOptionsMenu(menu);
242
Intents
intent.setData(Dato.CONTENT_URI);
intent.addCategory(Intent.CATEGORY_SELECTED_ALTERNATIVE);
return true;
El Ejemplo 1 de este Unidad muestra cmo se pueden transferir datos entre dos
actividades. Para ello, vamos a invocar intenciones explcitamente entre dos actividades. La
primera actividad llama a la segunda, tambin llamada subactividad, a travs de una intencin
explcita. Esta segunda actividad recibe datos de la primera a travs de la clase Bundle, que
pueden ser recuperados a travs de intent.getExtras().
Lo primero que vamos a hacer es definir el fichero XML que incluye los diseos (layout)
de ambas actividades. Puedes encontrar este diseo en los ficheros del proyecto Android:
243
res/layout/main.xml: actividad principal.
res/layout/segundaactividad.xml: actividad secundaria o subactividad.
Despus, creamos el cdigo fuente Java para las dos actividades. La segunda
actividad, que se invoca desde la primera, muestra los datos recibidos e indica si devuelve
datos a la actividad principal. Veamos el cdigo fuente de la actividad principal:
@Override
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tituloLbl.setText(contenido);
ModContactoBtn.setEnabled(false);
nombre="";
apellidos="";
244
Intents
switch (view.getId()) {
case R.id.AltaContactoBtn:
i.putExtra("operacion", "alta");
i.putExtra("nombre", "");
i.putExtra("apellidos", "");
break;
case R.id.ModContactoBtn:
i.putExtra("operacion", "modifica");
i.putExtra("nombre", nombre);
i.putExtra("apellidos", apellidos);
break;
startActivityForResult(i, COD_PETICION);
@Override
// Si la subactividad responde OK
if (resultCode == RESULT_OK) {
resultadoLbl.setText("");
if (data.hasExtra("nombre")) {
resultadoLbl.setText("Nombre: " +
data.getExtras().getString("nombre")+"\n");
nombre= data.getExtras().getString("nombre");
if (data.hasExtra("apellidos")) {
resultadoLbl.append("Apellidos: " +
data.getExtras().getString("apellidos")+"\n");
apellidos= data.getExtras().getString("apellidos");
245
}
if (!nombre.isEmpty() || !apellidos.isEmpty()) {
AltaContactoBtn.setEnabled(false);
ModContactoBtn.setEnabled(true);
} else {
AltaContactoBtn.setEnabled(true);
ModContactoBtn.setEnabled(false);
} else
@Override
super.onCreate(bundle);
setContentView(R.layout.segundaactividad);
246
Intents
operacion = extra.getString("operacion");
nombre.setText(valor1);
apellidos.setText(valor2);
if (operacion.equals("alta")) tituloLbl.setText("Alta de
contacto. Indica el nombre y los apellidos.");
datos.putExtra("nombre", nombre.getText().toString());
datos.putExtra("apellidos", apellidos.getText().toString());
datos.putExtra("operacion", operacion);
// Indicamos OK en el resultado
setResult(RESULT_OK, datos);
finish();
@Override
247
super.finish();
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="es.mentor.unidad5.eje1.intencionexplicita"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon"
android:label="@string/app_name">
<activity android:name=".IntencionexplicitaActivity"
android:label="@string/app_name">
<intent-filter>
</intent-filter>
</activity>
android:name=".ActividadDos">
</activity>
</application>
</manifest>
248
Intents
Lo primero que vamos a hacer es definir el fichero XML que incluye el diseo (layout)
de la actividad principal. Puedes encontrar este diseo en el fichero res/layout/main.xml del
proyecto Android.
Para poder usar Intents con componentes de Android hay que aadir los siguientes
permisos a la aplicacin en el fichero "AndroidManifest.xml":
249
<uses-permission android:name="android.permission.CALL_PRIVILEGED"></uses-
permission>
<uses-permission android:name="android.permission.CALL_PHONE"></uses-
permission>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.READ_CONTACTS"></uses-
permission>
switch (view.getId()) {
case R.id.navegadorBtn:
startActivity(intent);
break;
case R.id.llamarTfnoBtn:
startActivity(intent);
break;
case R.id.marcarTfnoBtn:
startActivity(intent);
break;
case R.id.contactosBtn:
startActivity(intent);
break;
case R.id.selContactoBtn:
250
Intents
startActivityForResult(intent, SELEC_CONTACTO);
break;
} // end switch
@Override
// Mtodo que se lanza cuando un Intent acaba su tarea. En este caso slo la
// accin que selecciona un contacto devuelve informacin
if (c.moveToFirst()) {
} // end RESULT_OK
En el cdigo fuente anterior podemos ver que algunas actividades se inician con el
mtodo startActivity() porque no queremos saber el resultado de la ejecucin de la
subactividad. Sin embargo, el segundo botn "Seleccionar contacto" se inicia con el mtodo
startActivityForResult() para obtener el resultado de la ejecucin de la subactividad en el
mtodo onActivityResult(). En la prctica lo que hacemos es obtener el contacto
seleccionado mediante un identificador URI de la base de datos de contactos del telfono.
Desde Eclipse puedes abrir el proyecto Ejemplo 2 (Intencin implcita) de la Unidad 5. Estudia
el cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa anterior, en el
que hemos utilizado Intenciones invocadas de manera implcita.
251
Si ejecutamos la aplicacin, veremos que al hacer clic en cualquiera de los botones de
la pantalla se inicia una nueva Actividad:
Fjate que en el cdigo fuente no especificamos la aplicacin que tiene que lanzarse
para esa Accin, simplemente dejamos que Android decida qu aplicacin es ms apropiada
para la tarea solicitada.
El Ejemplo 2 de este Unidad muestra cmo definir filtros de intenciones (Filter Intens)
de una Actividad interna de la aplicacin para que pueda ser invocada implcitamente desde
otra aplicacin mediante su Accin correspondiente.
Para que Android sepa que hay una nueva Accin disponible en el sistema operativo,
tenemos que aadir la Actividad con la Accin correspondiente en el fichero
"AndroidManifest.xml":
252
Intents
<activity android:name=".NavegadorActivity"
android:label="@string/navegadorAct">
<intent-filter>
<data android:scheme="http"/>
</intent-filter>
</activity>
En este caso, la accin es visualizar (VIEW) el protocolo HTTP (esquema del dato).
Hemos usado la etiqueta <intent-filter> para definir la nueva accin. Por la nomenclatura de
Android, es muy importante definir el punto "." en el nombre de la actividad:
android:name=".NavegadorActivity".
Lo primero que vamos a hacer es definir el fichero XML que incluye el diseo (layout)
de la actividad secundaria (Navegador de Internet). Puedes encontrar este diseo en el fichero
res/layout/navegador.xml del proyecto Android.
@Override
super.onCreate(savedInstanceState);
setContentView(R.layout.navegador);
try {
253
URL url = new URL(datos.getScheme(), datos.getHost(),
datos.getPath());
url.openStream()));
textoResultado.append(line);
} catch (Exception e) {
e.printStackTrace();
Desde Eclipse puedes abrir el proyecto Ejemplo 2 (Intencin implcita) de la Unidad 5. Estudia
el cdigo fuente y ejectalo para mostrar en el AVD el resultado del programa anterior, en el
que hemos definido una nueva Accin mediante Filtros de Intenciones.
254
Intents
El siguiente cdigo comprueba si una Accin existe. As, es muy fcil cambiar el
comportamiento de una aplicacin, como mostrar u ocultar opciones de la misma.
if (resolveInfo.size() > 0) {
return true;
}
return false;
}
255
5.3 PERMISOS Y SEGURIDAD EN ANDROID
Nota: por coherencia, en este apartado tratamos tambin la seguridad de Content Providers,
Servicios y Receptores de mensajes de difusin (Broadcast Receivers), que estudiaremos en la
Unidad 7. Puedes buscar informacin en esta Unidad para conocer los conceptos bsicos.
Debido a que Android separa la ejecucin de las aplicaciones en cajas de arena (del
ingls sandbox), las aplicaciones deben compartir recursos y datos de manera explcita.
Para compartir recursos entre aplicaciones, stas deben declarar al sistema Android
los permisos adicionales que necesitan para funcionar correctamente y ampliar su
funcionalidad bsica. Las aplicaciones declaran los permisos que necesitan y el sistema
Android solicita al usuario su consentimiento cuando instala la aplicacin. Android no tiene
ningn mecanismo para dar permisos de forma dinmica (en tiempo de ejecucin).
Todas las aplicaciones Android (archivos .apk) deben estar firmadas con un certificado
que identifica al autor de la aplicacin. El propsito de los certificados de Android es distinguir
a los autores de las aplicaciones. Esto permite al sistema conceder o denegar solicitudes de
acceso para compartir la identidad de Linux que tenga otra aplicacin. Es decir, si el
desarrollador es el mismo, es posible ejecutar ambas aplicaciones con el mismo usuario de
Linux y que compartan el mismo sandbox.
256
Intents
A los datos almacenados por una aplicacin tambin se les asigna el ID de usuario y
normalmente otras aplicaciones no pueden acceder a ellos. Cuando se crea un nuevo archivo
con funciones del estilo getSharedPreferences(), openFileOutput(), etctera, se puede usar
los parmetros MODE_WORLD_READABLE y MODE_WORLD_WRITEABLE para permitir
que cualquier otra aplicacin lea o escriba en el archivo. Aunque se establezcan estos
indicadores, el archivo sigue siendo propiedad de la aplicacin original, si bien se permite su
lectura y escritura globalmente, de manera que cualquier otra aplicacin puede acceder a esta
informacin almacenada.
Una aplicacin bsica de Android no tiene permisos asociados, por lo que no puede
hacer nada que afectara al usuario o a cualquier aplicacin instalada en el dispositivo. Para
hacer uso de las caractersticas protegidas del dispositivo, se debe incluir en el fichero
AndroidManifest.xml del proyecto una o ms etiquetas <uses-permission> que declaren los
permisos que necesita la aplicacin.
En alguno de los ejemplos anteriores del curso ya hemos usado esta etiqueta para
poder ampliar la funcionalidad de la aplicacin y acceder a determinados recursos del sistema
operativo.
257
Podemos escribir una o varias etiquetas <uses-permission> en el archivo
AndroidManifest.xml. El elemento <uses-permission> requiere que definamos el atributo
android:name, dentro del cual indicamos el nombre del permiso que requiere la aplicacin.
Por ejemplo:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="es.mentor.miapp" >
<uses-permission android:name="android.permission.RECEIVE_SMS" />
...
</manifest>
En este caso, estamos manifestando que esta aplicacin necesita poder recibir
mensajes cortos SMS para funcionar.
El programador puede definir sus propios permisos internos de aplicacin para que
otra aplicacin los utilice. Ms adelante veremos cmo se hace.
258
Intents
hemos dicho, las aplicaciones de terceros pueden tener sus propios permisos. De forma
general, resumimos algunos de los permisos ms utilizados:
Es posible que una aplicacin no reciba la autorizacin para hacer algo porque nos
hayamos olvidado de declarar el permiso necesario en el fichero AndroidManifest. En este
caso podemos usar la excepcin de tipo SecurityException, que indica el permiso que falta.
259
Para que funcione bien esta aplicacin, debemos aadir la siguiente etiqueta en el
fichero AndroidManifest:
<uses-permission android:name="android.permission.VIBRATE"></uses-permission>
Para comprobar que la aplicacin tiene asignado este permiso, hemos escrito las
siguientes sentencias en la Actividad:
PackageManager p = this.getPackageManager();
if (p.checkPermission("android.permission.VIBRATE",
"es.mentor.unidad5.eje3.permisos")==PackageManager.PERMISSION_DENIED) {
...
260
Intents
Otro aspecto que hay que conocer de los permisos de Android tiene que ver con la
forma en que podemos proteger el acceso de otras aplicaciones a nuestras aplicaciones.
261
En este caso, en lugar de escribir la etiqueta <uses-permission>, utilizamos la
etiqueta <permission> para declarar un permiso al sistema. De igual forma, podemos definir
uno o varios permisos.
Ejemplo de permiso:
<permission
android:name="es.mentor.ejemplo.VER_LISTADO"
android:description="Permite ver el listado de ..."
android:label="Ver listado contactos" />
<activity android:name=".nombreActividad"
android:label="@string/app_name"
android:permission="es.mentor.ejemplo.VER_LISTADO">
<intent-filter>
</intent-filter>
</activity>
De esta forma, nicamente las aplicaciones que hayan solicitado el permiso indicado
podrn acceder al elemento de forma segura. En este contexto, el acceso significa lo
siguiente:
262
Intents
<provider android:name=".nombreProveedor"
android:authorities="es.mentor.ejemplo.proveedor"
android:readPermission="es.mentor.ejemplo.VER_LISTADO"
android:writePermission="es.mentor.ejemplo.ESCRIBIR_LISTADO" />
</provider>
Existen dos formas, en el cdigo fuente Java, de comprobar que las aplicaciones
externas estn solicitando los permisos necesarios para iniciar un componente de nuestra
aplicacin:
263
5.3.8 Notas sobre seguridad en Android
5.4 Tab.Layout
Android tambin permite disear este tipo de interfaces de usuario, si bien lo hace de
una forma caracterstica, ya que la implementacin depende de varios elementos que deben
estar distribuidos y estructurados de una forma determinada.
264
Intents
La parte inferior que alberga el contenido de las pestaas se define con el elemento
FrameLayout y con el id obligatorio @android:id/tabcontent.
Si abrimos el fichero XML del layout, veremos el siguiente cdigo que corresponde a
esta estructura:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_height="match_parent" android:layout_width="match_parent">
<TabHost android:id="@android:id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_parent">
265
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TabWidget android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@android:id/tabs" />
<FrameLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@android:id/tabcontent" >
<LinearLayout android:id="@+id/pest1"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout android:id="@+id/pest2"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</FrameLayout>
</LinearLayout>
266
Intents
</TabHost>
</LinearLayout>
Como viene siendo habitual, lo primero que hacemos es obtener una referencia al
componente principal TabHost y lo inicializamos preparndolo para su configuracin
invocando su mtodo setup().
Despus, creamos un objeto de tipo TabSpec para cada una de las pestaas que
queramos aadir mediante el mtodo newTabSpec(), al que pasamos como parmetro una
etiqueta identificativa de la pestaa; en el ejemplo usamos mipestania1 y mipestania2.
TabHost tabs=(TabHost)findViewById(android.R.id.tabhost);
// Preparamos su configuracin
tabs.setup();
TabHost.TabSpec pestania=tabs.newTabSpec("mipestania1");
pestania.setContent(R.id.pest1);
267
pestania.setIndicator("Pestaa 1",
res.getDrawable(android.R.drawable.ic_menu_agenda));
tabs.addTab(pestania);
pestania=tabs.newTabSpec("mipestania2");
pestania.setContent(R.id.pest2);
pestania.setIndicator("Pestaa 2",
res.getDrawable(android.R.drawable.ic_menu_directions));
tabs.addTab(pestania);
268
Intents
tabs.setOnTabChangedListener(new OnTabChangeListener() {
@Override
tabId, 1).show();
}
});
269
Las Intenciones (Intents) permiten a las aplicaciones de Android expresar la
intencin que desea ejecutar una accin sobre unos datos usando algn
componente de sta o de otra aplicacin.
Para crear una Intencin hay que usar el objeto Intent de Android.
Las Intenciones son mensajes asncronos que se usan para realizar acciones e
intercambiar datos, tanto en la peticin como en la respuesta, entre componentes
de las aplicaciones.
o Implcita: invocando la accin y los datos sobre los que aplicar dicha
accin. Android selecciona, en tiempo de ejecucin, la actividad receptora
que cumple mejor con la accin y los datos solicitados.
Para que Android pueda iniciar un componente de una aplicacin, debe conocer
que existe; para ello, se declaran los componentes de una aplicacin en el fichero
AndroidManifest.xml.
270
Intents
271
BASES DE DATOS Y XML
NDICE
6.1 BASES DE DATOS .......................................................................275
6.1.1 Introduccin ............................................................................275
6.1.2 Teora sobre Bases de Datos ..............................................275
6.1.3 Ventajas de las bases de datos ..........................................279
6.1.4 Bases de datos relacionales ................................................281
6.1.5 Diseo de bases de datos ....................................................283
6.1.1 Introduccin
En esta Unidad vamos a repasar, como prembulo , la teora general sobre bases de
datos.
Android usa SQLite como motor de base de datos relacional. En el siguiente apartado
veremos sus caractersticas. La informacin que mostramos a continuacin est basada en la
versin 3 de SQLite.
275
Generalmente, en las bases de datos relacionales, de las que hablaremos despus, la
informacin est almacenada y organizada en ficheros formados por filas y columnas,
como puede verse en el ejemplo siguiente, en el que se presentan algunos datos de cinco
libros de una biblioteca:
Columnas
Fondo de Cultura
Tener o ser? Erich Fromm
Econmica
Filas
Crnica de una Gabriel Garca
Bruguera
muerte anunciada Mrquez
Hermann
El lobo estepario Anaya Editores
Hesse
Cada fila contiene el ttulo, el autor y la editorial de un libro y se relaciona con las
dems filas gracias a que incluye el mismo tipo de informacin (datos de los libros) y en todas
ellas la informacin est organizada de la misma forma: la primera columna contiene el ttulo
del libro, la segunda, el autor y la tercera, la editorial.
As pues, una base de datos contiene un conjunto de ficheros cuya informacin est
organizada de tal forma que puede ser tratada informticamente con rapidez y eficacia. La
informacin de una base de datos puede almacenarse en un solo fichero o en varios.
Los ficheros de una base de datos estn grabados en el servidor. Tienen un nombre
(por ejemplo, flores, ros, libros, coches, amigos, artculos, clientes, ventas, facturas, etctera).
Su denominacin debe seguir las normas establecidas para que el nombre de un fichero sea
correcto. Como puede verse, hemos prescindido de las tildes en los identificadores que las
llevan ortogrficamente.
El tipo o extensin de estos ficheros de base de datos puede ser muy variado, segn
el tipo de base de datos utilizado: en dBase es dbf (Data Base File, Fichero de Base de Datos),
en Access mdb, en Interbase db o dbf, en MySQL myd, etctera.
276
Introduccin al entorno Android
cualquier extensin e, incluso, sin ella. En otros sistemas operativos, los archivos de una base
de datos de tipo SQLite suelen tener la extensin .sqlite.
As pues, un fichero de base de datos est integrado por registros, que son cada
uno de sus elementos o componentes (flor, ro, libro, coche, amigo, artculo, cliente, venta o
factura). Todos los registros contienen un conjunto de campos en los que se almacena su
informacin; este conjunto define la estructura del fichero que integra una base de datos.
Campos
Registros 5
En las filas aparecen hasta once registros, cada uno de los cuales, en este caso,
contiene los cinco campos siguientes: Nombre, Sueldo, Fecha_nac, Observacion y Foto.
En el ejemplo anterior slo se han incluido once registros y cinco campos, pero de
hecho en las bases de datos que vamos a usar el nmero de registros es ilimitado (depende
de la capacidad del soporte) y el de campos es muy amplio, segn el tipo de base de datos
usada. Todos los registros tienen los mismos campos.
277
Cada campo de un registro tiene un nombre, un tipo, una longitud o ancho, un
nmero de decimales si es de tipo numrico o de coma flotante y un ndice opcional.
Segn el tipo de base de datos que se est utilizando, el identificador del campo, la clase de
tipos y la longitud de los mismos pueden ser diferentes.
Vamos a centrarnos en los tipos de campos que define SQLite. Este tipo de base de
datos no define todos los tipos de campos tpicos en bases de datos relacionales. nicamente
define unos tipos de campos bsicos y luego los reutiliza para especificar otros tipos de
campos.
El nombre de cada campo puede ser muy largo, si bien recomendamos que en el
orden prctico sea lo ms breve posible y tenga algn significado. Debe atenerse a las reglas
de todos los identificadores ya comentadas anteriormente.
Todos estos tipos de campo de texto se pueden definir al crear una tabla, si bien,
internamente, SQLite los traduce por afinidad al tipo TEXT inicial.
2. Campo de tipo Numrico. Se utiliza para escribir nmeros, incluidos los signos
positivo y negativo. Se asigna este tipo a un campo cuando se realizan operaciones
aritmticas con nmeros enteros o reales, como sumar, restar, multiplicar, dividir, etctera.
Admite ndice. SQLite admite estos valores para determinar los campos de este tipo:
INTEGER y REAL. Como puede verse, en realidad los valores posibles se refieren a si es un
campo de nmero entero o decimal.
278
Introduccin al entorno Android
INT
TINYINT
SMALLINT
MEDIUMINT
BIGINT
UNSIGNED BIG INT
INT2
INT8
Todos estos tipos de campo de nmero entero se pueden definir al crear una tabla, si
bien, internamente, SQLite los traduce por afinidad al tipo INTEGER anterior.
En el caso del tipo de campo numrico con decimales, podemos usar los siguientes
tipos de campos:
DOUBLE
DOUBLE PRECISION
FLOAT
Todos estos tipos de campo de nmero con decimales se pueden definir al crear una
tabla, si bien, internamente, SQLite los traduce por afinidad al tipo REAL anterior.
3. Campo de tipo Fecha y Lgico. Puede contener fechas y tiempos (horas, minutos,
segundos) o almacenar valores lgicos (true / false). Admite ndice. SQLite define el tipo de
campo interno NUMERIC para almacenar otros tipos de campos necesarios en las
aplicaciones en una tabla, tales como los campos lgicos o de fecha, as como los que
establecen los decimales exactos en un campo numrico. Podemos usar los siguientes tipos
de campos en SQLite:
DECIMAL(10,5)
BOOLEAN
DATE
DATETIME
Todos estos tipos de campo se pueden definir al crear una tabla, si bien, internamente,
SQLite los traduce por afinidad al tipo NUMERIC anterior.
4. Campo de tipo Memo. Es un campo de longitud variable que admite gran cantidad
de texto o datos binarios segn nuestras necesidades. Para cada registro tendr una longitud
distinta, segn la cantidad de datos que se introduzcan en este campo. No admite ndice.
SQLite admite nicamente BLOB.
Hemos dicho que los archivadores de una biblioteca o de una agenda pueden
considerarse, en cierta forma, bases de datos, pues en ellos se almacena informacin en un
279
determinado orden y es posible buscar esta informacin, consultarla, modificarla o eliminarla
con facilidad.
Sin embargo, todas estas operaciones suelen llevar mucho tiempo y, en ocasiones, no
se efectan tan fcilmente como desearamos. Adems, ocupan bastante espacio si la
informacin es abundante. Incluso, en ocasiones, algunas operaciones fundamentales son
imposibles de realizar manualmente.
Por ejemplo, si tenemos 1.000 fichas bibliogrficas ordenadas por autor y necesitamos
ordenarlas por ttulo, la operacin ha de realizarse manualmente, mirando una a una cada
ficha, lo cual puede hacerse muy largo y pesado. Podamos haber escrito dos ejemplares de
cada ficha, uno para el archivo por autores y otro para el de ttulos, pero esto hubiera llevado
el doble de tiempo, de trabajo y stas ocuparan el doble de espacio.
Supongamos ahora que necesitamos seleccionar todas las fichas en las que aparece
la misma editorial. De nuevo la tarea puede parecernos pesada y larga, y lo es. No digamos si
se cambia la situacin de los libros en los armarios de la biblioteca. Tambin ser necesario
modificar la signatura en las fichas.
Hemos puesto este ejemplo para explicar los graves problemas que se derivan
de la gestin manual de la informacin. Las dificultades aumentan a medida que crece el
volumen de informacin que debe manejarse y segn sean los criterios de ordenacin y
seleccin.
En segundo lugar, el espacio que ocupa una base de datos es mucho menor que el
de cualquier otra forma de archivo manual. En un disco flexible de 3,5 pulgadas puede
almacenarse casi un milln y medio de caracteres. En los discos duros de los actuales
servidores el volumen de informacin puede ser prcticamente ilimitado.
280
Introduccin al entorno Android
las diversas soluciones propuestas para resolver los problemas de estructuracin y acceso a
dicha informacin.
Por ejemplo, en el grfico siguiente puede observarse una tabla que contiene
diversos datos de personas:
Filas
Jos
(Registros) C/ Ro Sil, 11 50 V Dependiente
Rodrguez
3
Columnas (Campos)
Como se ve, una tabla consta de filas y de columnas; en cada columna, denominada
campo en la base de datos, hay un dato: Nombre, Direccin, Edad, etctera; cada fila es un
registro que contiene todos los datos de los elementos de la base. Cada tabla tiene un
281
registro especial, denominado cabecera, que contiene los nombres de los campos y sus
atributos (tipo y longitud).
Generalmente, una base de datos no consta de una sola tabla, sino de varias.
Estas tablas no son independientes unas de otras, sino que tienen al menos un
campo comn con las otras a travs del cual se puede acceder a la informacin que
contienen todas en conjunto.
Por ejemplo, la base de datos de una biblioteca puede estar integrada por una tabla de
libros, otra de lectores, otra de prstamos y otra de editoriales. El fichero de libros puede
contener la informacin completa de cada volumen: ttulo, autor, editorial, ao de edicin,
precio, nmero de pginas, cdigo de materia, nmero de registro, etctera.
El fichero de prstamos contendr datos de este tipo: nmero de registro del libro
prestado, nmero de carn del lector, fecha del prstamo, plazo, etctera.
Como puede verse, la informacin no debe repetirse en todos los ficheros, pero s
debe poder relacionarse. Por ejemplo, los ficheros de libros y editoriales, tienen en comn el
campo EDITORIAL. Los ficheros de libros y prstamos tienen en comn, al menos, el
NMERO DE REGISTRO del libro prestado, gracias a lo cual desde uno se puede acceder a
los datos del otro. Los ficheros de lectores y prstamos tienen en comn el campo CARN,
etctera.
282
Introduccin al entorno Android
Son bases de datos relacionales Microsoft Access, Oracle, SQL Server, MySQL,
SQLite y otras.
Imaginemos que una compaa area quiere gestionar toda la informacin contenida
en una base de datos relativa a los aviones y su mantenimiento, a los vuelos, viajes, destinos,
clientes, personal de la empresa, agencias de viajes, billetes, asistencia, etctera. Es evidente
que, en este caso, la complejidad es enorme y que para realizar el diseo de esta base se
requiere la colaboracin de tcnicos especialistas que faciliten la tarea.
Disear una base de datos consiste en determinar los datos que van a introducirse en
ella, la forma como se van a organizar y el tipo de esos datos. Adems, se debe precisar la
forma como se van a solicitar y las clases de operaciones que hay que realizar con los
mismos: aritmticas, lgicas, de fechas, de carcter, etctera. Tambin conviene conocer los
resultados concretos que se espera obtener: consultas, informes, actualizaciones,
documentos, etctera.
A continuacin, se resumen las operaciones que deben llevarse a cabo al disear una
base de datos:
283
Determinar los datos que debe contener cada uno de esos elementos.
Concretar las operaciones que se van a realizar con los datos: aritmticas, lgicas,
de salida slo por la pantalla, de salida tambin por la impresora, etctera.
Seleccionar el dato o datos esenciales que deben ser el campo clave por el que se
ordenarn las unidades o elementos mencionados.
Fijar los datos comunes a los diferentes ficheros de la base de datos que van a
permitir relacionar la informacin distribuida entre ellos.
Asignar a cada campo una longitud apropiada para tener los datos fundamentales
sin despilfarro de memoria interna ni de espacio en el disco duro o soporte
empleado.
Decidir cul o cules van a ser los campos clave permanentes y situarlos al
principio de la estructura.
Fijar los campos comunes a todos los ficheros para poder relacionarlos con otros
de la misma aplicacin.
Preferencias de la aplicacin
284
Introduccin al entorno Android
En la Unidad 4 hemos tratado las dos primeras formas y en esta Unidad 6 veremos las
bases de datos.
SQLite es un motor de bases de datos relacional muy popular por sus caractersticas,
que son muy especiales, como las siguientes:
Es de cdigo libre.
Usar bases de datos Android puede hacer ms lentas las aplicaciones debido a que es
necesario escribir y leer informacin de la memoria fsica del dispositivo.
Por lo tanto, es recomendable realizar esta operaciones de forma Asncrona, tal como hemos
estudiado en la Unidad 3 (Hilos). En los ejemplos de esta Unidad no vamos a incluir hilos, para
mostrar nicamente las sentencias de SQLite.
285
6.2.3 Creacin de Bases de datos SQLite
La forma usual en Android de crear, modificar y conectar con una base de datos
SQLite consiste en usar la clase Java SQLiteOpenHelper. En realidad, debemos definir una
clase propia que derive de ella y personalizarla para adaptarnos a las necesidades concretas
de la aplicacin.
En el Ejemplo 1 de esta Unidad, vamos a crear una base de datos muy sencilla
llamada BDBiblioteca.db, con una nica tabla interna llamada Ejemplares que albergar
nicamente cinco campos:
prestado: BOOLEAN
286
Introduccin al entorno Android
@Override
db.execSQL(createBDSQL);
@Override
*/
db.execSQL(createBDSQL);
}
}
En el cdigo fuente anterior se define la variable esttica (en Java se definen as las
constantes) createBDSQL, donde se establece la orden SQL para crear la tabla llamada
Ejemplares con los campos alfanumricos descritos anteriormente.
ATENCIN: en este curso no se describe la sintaxis del lenguaje SQL, pues se considera que
el alumno o alumna conoce cmo usar una base de datos relacional.
287
El mtodo onCreate() se ejecuta automticamente cuando es necesario crear la base
de datos, es decir, cuando an no existe y se instala la aplicacin por primera vez. Por lo
tanto, en este mtodo debemos crear todas las tablas necesarias y aadir, si fuera necesario,
los registros iniciales.
Por ejemplo, desarrollamos la versin 1 de la aplicacin que utiliza una tabla con los
campos descritos en el ejemplo anterior. Ms adelante, ampliamos la funcionalidad de la
aplicacin desarrollando la versin 2, que incluye en la tabla el campo "Editorial". Si un usuario
tiene instalada la versin 1 de la aplicacin en su dispositivo Android, la primera vez que
ejecute la versin 2 de la aplicacin hay que modificar la estructura de la tabla, para aadir el
nuevo campo; en este caso, Android ejecutar automticamente el mtodo onUpgrade().
288
Introduccin al entorno Android
Por ltimo, cerramos la conexin con la base de datos llamando al mtodo close(). A
BibliotecaSQLiteHelper bibliodbh =
// Modo escritura
SQLiteDatabase db = bibliodbh.getWritableDatabase();
if(db != null)
db.execSQL(SQLStr);
289
//Cerramos la base de datos
db.close();
/data/data/paquete_java/databases/nombre_del_fichero
/data/data/es.mentor.unidad6.eje1.crearbd/databases/DBBiblioteca.db
Desde Eclipse puedes abrir el proyecto Ejemplo 1 (Crear base de datos SQLite) de la
Unidad 6. Estudia el cdigo fuente y ejectalo para mostrar en el AVD el resultado del
programa anterior, en el que hemos utilizado los mtodos de la base de datos SQLite.
Hemos visto que el fichero de la base de datos del Ejemplo 1 se ha creado en la ruta
correcta. Para comprobar que la tabla se ha creada correctamente y hemos insertado los
registros en la misma, podemos usar dos mtodos:
290
Introduccin al entorno Android
NOTA: a veces, al desarrollar una aplicacin Android con bases de datos, el programador
debe eliminar a mano un fichero porque la estructura creada no es correcta y Android no
elimina el fichero automticamente cada vez que cargamos la aplicacin en el emulador de
Eclipse.
Una vez hemos descargado el fichero a nuestro PC, podemos utilizar cualquier
administrador de SQLite para abrir y consultar la base de datos.
Ten en cuenta que para que este mtodo funcione debemos haber incluido bien el
PATH del SDK de Android en el sistema operativo del PC donde trabajemos. En caso de duda,
conviene repasar el documento de Instalacin de Android de este curso.
Tras obtener este identificador del emulador activo vamos a acceder a su shell
mediante el comando adb -s <identificador-del-emulador> shell.
Una vez conectados a la consola del emulador, podemos acceder a la base de datos
utilizando el comando sqlite3 e indicando la ruta del fichero de la base de datos; en el caso
del ejemplo debemos escribir
sqlite3 /data/data/es.mentor.unidad6.eje1.crearbd/databases/DBBiblioteca.db.
A continuacin, debe aparecer el prompt de SQLite sqlite>, que nos indica que ya
podemos escribir consultas SQL sobre la base de datos.
291
Vamos a comprobar que se han insertado bien los cinco registros del ejemplo en la
tabla Ejemplares. Para ello, escribimos la siguiente orden: SELECT * FROM Ejemplares;.
Para salir del cliente SQLite debemos escribir el comando ".exit" (fjate que lleva un
punto delante) y para abandonar la shell del emulador debemos escribir el comando "exit".
La librera de SQLite incluida en Android proporciona dos formas para llevar a cabo
operaciones sobre una base de datos que no devuelven resultados. Por ejemplo, aadir,
actualizar y eliminar registros de una tabla; tambin se puede crear tablas, ndices de
bsqueda, etctera.
292
Introduccin al entorno Android
//Insertar un registro
db.execSQL("INSERT INTO Ejemplares (titulo, autor, anio, prestado)
VALUES ('Ttulo', 'Autor', 2001, 'false'));
//Eliminar un registro
//Actualizar un registro
Este mtodo se usa para aadir nuevos registros en una tabla de la base de datos. Al
invocar insert (String table, String nullColumnHack, ContentValues values), es necesario
definir tres parmetros:
Los valores que queremos insertar los pasamos como elementos de una coleccin de
tipo ContentValues. Esta coleccin es del tipo duplos de clave-valor, donde la clave es el
nombre del campo de la tabla y el valor es el dato que debemos insertar en dicho campo.
Veamos un ejemplo sencillo:
293
Este mtodo devuelve el campo ID del nuevo registro insertado o el valor -1 si ocurre
algn error durante la operacin.
Estos mtodos se usan para actualizar o borrar registros de una tabla. Los mtodos
update (String table, ContentValues values, String whereClause, String[] whereArgs) y
delete(String table, String whereClause, String[] whereArgs) se invocan de manera
parecida a insert(). En estos mtodos hay que usar el parmetro adicional whereArgs para
indicar la condicin WHERE de la orden SQL.
valores.put("autor","Otro autor");
En el tercer parmetro del mtodo update() indicamos la condicin tal como haramos
en la clusula WHERE en una orden UPDATE de SQL.
El mtodo delete() se aplica de igual forma. Por ejemplo, para eliminar el registro 2
escribimos lo siguiente:
db.delete("Ejemplares", "_id=2");
294
Introduccin al entorno Android
Estos argumentos son piezas variables de la sentencia SQL, en forma de matriz, que
evitan tener que construir una sentencia SQL concatenando cadenas de texto y variables para
formar la orden final SQL.
Existen dos formas de buscar y recuperar registros de una base de datos SQLite. La
primera de ellas consiste en utilizar directamente un comando de consulta SQL; la segunda
forma consiste en utilizar un mtodo especfico con parmetros de consulta a la base de
datos.
295
Tal y como hemos visto anteriormente en algunos mtodos de modificacin de datos,
tambin es posible incluir en este mtodo una lista de argumentos variables que indicamos en
la orden SQL con el smbolo ?; por ejemplo, as:
Cursor c =
db.rawQuery(" SELECT autor,titulo FROM Ejemplares WHERE _id=? ", args);
La clase Cursor dispone de varios mtodos para recorrer y manipular los registros
devueltos por la consulta. Entre ellos podemos destacar dos de los dedicados a recorrer el
cursor de forma secuencial y en orden natural:
296
Introduccin al entorno Android
Una vez colocado el cursor en el registro que queremos leer, podemos utilizar
cualquiera de los mtodos getXXX(ndice_columna) existentes para cada tipo de dato y as
recuperar el dato de cada campo de ese registro.
Por ejemplo, si queremos recuperar la segunda columna del registro actual y sta
contiene un campo alfanumrico, usamos la sentencia getString(1).
Teniendo todo esto en cuenta, veamos, a continuacin, cmo recorrer todos los
registros devueltos por la consulta del ejemplo anterior usando un cursor:
if (c.moveToFirst()) {
297
//Recorremos el cursor mientras haya registros sin leer
do {
} while(c.moveToNext());
Como hemos comentado ya en esta Unidad, las bases de datos SQLite de una
aplicacin son siempre privadas e internas a esta aplicacin. Para que el resto de aplicaciones
pueda acceder a la informacin de la BD, Android define los Content Provider. En la Unidad
7 tratamos este tema en profundidad.
Adems, se pueden tratar datos dinmicos de una base de datos usando la clase de
Android SQLiteQueryBuilder. Esta clase es similar a la interfaz de un proveedor de
contenidos, por lo que suele utilizarse conjuntamente con los Content Providers.
Desde Eclipse puedes abrir el proyecto Ejemplo 2 (Notas) de la Unidad 6. Estudia el cdigo
fuente y ejectalo para mostrar en el AVD el resultado del programa anterior, en el que hemos
utilizado mtodos de la base de datos SQLite.
298
Introduccin al entorno Android
Se trata de una aplicacin donde un usuario puede gestiona notas sencillas por
categoras. Estas notas se almacenan en la base de datos "bdnotas.db" en la tabla "notas"
que tiene la siguiente estructura:
La aplicacin est formada por dos actividades: la primera muestra todas las notas en
un listado y la segunda permite editarlas o dar de alta una nueva. Ambas actividades se
interconectan con Intents invocados de manera explcita.
Para mostrar el listado con las notas en la actividad principal hemos heredado la clase
ListActivity. Como ya hemos visto anteriormente en el curso, esta clase define un ListView
interno. Podemos conectarlo con la clase Cursor, que devuelve los resultados de las consultas
a la BD, usando la clase SimpleCursorAdapter de Android.
299
public class NotasBDHelper extends SQLiteOpenHelper {
private static final String BD_CREAR = "create table notas (_id integer
primary key autoincrement, " + "categoria text not null, titulo
text not null, descripcion text not null);";
// Contructor de la clase
@Override
// Creamos la estructura de la BD
database.execSQL(BD_CREAR);
@Override
int newVersion) {
onCreate(database);
Basada en esta clase anterior vamos a definir la nueva clase NotasBDAdapter, que es
la encargada de hacer las consultas a la base de datos, borrar y actualizar registros de sta.
300
Introduccin al entorno Android
Dentro de esta clase hemos definido el mtodo abrir(), que se conecta a la base de
datos utilizando la clase NotasBDHelper.
Para actualizar y dar de alta registros hemos usado un argumento del tipo
ContentValues, que hemos estudiado en el apartado anterior.
class NotasBDAdapter {
// Campos de la BD
this.contexto = context;
basedatos = bdHelper.getWritableDatabase();
return this;
bdHelper.close();
descripcion);
String descripcion) {
descripcion);
if (mCursor != null) {
mCursor.moveToFirst();
302
Introduccin al entorno Android
return mCursor;
String descripcion) {
values.put(CAMPO_CATEGORIA, categoria);
values.put(CAMPO_TITULO, titulo);
values.put(CAMPO_DESCRIPCION, descripcion);
return values;
El alumno o alumna puede abrir estos ficheros en su ordenador y ver cmo estn
implementados los distintos diseos.
303
6.3.5 Actividades
Como hemos comentado, la aplicacin est formada por dos actividades: la actividad
principal (NotasActivity) muestra un listado con todas las notas y la segunda (GestionarNota)
sirve para editarlas o dar de alta una nueva.
@Override
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.getListView().setDividerHeight(3);
bdHelper.abrir();
cargaDatos();
registerForContextMenu(getListView());
@Override
inflater.inflate(R.menu.menulistado, menu);
304
Introduccin al entorno Android
return true;
@Override
switch (item.getItemId()) {
case R.id.insertar:
startActivityForResult(i, ACTIVIDAD_NUEVA);
return true;
// El usuario hace clic en una opcin del men contextual del listado
@Override
switch (item.getItemId()) {
case MENU_ID:
bdHelper.borraNota(info.id);
cargaDatos();
return true;
return super.onContextItemSelected(item);
305
}
@Override
i.putExtra(NotasBDAdapter.CAMPO_ID, id);
startActivityForResult(i, ACTIVIDAD_EDITAR);
@Override
Intent intent) {
cursor = bdHelper.obtenerNotas();
startManagingCursor(cursor);
306
Introduccin al entorno Android
// asociado al cursor
setListAdapter(notas);
@Override
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
@Override
super.onDestroy();
if (bdHelper != null) {
bdHelper.cerrar();
@Override
super.onCreate(bundle);
bdHelper.abrir();
setContentView(R.layout.editar_nota);
descripcionText = (EditText)
findViewById(R.id.nota_editar_descripcion);
filaId = null;
if (extras != null) {
filaId = extras.getLong(NotasBDAdapter.CAMPO_ID);
cargarRegistro();
aceptaBoton.setOnClickListener(new View.OnClickListener() {
308
Introduccin al entorno Android
// Alta de registro
if (filaId == null) {
setResult(RESULT_OK);
// Acabamos la actividad
finish();
});
} // end onCreate
if (filaId != null) {
startManagingCursor(nota);
309
String s = (String) categoriaSpinner.getItemAtPosition(i);
if (s.equalsIgnoreCase(categoria)){
categoriaSpinner.setSelection(i);
break;
tituloText.setText(nota.getString(
nota.getColumnIndexOrThrow(NotasBDAdapter.CAMPO_TITULO)));
descripcionText.setText(nota.getString(
nota.getColumnIndexOrThrow(NotasBDAdapter.CAMPO_DESCRIPCION)));
} // end cargarRegistro
<activity android:name=".GestionarNota"
android:windowSoftInputMode="stateVisible|adjustResize">
</activity>
310
Introduccin al entorno Android
En este ejemplo hemos usado las opciones stateVisible y adjustResize para que el
teclado se muestre cuando el usuario acceda a un componente de introduccin de texto y
cambie las proporciones de la pantalla para hacer un "hueco" al teclado.
En la ayuda oficial de Android puedes encontrar todos los posibles valores con su
descripcin.
Los tres modelos ms extendidos para leer y escribir ficheros de tipo XML son DOM
(Document Object Model), SAX (Simple API for XML) y StAX (Streaming API for XML):
311
DOM: vuelca el documento XML en la memoria del dispositivo en forma de
estructura de rbol, de manera que se puede acceder aleatoriamente a los
elementos de las ramas.
StAX: es una mezcla de las dos modelos anteriores. En este caso, tambin se
lee el fichero XML de forma secuencial, pero podemos controlar la forma en
que se leen sus elementos. En el caso de SAX es obligatorio leer todos los
elementos a la vez. Este modelo es tambin mucho ms rpido que DOM,
pero algo ms lento de SAX.
Android dispone de analizadores XML para estos tres modelos. Con cualquiera de
ellos podemos hacer las mismas tareas. Ya veremos ms adelante que, dependiendo de la
naturaleza de la aplicacin, es ms eficiente utilizar un modelo u otro.
Estas tcnicas se pueden utilizar para leer cualquier documento XML, tanto de Internet
como del sistema de archivos. En el Ejemplo 3 de esta Unidad vamos a leer datos XML de un
documento RSS de un peridico; concretamente, del canal RSS de noticias de
20minutos.es.
"http://20minutos.feedsportal.com/c/32489/f/478284/index.rss";
Si abrimos el documento RSS de esta fuente de noticias (en ingls feed), vemos la
estructura siguiente:
<rss version="2.0">
<channel>
<title>20minutos.es</title>
<link> http://www.20minutos.es/</link>
<language>es-ES</language>
312
Introduccin al entorno Android
<item>
<link>http://link_de_la_noticia_2.es</link>
</item>
<item>
<link>http://link_de_la_noticia_2.es</link>
</item>
...
</channel>
</rss>
En este apartado vamos a describir cmo leer este archivo XML sirvindonos de cada
una de las tres alternativas citadas anteriormente.
Desde Eclipse puedes abrir el proyecto Ejemplo 3 (XML) de la Unidad 6. Estudia el cdigo
fuente y ejectalo para mostrar en el AVD el resultado del programa anterior, en el que hemos
utilizado mtodos de lectura del formato XML.
313
Para implementar la Actividad principal, hemos usado la clase ListActivity, donde
mostraremos un listado con las noticias.
Para empezar, en primer lugar debemos definir una clase Java para almacenar los
datos ledos de una noticia. Para cargar el listado de la clase ListActivity con los titulares de
las noticias usamos una lista de objetos de este tipo. Veamos el cdigo fuente de esta clase
que hemos denominado Noticia:
// Clase que sirve para cargar en un objeto cada noticia que leamos del
fichero XML
return enlace;
314
Introduccin al entorno Android
try {
} catch (MalformedURLException e) {
while (!fecha.endsWith("00")){
fecha += "0";
this.fecha = fecha.trim();
return this.fecha;
return titulo;
this.titulo = titulo;
return descripcion;
this.descripcion = descripcion;
Por simplificacin, hemos tratado todos los datos como cadenas de texto.
315
6.4.1 SAX es el modelo clsico en Android
Por ejemplo, a medida que lee el documentos XML, si el analizador encuentra una
etiqueta <title> lanzar el mtodo startElement() del parser de inicio de etiqueta con la
informacin asociada. Si despus de esa etiqueta encuentra una cadena de texto, invocar el
mtodo characters() del parser con toda la informacin necesaria.
Por lo tanto, debemos implementar las sentencias necesarias para tratar cada uno de
los mtodos posibles que el analizador puede lanzar durante la lectura del documento XML.
316
Introduccin al entorno Android
return noticias;
@Override
super.startDocument();
@Override
if (localName.equals(EtiquetasRSS.ITEM)) {
@Override
throws SAXException {
if (this.noticiaActual != null) {
317
// Cargamos el campo correspondiente de la etiqueta que acabamos
de leer
if (localName.equals(EtiquetasRSS.TITLE)) {
noticiaActual.setTitulo(sbTexto.toString());
} else if (localName.equals(EtiquetasRSS.LINK)) {
noticiaActual.setEnlace(sbTexto.toString());
} else if (localName.equals(EtiquetasRSS.DESCRIPTION)) {
noticiaActual.setDescripcion(sbTexto.toString());
} else if (localName.equals(EtiquetasRSS.PUB_DATE)) {
noticiaActual.setFecha(sbTexto.toString());
} else if (localName.equals(EtiquetasRSS.ITEM)) {
noticias.add(noticiaActual);
sbTexto.setLength(0);
@Override
throws SAXException {
if (this.noticiaActual != null)
318
Introduccin al entorno Android
Una vez hemos implementado nuestro handler, vamos a crear la nueva clase
ParserSaxClasico que hace uso de este handler para analizar un documento XML en
concreto usando el modelo SAX.
Esta clase nicamente define un constructor que recibe como parmetro la direccin
de Internet del documento XML que hay que analizar. El mtodo pblico analizar() analiza el
documento XML y devuelve como resultado una lista de noticias. Veamos cmo queda esta
clase:
try
catch (MalformedURLException e)
try {
319
SAXParser parser = factory.newSAXParser();
parser.parse(getInputStream(), handler);
return handler.getNoticias();
} catch (Exception e) {
try
return feedUrl.openConnection().getInputStream();
catch (IOException e)
El constructor de la clase anterior acepta como parmetro una direccin URL del
documento XML que analiza y controla la validez de dicha direccin generando una
excepcin si no puede crear la clase URL correspondiente.
Por otra parte, el mtodo analizar() es el encargado de crear un nuevo parser SAX y
de iniciar el proceso de anlisis pasando al parser una instancia del handler que hemos
creado anteriormente con una referencia al documento XML en forma de stream.
320
Introduccin al entorno Android
// Creamos un objeto del parser (analizador XML) en funcin del tipo (opcin
// men principal). La direccin (URL) de la fuente de noticias es una
//constante en este ejemplo
noticias = analizador.analizar();
titulos.add(msg.getTitulo());
ArrayAdapter<String> adaptador =
this.setListAdapter(adaptador);
321
Primero creamos el parser correspondiente usando la direccin URL del documento
XML y, despus, ejecutamos el mtodo analizar() para obtener una lista de objetos de tipo
Noticia que, posteriormente, asignamos al adaptador del listado de la Actividad principal.
switch (tipo){
case SAX_CLASICO:
case DOM:
case SAX_SIMPLIFICADO:
case XML_PULL:
Observa que, como estamos usando la misma aplicacin para mostrar cmo
funcionan todos los modelos de carga de archivos XML en Android, hemos creado una clase
abstracta que devuelve un objeto en funcin del tipo de analizador que el usuario ha decido
usar en ese momento.
NOTA: Para que esta aplicacin Android acceda a Internet, es necesario declararlo en el
fichero AndroidManifest.xml, que requiere el permiso "android.permission.INTERNET".
322
Introduccin al entorno Android
Posteriormente, hay que distinguir con unas sentencias IF entre todas las etiquetas
posibles la accin que debemos realizar.
Tengamos en cuenta que hemos usado un documento XML muy sencillo, pero si
tratamos un documento XML ms enrevesado, la complejidad de este handler aumenta
mucho y pueda dar lugar a errores de programacin.
Para evitar estos problemas, Android propone una variante del modelo SAX que evita
definir una clase separada para el handler y que permite asociar directamente las acciones a
etiquetas concretas dentro de la estructura del documento XML.
Veamos cmo queda el analizador XML utilizando SAX simplificado para Android:
// Variable que define la etiqueta raz del XML que es <rss >
try
catch (MalformedURLException e)
/*
*/
item.setStartElementListener(new StartElementListener(){
});
item.setEndElementListener(new EndElementListener(){
noticias.add(noticiaActual);
});
324
Introduccin al entorno Android
item.getChild(EtiquetasRSS.TITLE).setEndTextElementListener(new
EndTextElementListener(){
noticiaActual.setTitulo(body);
});
item.getChild(EtiquetasRSS.LINK).setEndTextElementListener(new
EndTextElementListener(){
noticiaActual.setEnlace(body);
});
item.getChild(EtiquetasRSS.DESCRIPTION).setEndTextElementListener(new
EndTextElementListener(){
noticiaActual.setDescripcion(body);
});
item.getChild(EtiquetasRSS.PUB_DATE).setEndTextElementListener(new
EndTextElementListener(){
noticiaActual.setFecha(body);
});
try {
Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8,
root.getContentHandler());
} catch (Exception e) {
325
// Devolvemos las noticias ledas
return noticias;
try
return feedUrl.openConnection().getInputStream();
catch (IOException e)
}
}
En este nuevo modelo SAX simplificado de Android las acciones que debemos realizar
dentro de cada mtodo se definen dentro de la misma clase asociadas a etiquetas concretas
del XML.
Para esto, lo primero que hacemos es navegar por la estructura del archivo XML hasta
encontrar las etiquetas que tenemos que tratar y asignarlaa a algunos mtodos de tipo
listeners ("escuchadores") disponibles como StartElementListener() de inicio de etiqueta o
EndElementListener() de finalizacin de etiqueta, incluyendo las sentencias oportunas dentro
de estos mtodos.
Por ejemplo, para obtener el elemento <item>, en primer lugar buscamos el elemento
raz del XML (<rss>) declarando un objeto RootElement. Despus, accedemos a su elemento
hijo <channel> y, finalmente, obtenemos de ste ltimo el elemento hijo <item>. En cada
"salto" hemos utilizado el mtodo getChild().
Una vez hemos llegado a la etiqueta buscada, asignamos los listeners necesarios. En
este caso, uno de apertura y otro de cierre de etiqueta item, donde inicializamos la noticia
actual y la aadimos a la lista final, respectivamente, de forma similar a como lo hemos hecho
para el modelo SAX clsico.
326
Introduccin al entorno Android
Para acabar, arrancamos todo el proceso de anlisis del XML llamando al mtodo
parse(), definido en la clase android.Util.Xml, al que pasamos como parmetros el stream del
archivo XML, la codificacin del documento XML y un handler SAX obtenido directamente del
objeto RootElement definido anteriormente.
Hay que tener en cuenta que el modelo clsico es tan vlido y eficiente como ste, que
nicamente simplifica el trabajo al programador.
Al acabar la lectura del documento XML este modelo devuelve todo su contenido en
una estructura de tipo rbol, donde los distintos elementos del fichero XML se representan en
forma de nodos y su jerarqua padre-hijo se establece mediante relaciones entre dichos nodos.
<noticias>
<noticia>
<titulo>Ttulo 1</titulo>
<enlace>Enlace 1</link>
</noticia>
<noticia>
<titulo>Ttulo 2</titulo>
<enlace>Enlace 2</link>
</noticia>
<noticias>
327
Como vemos, este rbol conserva la misma informacin del fichero XML, pero en
forma de nodos y relaciones entre nodos. Por esta razn es sencillo buscar fcilmente dentro
de la estructura un elemento en concreto.
Este rbol se conserva en memoria una vez ledo el documento completo, lo que
permite procesarlo en cualquier orden y tantas veces como sea necesario, a diferencia del
modelo SAX, donde el tratamiento es secuencial y siempre desde el principio hasta el final del
documento. Es decir, no se puede volver atrs una vez finalizada la lectura del documento
XML.
El modelo DOM de Android ofrece una serie de clases y mtodos que permiten cargar
la informacin de la forma descrita y facilitan la bsqueda de elementos dentro de la estructura
creada.
try
328
Introduccin al entorno Android
catch (MalformedURLException e)
try {
// Leemos el item i
329
if (nombre.equalsIgnoreCase(EtiquetasRSS.TITLE)){
noticia.setTitulo(contenido.getFirstChild().getNodeValue());
} else if (nombre.equalsIgnoreCase(EtiquetasRSS.LINK)){
noticia.setEnlace(contenido.getFirstChild().getNodeValue());
} else if (nombre.equalsIgnoreCase(EtiquetasRSS.DESCRIPTION)){
} else if (nombre.equalsIgnoreCase(EtiquetasRSS.PUB_DATE)){
noticia.setFecha(contenido.getFirstChild().getNodeValue());
} // end for j
noticias.add(noticia);
} // end for i
} catch (Exception e) {
return noticias;
try
return feedUrl.openConnection().getInputStream();
catch (IOException e)
330
Introduccin al entorno Android
Despus, nicamente hay que leer el documento XML invocando el mtodo parse()
del parser DOM, pasndole como parmetro el stream de entrada del fichero.
Para esto, lo primero que hacemos es acceder al nodo raz (root) del rbol utilizando el
mtodo getDocumentElement(); en este ejemplo es la etiqueta <rss>,.
Una vez que sabemos dnde est el nodo raz, vamos a buscar todos los nodos con la
etiqueta <item>. Para esto, usamos el mtodo de bsqueda por el nombre de etiqueta
getElementsByTagName(nombre_de_etiqueta), que devuelve una lista de tipo NodeList
con todos los nodos hijos del nodo actual cuya etiqueta coincida con el nombre indicado.
Una vez hemos obtenido todos los elementos <item> que contienen cada noticia,
vamos a recorrerlos de uno en uno para crear todos los objetos de tipo Noticia necesarios.
Para cada uno de estos elementos obtenemos sus nodos hijos mediante el mtodo
getChildNodes(). Despus, recorremos estos nodos hijos obteniendo su texto y
almacenndolo en el campo correspondiente del objeto Noticia. Para saber qu etiqueta
representa cada nodo hijo utilizamos el mtodo getNodeName().
331
public class ParserXmlPull implements RSSParser {
try
catch (MalformedURLException e)
try {
parser.setInput(this.getInputStream(), null);
332
Introduccin al entorno Android
switch (eventType){
case XmlPullParser.START_DOCUMENT:
break;
// Etiqueta de incicio
case XmlPullParser.START_TAG:
nombre = parser.getName();
if (nombre.equalsIgnoreCase(EtiquetasRSS.ITEM)){
if (nombre.equalsIgnoreCase(EtiquetasRSS.LINK)){
noticiaActual.setEnlace(parser.nextText());
} else if
(nombre.equalsIgnoreCase(EtiquetasRSS.DESCRIPTION)){
noticiaActual.setDescripcion(parser.nextText());
} else if
(nombre.equalsIgnoreCase(EtiquetasRSS.PUB_DATE)){
noticiaActual.setFecha(parser.nextText());
} else if (nombre.equalsIgnoreCase(EtiquetasRSS.TITLE)){
noticiaActual.setTitulo(parser.nextText());
break;
// Etiqueta de cierre
case XmlPullParser.END_TAG:
nombre = parser.getName();
if (nombre.equalsIgnoreCase(EtiquetasRSS.ITEM) &&
noticiaActual != null){
noticias.add(noticiaActual);
} else if (nombre.equalsIgnoreCase(EtiquetasRSS.CHANNEL)){
333
docAcabado = true;
break;
eventType = parser.next();
} // end while
} catch (Exception e) {
return noticias;
try
return feedUrl.openConnection().getInputStream();
catch (IOException e)
}
}
334
Introduccin al entorno Android
Una vez identificado el tipo de evento, podemos consultar el nombre de la etiqueta del
elemento XML mediante parser.getName() y el texto contenido mediante parser.nextText().
distintos modelos de tratamiento de ficheros XML pulsando la tecla men del emulador:
335
Si haces clic sobre una noticia vers que Android te permite seleccionar el navegador
que quieres usar para iniciar la accin Intent.ACTION_VIEW que permite abrir una pgina
Web:
336
Introduccin al entorno Android
En Android las bases de datos son privadas y nicamente una aplicacin puede
acceder a ellas para leer y escribir datos.
Para compartir informacin de base de datos entre aplicaciones Android hay que
usar los Content Providers.Explcita: invocando la clase Java del componente que
queremos ejecutar. Normalmente, se hace para invocar componentes de una
misma aplicacin.
Usar bases de datos Android hace ms lentas las aplicaciones debido a que es
necesario escribir y leer informacin de la memoria fsica del dispositivo. Por esto,
es recomendable usar hilos de ejecucin.
La forma usual en Android de crear, modificar y conectar con una base de datos
SQLite consiste en usar la clase Java SQLiteOpenHelper.
Los tres modelos ms extendidos para leer y escribir ficheros de tipo XML son
DOM (Document Object Model), SAX (Simple API for XML) y StAX (Streaming API
for XML).
337
El modelo DOM vuelca el documento XML en la memoria del dispositivo en
forma de estructura de rbol, de manera que se puede acceder aleatoriamente a
los elementos de las ramas.
El modelo SAX se basa en eventos. La aplicacin recorre todos los elementos del
archivo XML de una sola vez. La ventaja respecto a la anterior es que es ms rpido
y requiere menos memoria, si bien no permite el acceso aleatorio a una de sus
ramas.
El modelo StAX es una mezcla de las dos modelos anteriores. En este caso,
tambin se lee el fichero XML de forma secuencial, pero podemos controlar la
forma en que se leen los elementos. Este modelo es tambin mucho ms rpido
que DOM, pero algo ms lento que SAX.
338
CONTENT PROVIDERS,
SERVICIOS Y
NOTIFICACIONES
NDICE
7.1 CONTENT PROVIDERS ............................................................. 341
7.1.1 Introduccin ................................................................... 341
7.1.2 Proveedores de contenido (Content Providers) ....... 341
7.1.3 Construccin de un Content Provider ........................ 342
7.1.1 Introduccin
Una aplicacin que desee compartir de manera controlada parte de la informacin que
almacena con resto de aplicaciones debe declarar un Content Provider al sistema operativo a
travs del cul se realiza el acceso a dicha informacin.
Android, de serie, incluye varios proveedores de contenido para los tipos de datos ms
comunes, como audio, vdeo, imgenes, agenda de contactos personal, etctera. Puedes ver el
listado completo en el paquete android.provider.
En este apartado vamos a tratar dos funcionalidades diferenciadas, que son las
siguientes:
En la Unidad 5 ya hemos visto un ejemplo muy sencillo sobre del acceso a un Content
Provider ya existente, concretamente en la lista de contactos de Android.
341
Dado que es importante conocer el funcionamiento interno de un Content Provider,
antes de pasar a utilizarlo en nuestras aplicaciones, vamos a estudiar cmo se construye.
Por simplificacin, ser la misma aplicacin la que acceda al Content Provider interno,
si bien el cdigo necesario desde otra aplicacin es exactamente el mismo.
Fjate que en este ejemplo los botones "Insertar" y "Eliminar" son excluyentes. Slo se
puede borrar un alumno si previamente ha sido dado de alta y viceversa.
La aplicacin del colegio almacena la informacin que queremos compartir en una base
de datos SQLite.
342
Content Providers, servicios y notificaciones
El Content Provider es el mecanismo que permite compartir estos datos con otras
aplicaciones de una forma homognea usando una interfaz estandarizada.
Las tablas de la base de datos SQLite usadas por un Content Provider deben incluir siempre el
campo _ID que identifica sus registros de forma unvoca.
En este ejemplo, los registros devueltos por el Content Provider de alumnos tiene este
aspecto:
Lo primero que hemos hecho en este Ejemplo es crear una aplicacin muy simple que
almacena y consulta los datos de los alumnos con la estructura similar a la tabla anterior.
Para esto, aplicamos los mismos conceptos que ya hemos estudiado en la Unidad 6
para el tratamiento de bases de datos.
343
public ColegioSqliteHelper(Context contexto, String nombre,
@Override
db.execSQL(sqlCreate);
String[] cursos={"1 ESO", "1 ESO", "2 ESO", "3 ESO", "1 ESO",
"4 ESO", "2 ESO", "2 ESO", "1 ESO", "4 ESO"};
@Override
344
Content Providers, servicios y notificaciones
db.execSQL(sqlCreate);
}
}
Fjate que hemos incluido el campo _id en la tabla de la base de datos de alumnos.
Este campo lo declaramos como INTEGER PRIMARY KEY AUTOINCREMENT para que se
incremente automticamente cada vez que insertamos un nuevo registro en la tabla.
Adems, esta clase aade algunos registros de ejemplo para poder hacer pruebas.
Una vez que ya contamos con una aplicacin que ha definido su base de datos, vamos
a construir el nuevo Content Provider que permite compartir sus datos con otras aplicaciones.
Los identificadores URI de los Content Providers se pueden dividir en tres partes:
Prefijo content://: indica que dicho recurso debe ser tratado por un Content
Provider.
A continuacin, vamos a crear el Content Provider de la aplicacin. Para esto, hay que
extender la clase ContentProvider. Esta clase dispone de los mtodos abstractos siguientes ,
que podemos implementar:
345
onCreate(): se usa para inicializar todos los recursos necesarios para el
funcionamiento del nuevo Content Provider.
"content://es.mentor.unidad7.ejemplo/alumnos";
private Alumnos() {}
Por ltimo, vamos a definir varias cadenas constantes privadas que almacenen
informacin auxiliar con el nombre de la base de datos, su versin y la tabla a la que accede el
Content Provider.
Lo primero que debe hacer un Content Provider cuando otra aplicacin le solicita una
operacin es interpretar el URI utilizado. Para facilitar esta tarea al programador, Android
proporciona la clase llamada UriMatcher que interpreta los patrones en un URI.
Esto es muy til para determinar, por ejemplo, si un URI hace referencia a una tabla
genrica o a un registro concreto a travs de su ID:
Para ello definimos tambin en esta clase un objeto UriMatcher y dos nuevas
constantes que representan los dos tipos de URI que hemos indicado: acceso genrico a la
tabla (ALUMNOS) o acceso a un alumno por ID (ALUMNOS_ID).
static {
347
En el cdigo anterior vemos que mediante el mtodo addUri() indicamos el campo
authority del URI, el formato de la entidad que estamos solicitando y el tipo que identifica el
formato del dato. Ms adelante veremos cmo utilizar esto de forma prctica.
return true;
}
El mtodo ms importante del Content Provider es query(). Este mtodo recibe como
parmetros un URI, una lista de nombres de columna, un criterio de seleccin, una lista de
valores para las variables utilizadas en el criterio anterior y un criterio de ordenacin.
Todos estos parmetros son similares a los que estudiamos cuando tratamos sobre las
bases de datos SQLite para Android.
El mtodo query() devuelve los datos solicitados segn el URI, los criterios de
seleccin y ordenacin indicados como parmetros. As, si el URI hace referencia a un alumno
en concreto por su ID, se debe ser el nico registro devuelto. Si se solicita el contenido de la
tabla de alumnos, hay que realizar la consulta SQL correspondiente a la base de datos
respetando los criterios pasados como parmetros.
Para distinguir entre los dos tipos posibles de URI utilizamos el mtodo match() del
objeto uriMatcher. Si el tipo devuelto es ALUMNOS_ID, es decir, se ha solicitado informacin
de un alumno en concreto, sustituimos el criterio de seleccin por uno que busca en la tabla de
alumnos slo el registro con el ID indicado en la URI. Para obtener este ID utilizamos el mtodo
getLastPathSegment() del objeto uri, que extrae el ltimo elemento de la URI, en este caso el
ID del alumno.
Despus, hay que realizar la consulta a la base de datos mediante el mtodo query()
de SQLiteDatabase. Esto es muy fcil, ya que los parmetros son similares a los empleados
en el mtodo query() del Content Provider:
SQLiteDatabase db = colegioBDhelper.getReadableDatabase();
348
Content Providers, servicios y notificaciones
if(uriMatcher.match(uri) == ALUMNOS_ID){
// Hacemos la consulta a la BD
return c;
}
Podemos observar que los resultados se devuelven en forma de Cursor, tal y como lo
hace el mtodo query() de SQLiteDatabase.
Por otra parte, los mtodos update() y delete() son completamente similares al mtodo
anterior. nicamente se diferencian en que stos devuelven como resultado el nmero de
registros afectados en lugar de un cursor. Veamos su cdigo:
@Override
// Variable temporal
int cont;
SQLiteDatabase db = colegioBDhelper.getWritableDatabase();
if(uriMatcher.match(uri) == ALUMNOS_ID){
// Actualizamos la tabla
return cont;
349
@Override
// Variable temporal
int cont;
SQLiteDatabase db = colegioBDhelper.getWritableDatabase();
if(uriMatcher.match(uri) == ALUMNOS_ID){
return cont;
SQLiteDatabase db = colegioBDhelper.getWritableDatabase();
return newUri;
}
350
Content Providers, servicios y notificaciones
Por ltimo, slo queda implementar el mtodo getType(). Este mtodo se utiliza para
identificar el tipo de datos que devuelve el Content Provider. Este tipo de datos se expresa
como un MIME Type, tal y como hacen los navegadores Web para determinar qu tipo de
datos se est recibiendo al hacer una peticin a un servidor. Identificar el tipo de datos que
devuelve un Content Provider ayuda a Android a determinar qu aplicaciones son capaces de
procesar dichos datos.
En este ejemplo, existen dos tipos MIME distintos para cada entidad del Content
Provider: el primero se usa cuando se devuelve un registro nico concreto y el segundo cuando
se devuelven varios registros simultneamente. As, podemos utilizar los siguientes patrones
para definir uno u otro tipo de datos:
vnd.android.cursor.dir/vnd.mentor.alumno
vnd.android.cursor.item/vnd.mentor.alumno
@Override
switch (match)
case ALUMNOS:
return "vnd.android.cursor.dir/vnd.mentor.alumno";
case ALUMNOS_ID:
return "vnd.android.cursor.item/vnd.mentor.alumno";
default:
return null;
<application android:icon="@drawable/icon"
android:label="@string/app_name">
...
<provider android:name=".ColegioContentProvider"
android:authorities="es.mentor.unidad7.ejemplo"/>
</application>
Para ello, vamos a usar la clase ContentResolver de Android que permite realizar
acciones (consultas de datos, actualizaciones de informacin, etctera) con cualquier Content
Provider que est disponible en el sistema operativo Android.
Una vez obtenida esta referencia, podemos utilizar sus mtodos query(), update(),
insert() y delete() para realizar las acciones equivalentes sobre el Content Provider.
En la aplicacin del ejemplo anterior hay tres botones en la pantalla principal: uno para
hacer una consulta de todos los alumnos, otro para insertar registros nuevos y el ltimo para
eliminar todos los registros nuevos insertados con el segundo botn.
Primero definimos una matriz con los nombres de las columnas de la tabla que
queremos recuperar en el resultado de la consulta: ID, nombre, apellidos y curso.
352
Content Providers, servicios y notificaciones
En este caso, para no complicar el ejemplo tan slo indicamos los dos primeros:
CONTENT_URI del Content Provider y la matriz de columnas que acabamos de definir:
//Columnas de la tabla
Alumnos._ID,
Alumnos.COL_NOMBRE,
Alumnos.COL_APELLIDOS,
Alumnos.COL_CURSO };
ContentResolver cr = getContentResolver();
//Hacemos la consulta
Una vez solicitada la consulta, hay que recorrer el cursor para procesar los registros.
Veamos cmo queda el cdigo fuente:
// Si obtenemos resultados
if (cur.moveToFirst())
String nombre;
String apellidos;
String curso;
353
int colNombre = cur.getColumnIndex(Alumnos.COL_NOMBRE);
txtResultados.setText("Resultado consulta:\n\n");
do {
nombre = cur.getString(colNombre);
apellidos = cur.getString(colApellidos);
curso = cur.getString(colCurso);
values.put(Alumnos.COL_NOMBRE, "Jess");
values.put(Alumnos.COL_APELLIDOS, "Sanz");
values.put(Alumnos.COL_CURSO, "BACHIDERATO");
ContentResolver cr = getContentResolver();
cr.insert(ColegioContentProvider.CONTENT_URI, values);
ContentResolver cr = getContentResolver();
354
Content Providers, servicios y notificaciones
Hemos visto lo sencillo que resulta acceder a los datos proporcionados por un Content
Provider.
Para ver cmo se usan los Content Providers con un tipo de datos definido por Android, en
el Ejemplo 2 de esta Unidad vamos a consultar el historial de llamadas del dispositivo, usando
el Content Provider android.provider.CallLog.
Para poder ver algn dato en este ejemplo, en primer lugar, vamos a registrar varias
llamadas en el emulador de Android. As, los resultados de la consulta al historial de llamadas
devolvern algunos registros.
Las llamadas salientes son sencillas de realizar usando el emulador como si se tratara de
un telfono normal y corriente. Accedemos al icono telfono, marcamos un nmero y
descolgamos como si se tratara de un dispositivo fsico:
355
Para simular llamadas entrantes debemos acceder desde Eclipse a la vista del DDMS. En
esta vista, en la pestaa Emulator Control aparece el apartado Telephony Actions, donde
podemos introducir un nmero cualquiera de telfono origen Incoming number y pulsar el
botn Call para que el dispositivo del emulador reciba una llamada entrante.
Una vez hemos simulado tanto llamadas entrantes como llamadas salientes, vamos a
desarrollar una aplicacin que consulte el historial de llamadas.
A continuacin, definimos una matriz con las columnas que vamos a recuperar,
obtenemos la referencia al Content Resolver de la aplicacin y ejecutamos la consulta
llamando al mtodo query(). Por ltimo, recorremos el cursor obtenido y procesamos los
resultados. Veamos el cdigo fuente:
356
Content Providers, servicios y notificaciones
ContentResolver cr = getContentResolver();
if (cur.moveToFirst())
int tipo;
String telefono;
txtResultados.setText("");
do
tipo = cur.getInt(colTipo);
telefono = cur.getString(colTelefono);
if(tipo == Calls.INCOMING_TYPE)
tipoLlamada = "ENTRADA";
357
tipoLlamada = "SALIDA";
tipoLlamada = "PERDIDA";
// Mostramos la informacin
Para que la aplicacin pueda acceder al historial de llamadas del dispositivo hay que
incluir en el fichero AndroidManifest.xml el permiso READ_CONTACTS:
<uses-permission android:name="android.permission.READ_CONTACTS">
</uses-permission>
358
Content Providers, servicios y notificaciones
Cuando una aplicacin Android define sus propios Servicios, deben ser declarados en
el fichero AndroidManifest.xml del proyecto.
Adems, un componente de la aplicacin puede unirse (en ingls bind) al servicio para
interactuar con l e incluso realizar comunicaciones entre procesos. Por ejemplo, un servicio
podra conectarse a Internet en un segundo plano para descargar noticias, reproducir msica,
etctera,.
Una aplicacin puede declarar su propio servicio para llevar a cabo operaciones que
tarden en ejecutarse y no necesiten interactuar con el usuario o para suministrar una nueva
funcionalidad a otras aplicaciones.
A continuacin, se muestra un esquema con los mtodos que invoca Android cuando
lanzamos un servicio segn su modo de funcionamiento:
360
Content Providers, servicios y notificaciones
Una Actividad puede iniciar un servicio en modo Autnomo a travs del mtodo
StartService() y detenerlo mediante el mtodo StopService(). Cuando lo hacemos, Android
invoca su mtodo onCreate(); despus, se invoca el mtodo onStartCommand() con los datos
proporcionados por la Intencin de la actividad.
Si la actividad quiere interactuar con un servicio (modo Dependiente o Ligado) para, por
ejemplo, mostrar el progreso de una operacin, puede utilizar el mtodo bindService(). Para
esto, hay que usar el objeto ServiceConnection, que permite conectarse al servicio y devuelve
un objeto de tipo IBinder, que la actividad puede utilizar para comunicar con el servicio. Ms
adelante veremos en detalle cmo definir servicios en modo Ligado dentro de las aplicaciones
Android.
Hay casos en los que se usan mensajes de difusin (Broadcast) para comunicar
eventos entre servicios. Estos mensajes son, en realidad, Intents.
Este tipo de Intenciones se usa mucho para iniciar aplicaciones como el Administrador
de notificaciones (Notification Manager) y Administrador de alarmas (Alarm Manager).
361
Para enviar un mensaje de difusin mediante una Intencin pendiente hay que usar su
mtodo getBroadcast(). Para iniciar una subactividad mediante una Intencin pendiente hay
que usar su mtodo getActivity().
Para que la aplicacin funcione bien, debemos incluir las siguientes sentencias en el
archivo AndroidManifest.xml del proyecto:
<application android:icon="@drawable/icon"
android:label="@string/app_name">
<receiver android:name="ReceptorLlamadas">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE">
</action>
</intent-filter>
</receiver>
</application>
...
<uses-permission android:name="android.permission.READ_PHONE_STATE">
</uses-permission>
362
Content Providers, servicios y notificaciones
@Override
if (extras != null) {
if (estado.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
363
7.4.6 Ejemplo de envo y recepcin de mensajes internos en una aplicacin y uso de
servicios por defecto de Android
@Override
vibrator.vibrate(2000);
364
Content Providers, servicios y notificaciones
Para cargar el servicio "Vibracin" por defecto de Android hemos usado el mtodo
getSystemService(), al que indicamos como parmetro el nombre del servicio al que
queremos acceder.
Para que Android conozca que tiene disponible un receptor de mensajes de difusin y
permita a la aplicacin el acceso al servicio de vibracin, debemos aadir al fichero
AndroidManifest.xml las siguientes lneas:
<receiver android:name="MiBroadcastReceiver"></receiver>
...
<uses-permission android:name="android.permission.VIBRATE"></uses-permission>
A continuacin, solo queda indicar en la actividad principal que se inicie una la cuenta
atrs:
@Override
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if (texto.getText().equals("")){
Toast.LENGTH_LONG).show();
return;
365
int i = Integer.parseInt(texto.getText().toString());
// Cargamos el BroadcastReceiver
alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
+ (i * 1000), pendingIntent);
Toast.LENGTH_LONG).show();
Para cargar el servicio "Alarma" por defecto de Android, hemos usado el mtodo
getSystemService(), al que indicamos como parmetro el nombre del servicio al que
queremos acceder.
Esta intencin pendiente se forma a partir de una intencin normal que invoca
explcitamente la clase que recibe el mensaje y que transformamos en un mensaje de difusin
con el mtodo getBroadcast() de PendingIntent.
366
Content Providers, servicios y notificaciones
En el Ejemplo 4 de esta Unidad vamos a ver cmo definir un servicio privado en modo
Ligado dentro de una aplicacin Android.
Los servicios deben utilizarse para mantener en segundo plano tareas en ejecucin de
la aplicacin, como descargar mensajes de correo de un servidor.
Como ya hemos comentado, para crear un servicio debemos definir una clase que se
extienda de la clase Service de Android:
super.onCreate();
temporizador.scheduleAtFixedRate(new TimerTask() {
@Override
if (listado.size() >= 8) {
listado.remove(0);
listado.add(listadoDatos[indice++]);
indice = 0;
368
Content Providers, servicios y notificaciones
}, 0, INTERVALO);
@Override
super.onDestroy();
if (temporizador != null) {
temporizador.cancel();
@Override
return miBinder;
Servicio getService() {
return Servicio.this;
return listado;
369
Como vamos a usar el servicio en modo Ligado, hemos definido el mtodo onBind() en
el cdigo Java anterior.
<service android:name=".Servicio"></service>
private Servicio s;
s = ((Servicio.MiBinder) binder).getService();
Toast.LENGTH_SHORT).show();
s = null;
};
370
Content Providers, servicios y notificaciones
@Override
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
list.setAdapter(adaptador);
if (s != null) {
matrizAdaptador.clear();
matrizAdaptador.addAll(datos);
adaptador.notifyDataSetChanged();
Para conectar con el servicio definido en la clase Servicio, hemos escrito la sentencia:
371
bindService(new Intent(this, Servicio.class), miConexion,
Context.BIND_AUTO_CREATE);
service: Intent que identifica el servicio al que queremos conectar. Este Intent
puede ser explcito (como en el ejemplo) indicando el nombre de la clase que
implementa el servicio o implcito sealando la accin que se define mediante
un IntentFilter de un servicio publicado en el sistema.
372
Content Providers, servicios y notificaciones
373
Este mensaje no recibe el foco de la aplicacin en ningn momento, es decir, no
interfiere con las acciones que est realizando el usuario en ese momento.
Ya hemos visto durante el curso que su utilizacin es muy sencilla. La clase Toast
dispone del mtodo esttico makeText(Context context, CharSequence text, int duration) al
que debemos pasar como parmetros el contexto de la actividad, el texto del mensaje y el
tiempo que de permanecer en la pantalla en milisegundos.
Tras obtener una referencia al objeto Toast a travs de este mtodo, usamos el
mtodo show() para mostrar el mensaje en la pantalla.
Para comenzar, vamos a incluir un botn que muestre un Toast bsico cuando
hagamos clic sobre l:
xDefectoBtn.setOnClickListener(new OnClickListener() {
@Override
// Creamos el mensaje
Toast toast1 =
// Mostramos el mensaje
toast1.show();
374
Content Providers, servicios y notificaciones
}
});
gravityBtn.setOnClickListener(new OnClickListener() {
@Override
// Indicamos el posicionamiento
toast2.setGravity(Gravity.CENTER|Gravity.RIGHT,0,0);
toast2.show();
375
}
});
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layoutToast"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal"
android:background="#555555"
376
Content Providers, servicios y notificaciones
android:padding="5dip" >
<ImageView android:id="@+id/imagen"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="@drawable/info" />
<TextView android:id="@+id/mensajeLbl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textColor="#FFFFFF"
android:paddingLeft="10dip" />
</LinearLayout>
Para asignar este fichero de diseo (layout) a un Toast, hay que proceder de una
forma algo distinta a como lo hemos hecho en las anteriores notificaciones.
En primer lugar, hay que inflar el layout mediante un objeto LayoutInflater, como ya
hemos usado en varias ocasiones a lo largo del curso, para disear la interfaz de usuario. Una
vez construido el layout, modificamos los valores de los distintos componentes internos de ste
para mostrar la informacin.
layoutBtn.setOnClickListener(new OnClickListener() {
@Override
// Creamos el Toast
377
View layout = inflater.inflate(R.layout.layout_toast,
(ViewGroup) findViewById(R.id.layoutToast));
toast3.setDuration(Toast.LENGTH_SHORT);
toast3.setView(layout);
// Mostramos el Toast
toast3.show();
}
});
Estas notificaciones son las que muestran los dispositivos Android cuando recibimos un
mensaje SMS, hay actualizaciones disponibles, est el reproductor de msica funcionando en
segundo plano, etctera.
Por ejemplo, cuando hay una llamada perdida en nuestro telfono, se muestra en un
lado el siguiente icono en la barra de estado:
Arrastrar
En este ejemplo hemos aadido un nuevo botn que genera una notificacin en la
barra de estado con los elementos comentados y con la posibilidad de dirigirnos a la propia
aplicacin del ejemplo cuando se pulsa sobre la notificacin.
Para generar notificaciones en la barra de estado del sistema, lo primero que hay que
hacer es obtener una referencia al servicio de notificaciones de Android usando la clase
NotificationManager.
379
String ns = Context.NOTIFICATION_SERVICE;
// Creamos la notificacin
Para indicar la actividad que se debe ejecutar si el usuario pulsa sobre la notificacin,
debemos construir una Intencin pendiente PendingIntent, que ya hemos usado en el
apartado anterior de esta Unidad.
380
Content Providers, servicios y notificaciones
notificacion.flags |= Notification.FLAG_AUTO_CANCEL;
//Para aadir sonido, vibracin y luces hay que descomentar estas sentencias
//notif.defaults |= Notification.DEFAULT_SOUND;
//notif.defaults |= Notification.DEFAULT_VIBRATE;
//notif.defaults |= Notification.DEFAULT_LIGHTS;
Para acabar, una vez tenemos definidas las opciones de la notificacin, podemos
generarla invocando el mtodo notify() y pasando como parmetro un identificador nico
definido por la aplicacin, as como el objeto Notification construido anteriormente.
//Enviamos la notificacin
notManager.notify(ID_MEN_BARRA_NOTIF, notificacion);
381
Si desplegamos la bandeja del sistema, podemos verificar el resto de informacin de la
notificacin:
382
Content Providers, servicios y notificaciones
Si has utilizado alguna vez un dispositivo Android, te habrs dado cuenta de que
algunas aplicaciones permiten desplazar pginas deslizando el dedo horizontalmente sobre la
pantalla. Por ejemplo, en la aplicacin del Android Market y en el visor de imgenes podemos
cambiar de pgina dentro de la misma aplicacin:
383
Al desplazar el dedo se
cambia de pantalla.
Este componente no forma parte de las clases por defecto del SDK de Android. Est
incluido en el paquete externo de Compatibilidad de Android que deberas haber aadido al
instalar el SDK de Android en Eclipse. Para comprobar que est bien aadido, haz clic en el
botn "Opens the Android SDK Manager" de Eclipse:
384
Content Providers, servicios y notificaciones
C:\cursos_Mentor\Android\android-sdk-windows\extras\android\support\v4
Una vez que hemos comprobado que tenemos las libreras extra de compatibilidad de
Android, procedemos a incluirlas en el proyecto.
En este proyecto hemos creado la carpeta "libs" y copiado dentro el archivo android-
support-v4.jar del directorio donde se encuentre la librera:
A continuacin, aadimos la librera al Build Path haciendo clic con el botn derecho
del ratn sobre el archivo de la librera y eligiendo la opcin "Build Path->Add to Build Path"
del men desplegable:
385
Para comprobar que hemos incluido la librera correctamente en Eclipse, debe
aparecer como Librera referenciada ("Referenced Libraries"):
La aplicacin que vamos a desarrollar consta de una Actividad que muestra un visor
sencillo de imgenes dentro del ViewPager. Para generar las pginas contenidas en este
ViewPager es necesario usar un objeto PagerAdapter, que se encarga de alimentar de
pginas al componente ViewPager.
// Variable de ViewPager
@Override
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
vPager.setAdapter(vPagerAdapter);
386
Content Providers, servicios y notificaciones
vPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
@Override
@Override
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#a4c639">
<android.support.v4.view.ViewPager
387
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/vPager"/>
</LinearLayout>
Como se trata de una Vista que se define en un paquete extra de Android, es necesario
incluir el nombre completo del mismo android.support.v4.view.ViewPager.
// Constructor de la clase
this.contexto=contexto;
this.nViews=nViews;
@Override
return nViews;
/**
388
Content Providers, servicios y notificaciones
*/
@Override
*/
//Orientacion vertical = 1
linearLayout.setOrientation(1);
tv.setTextColor(Color.WHITE);
tv.setTextSize(30);
imagen.setImageResource(resID);
params.setMargins(0, 0, 0, 20);
params.gravity=Gravity.CENTER;
linearLayout.addView(tv, params);
linearLayout.addView(imagen);
389
// Aadimos la pgina a la coleccin de pginas
((ViewPager) collection).addView(linearLayout,0);
return linearLayout;
} // end instantiateItem
/**
*/
@Override
/**
*/
@Override
return view==((LinearLayout)object);
/**
*/
@Override
390
Content Providers, servicios y notificaciones
/**
* Mtodo que se invoca cuando Android indica que hay que recuperar el
estado de ejecucin
*/
@Override
/**
* Mtodo que se invoca cuando Android indica que hay que guardar el estado
de ejecucin
*/
@Override
return null;
/**
*/
@Override
391
contexto con la orden contexto.getResources(); despus, hemos buscado el ID del recurso de
la imagen usando el mtodo getIdentifier(nombre_recurso, tipo_recurso,
paquete_recurso).
Arrastrar Arrastrar
392
Content Providers, servicios y notificaciones
Las tablas de la base de datos SQLite usadas por un Content Provider deben
incluir siempre el campo _ID que identifica sus registros de forma unvoca.
393
Los servicios propios de una aplicacin se ejecutan en el hilo principal de su
proceso; por lo tanto, para no bloquear el hilo principal o de interfaz, debemos
ejecutar estos servicios con hilos de ejecucin.
Para generar notificaciones en la barra de estado del sistema, hay que obtener una
referencia al servicio de notificaciones de Android usando la clase
NotificationManager.
Este componente ViewPager no forma parte de las clases por defecto del SDK de
Android. Est incluido en el paquete externo de Compatibilidad de Android.
394
ANDROID AVANZADO
NDICE
8.1 INTRODUCCIN ................................................................................... 397
2
Android Avanzado
8.1 INTRODUCCIN
Para depurar (en ingls Debug) una aplicacin Andriod, vamos a emplear las
capacidades disponibles en el entorno de desarrollo Eclipse. Para ello, nos serviremos de la
ltima versin disponible, la 3.7, a fecha de edicin de este documento.
Para que el alumno o alumna pueda practicar la Depuracin de cdigo Android con
Eclipse, hemos creado un proyecto Android con las siguientes clases:
@Override
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
contador.count();
397
Object o = null;
o.toString();
}
}
return resultado;
resultado += i++;
}
}
Si ejecutas la aplicacin tal y como est, vers que aparece el siguiente mensaje de error:
398
Android Avanzado
Si haces clic en el botn "Force close", la aplicacin termina. Veamos cmo depurar
este programa que provoca un error.
Para establecer puntos de interrupcin con Eclipse, hay que hacer clic en la opcin
"Toggle Breakpoint" del men desplegable que aparece si pulsamos el botn derecho del
ratn sobre el nmero de lnea del cdigo fuente correspondiente. Tambin podemos hacer
doble clic en este nmero de lnea para activar o desactivar esta opcin:
399
8.2.2 Iniciar la depuracin (Debug) del cdigo
Para iniciar la depuracin del cdigo hay que hacer clic en la opcin "Run->Debug" del
men principal de Eclipse. Tambin podemos usar la tecla rpida [F11] o usar el icono del
men principal.
Contestaremos que s para cambiar el tipo de Perspectiva a "Debug", muy til para
depurar programas. A continuacin, cambiar la perspectiva de Eclipse as:
Y la ejecucin del programa se parar en la primera lnea del cdigo que tenga un
punto de interrupcin.
400
Android Avanzado
Comando Descripcin
La ejecucin sigue todas las sentencias de todos los mtodos o funciones que
F7 formen nuestro programa. Es decir, ejecuta en secuencia todas las rdenes que
conforman el programa.
Nota: tambin existen unos botones de acceso rpido que permiten ejecutar estas
rdenes. Observa la imagen siguiente:
401
Es posible tambin usar este men para cambiar las columnas que han de aparecer en
esta vista:
Adems, es posible utilizar la opcin "New Detail Formater" (men desplegable con el
botn derecho del ratn) para modificar la informacin mostrada sobre la variable. Por ejemplo,
como el texto (posicin de memoria de una variable)
es.mentor.unidad8.eje1.depuracion.Contador@4051b760 no dice nada, podemos usar la
opcin "New Detail Formater"
402
Android Avanzado
Si pulsas este botn otra vez, los puntos de interrupcin se activarn de nuevo.
403
8.2.5 Propiedades de los puntos de interrupcin
Para acceder a las propiedades del punto de interrupcin, hay que hacer clic en la
opcin "Breakpoint Properties..." del men desplegable con el botn derecho del ratn sobre
el punto de interrupcin:
404
Android Avanzado
405
8.2.9 Finalizar la Depuracin del cdigo
Para finalizar la depuracin del cdigo basta con cambiar la Perspectiva a "Java" de
nuevo. Cuando hagamos alguna modificacin del cdigo fuente, aparecer el siguiente
mensaje para indicar que no se puede sustituir el cdigo de una aplicacin ya instalada en el
emulador de Android y se pregunta si deseamos desconectar ("Disconnect") el modo Debug:
Nota: en esta Unidad 8 puedes encontrar el vdeo Cmo depurar aplicaciones Android en
Eclipse, que muestra visualmente cmo llevar a cabo la depuracin del Ejemplo 1 de esta
Unidad.
406
Android Avanzado
Al instalar el SDK de Android en Eclipse deberas haber aadido ya este paquete. Para
comprobar que est correctamente instalado, haz clic en el botn "Opens the Android SDK
Manager" de Eclipse:
407
Para acabar de crear el dispositivo virtual, hacemos clic en el botn "Create AVD".
Para poder utilizar la API de Google Maps es necesario obtener previamente una
clave de uso (API Key) que estar asociada al certificado con el que firmamos digitalmente las
aplicaciones. En el apartado "Permisos y Seguridad" de la Unidad 5 ya hemos hablado de
estos certificados, necesarios para firmar aplicaciones.
En primer lugar, hay que localizar el fichero donde se almacenan los datos del
certificado de depuracin "debug.keystore". Podemos conocer la ruta de este fichero
accediendo a las preferencias de Eclipse, seccin "Android", apartado "Build":
408
Android Avanzado
Una vez conocemos la ruta del fichero debug.keystore, vamos a acceder a l con la
herramienta keytool.exe de Java para obtener el hash MD5 del certificado. Esto lo hacemos
desde una ventana de lnea de comandos en el directorio C:\Program Files
(x86)\Java\jre6\bin (o donde est instalado Java) mediante la orden:
409
A continuacin, copiamos en el portapapeles el dato que aparece identificado como
Huella digital de certificado (MD5).
Nota: Observa que hemos borrado intencionalmente parte de la clave, pues, cuando
solicites sta, te darn otra diferente.
Ya hemos terminado la preparacin del entorno de programacin para poder utilizar los
servicios de Google Maps dentro de nuestras aplicaciones Android.
Para poder ver este proyecto en tu emulador Android es necesario que obtengas la clave de
uso de la API de Mapas de Google y la cambies en el fichero de diseo main.xml de la interfaz
de usuario. Si no lo haces, arrancar la aplicacin del ejemplo pero no se mostrar el mapa,
como en la imagen siguiente:
410
Android Avanzado
Hay que tener en cuenta que, a la hora de crear el proyecto Android en Eclipse,
tenemos que seleccionar "Google APIs" en el campo "Build Target" en las propiedades del
proyecto:
411
<!-- Aqu se escribe la clave de uso de Google Maps -->
<com.google.android.maps.MapView
android:id="@+id/mapa"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:apiKey="xxxxxxxxxxxxxLIdwwbCEmC3DeN1omnaSkig"
android:clickable="true" />
Adems, tambin hemos establecido el atributo clickable a true, para que el usuario
pueda interactuar con el componente si quiere, por ejemplo, desplazar el mapa con el dedo.
Los componentes MapView slo se pueden utilizar desde una actividad de tipo
MapActivity. La clase MapActivity se extiende de la clase Activity y permite la gestin del
ciclo de vida de la Actividad y de los servicios de visualizacin de un mapas. De igual forma
que ListActivity se usa para mostrar listas, MapActivity se usa para mostrar mapas.
@Override
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mapa = (MapView)findViewById(R.id.mapa);
412
Android Avanzado
sateliteBtn = (Button)findViewById(R.id.SateliteBtn);
irBtn = (Button)findViewById(R.id.IrBtn);
animarBtn = (Button)findViewById(R.id.AnimarBtn);
moverBtn = (Button)findViewById(R.id.MoverBtn);
controlMapa = mapa.getController();
controlMapa.setCenter(loc);
controlMapa.setZoom(6);
mapa.setBuiltInZoomControls(true);
sateliteBtn.setOnClickListener(new OnClickListener() {
@Override
if(mapa.isSatellite())
mapa.setSatellite(false);
else
mapa.setSatellite(true);
});
irBtn.setOnClickListener(new OnClickListener() {
@Override
413
GeoPoint loc = new GeoPoint(latitud.intValue(),
longitud.intValue());
controlMapa.setCenter(loc);
controlMapa.setZoom(10);
});
animarBtn.setOnClickListener(new OnClickListener() {
@Override
controlMapa.animateTo(loc);
controlMapa.zoomIn();
});
moverBtn.setOnClickListener(new OnClickListener() {
@Override
controlMapa.scrollBy(1000, 50);
});
414
Android Avanzado
@Override
return false;
}
}
setSatellite(true)
setStreetView(true)
setTraffic(true)
Tambin existen otros tres mtodos para consultar el estado de cada uno de estos
modos: isSatellite(), isStreetView() y isTraffic().
En el evento onClick del botn sateliteBtn hemos usado el mtodo setSatellite() para
intercambiar el modo satlite y el estndar.
Adems de los mtodos para personalizar el aspecto grfico del mapa, tambin
disponemos de varios mtodos para consultar la informacin geogrfica visualizada en el
mismo. Por ejemplo, podemos saber las coordenadas geogrficas en las que el mapa est
centrado actualmente mediante el mtodo getMapCenter() y el nivel de zoom que est
aplicando a travs del mtodo getZoomLevel().
415
Como podemos observar en el cdigo anterior, las coordenadas del centro del mapa se
obtienen mediante el mtodo getMapCenter() en forma de objeto GeoPoint que encapsula los
valores de latitud y longitud expresados en microgrados (grados * 1E6). Los valores en la
magnitud grados se pueden obtener mediante los mtodos getLatitudeE6() y
getLongitudeE6() respectivamente.
El nivel de zoom del mapa contiene un valor entero entre 1 y 21, siendo 21 el que
ofrece mayor nivel de detalle en el mapa.
Para modificar el centro del mapa, en primer lugar, debemos acceder al controlador del
mapa (MapController) mediante el mtodo getController(). Este mtodo devuelve un objeto
MapController con el que podemos modificar la posicin central del mapa. Para ello, podemos
usar los mtodos setCenter() y setZoom() a los que podemos indicar las coordenadas
centrales del mapa y el nivel de zoom deseado, respectivamente.
En este ejemplo hemos incluido un botn irBtn que centra el mapa sobre un punto
determinado y hemos aplicado un nivel de zoom (10), que permite distinguir en el mapa
algunos detalle. Si pruebas el ejemplo del curso, vers que el desplazamiento a la posicin y el
zoom al nivel indicados se hacen de forma instantnea sin ningn tipo de animacin.
Para mejorar la sensacin de movimiento en el mapa, la API de Google nos ofrece otra
serie de mtodos que permiten desplazar el mapa a una posicin especfica de forma
progresiva y aumentar o disminuir el nivel de zoom de forma animada. El mtodo
animateTo(GeoPoint) desplaza el mapa hasta un punto determinado y los mtodos zoomIn()
y zoomOut() aumentan o disminuyen de forma progresiva, respectivamente, en 1 el nivel de
zoom. En el botn animarBtn hemos usado este mtodo para desplazar de forma animada el
mapa.
Finalmente, ten en cuenta que, para ejecutar la aplicacin del ejemplo sobre el
emulador de Android, hay que modificar el fichero AndroidManifest.xml. Es necesario
especificar que hacemos uso de la API de Google Maps mediante la clusula <uses-library> y,
en segundo lugar, hay que solicitar los permisos de acceso a Internet mediante la clusula
<uses-permission>.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="es.mentor.unidad8.eje2.mapas"
android:versionCode="1"
416
Android Avanzado
android:versionName="1.0" >
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<uses-library android:required="true"
android:name="com.google.android.maps">
</uses-library>
<activity
android:label="@string/app_name"
android:name=".MapasActivity" >
<intent-filter >
</intent-filter>
</activity>
</application>
Desde Eclipse puedes abrir el proyecto Ejemplo 2 (Mapas) de la Unidad 8. Estudia el cdigo
fuente y ejectalo para mostrar en el AVD el resultado del programa anterior, en el que hemos
utilizado un mapa.
417
8.4 DESARROLLO DE APLICACIONES SENSIBLES A LA ORIENTACIN
DEL DISPOSITIVO
Si has usado alguna vez un telfono con Android, vers que, al cambiar la orientacin
del mismo de vertical a horizontal y viceversa, normalmente se modifica el aspecto de la
aplicacin que ests usando distribuyndose las Vistas de la interfaz de usuario de forma
acorde.
Aunque a priori este cambio de orientacin del dispositivo parece sencillo, a veces los
desarrolladores de aplicaciones Android deben desarrollar complejos cdigos para controlarlo.
Este apartado describe cmo implementar esta funcionalidad.
418
Android Avanzado
ATENCIN
Ten en cuenta que el cambio de orientacin puede tardar unos segundos en el emulador
dependiendo de la capacidad del PC con el que trabajes.
En el Ejemplo 3 de esta Unidad vamos a mostrar cmo funcionan las dos formas de
controlar el cambio de orientacin del dispositivo Android.
419
Por lo tanto, hay que probar el Ejemplo 3 de esta Unidad en otra versin de Android.
Tal y como hemos hecho para la versin 2.3.3 en la Instalacin del curso, hay que descargar
las libreras de Android 2.2 y crear el dispositivo virtual correspondiente:
Tambin puedes usar la versin de Android del curso teniendo en cuenta que puedes
cambiar al modo horizontal, pero no volver de nuevo al vertical.
Para definir el modo horizontal (landscape), hay que crear la carpeta res/layout-land.
Esta nueva carpeta contiene tambin el archivo main.xml:
420
Android Avanzado
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/relativeLayout1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.85"
android:gravity="center_horizontal" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginBottom="10dp"
android:layout_marginTop="32dp"
android:textAppearance="?android:attr/textAppearanceMedium" />
421
<Button
android:id="@+id/boton1"
android:layout_width="150px"
android:layout_height="60px"
android:layout_alignParentLeft="true"
android:layout_below="@+id/textView1"
android:layout_marginLeft="100dp"
android:layout_marginTop="93dp"
android:text="Botn" />
<EditText
android:id="@+id/editText"
android:layout_width="197dp"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/boton1"
android:layout_marginLeft="50dp"
android:layout_marginTop="150dp" />
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/relativeLayout1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.85"
android:gravity="center_horizontal" >
<TextView
android:id="@+id/textView1"
422
Android Avanzado
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginBottom="10dp"
android:layout_marginTop="10dp"
android:textAppearance="?android:attr/textAppearanceMedium" />
<Button
android:id="@+id/boton1"
android:layout_width="150px"
android:layout_height="60px"
android:layout_alignParentLeft="true"
android:layout_below="@+id/textView1"
android:layout_marginLeft="100dp"
android:layout_marginTop="30dp"
android:text="Botn" />
<EditText
android:id="@+id/editText"
android:layout_width="197dp"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/boton1"
android:layout_marginLeft="50dp"
android:layout_marginTop="70dp" />
</RelativeLayout>
423
Si no creas este archivo para el modo horizontal y ejecutas la aplicacin, vers que al
cambiar al modo horizontal desaparece el componente TextView:
424
Android Avanzado
Lo primero que hay que tener en cuenta es que es imprescindible establecer el atributo
android:id de todas las Vistas de la actividad. Este atributo es indispensable para que Android
guarde automticamente el contenido de las Vistas cuando cambia la orientacin de la pantalla
y se destruye la Actividad.
* este mtodo.
*/
@Override
String texto="";
if ((orientation==Surface.ROTATION_90) ||
(orientation==Surface.ROTATION_270))
texto="vertical";
else texto="horizontal";
outState.putString("dato", texto);
super.onSaveInstanceState(outState);
* este mtodo.
*/
@Override
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState.containsKey("dato"))
426
Android Avanzado
guardar informacin usando el objeto de tipo Bundle. No permite guardar estructuras de datos
ms complejas, como objetos.
@Override
return(objeto);
Fjate que el mtodo anterior devuelve el tipo objeto (Object), lo que permite
prcticamente devolver cualquier tipo de dato.
Para extraer los datos guardados se puede usar dentro del mtodo onCreate() el
mtodo getLastNonConfigurationInstance(). Por ejemplo, as:
@Override
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
427
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="es.mentor.unidad8.eje3.orientacion"
android:versionCode="1"
android:versionName="1.0" >
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<!-- CUIDADO! Para que el cambio lo haga Android debemos permitirle que
gestione esta funcionalidad. Esto se consigue
quitando el atributo android:configChanges="orientation..." -->
<activity
android:label="@string/app_name"
android:name=".OrientacionActivity"
android:configChanges="orientation|keyboardHidden" >
<intent-filter >
</intent-filter>
</activity>
</application>
</manifest>
428
Android Avanzado
* android:configChanges="orientation..."
* y debemos comentarlos.
*/
@Override
super.onConfigurationChanged(newConfig);
if (newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE) {
setContentView(R.layout.main);
} else {
setContentView(R.layout.main);
et = (EditText)findViewById(R.id.editText);
et.setText(texto);
429
En ocasiones, es necesario asegurarse de que una aplicacin se muestra siempre en
una orientacin concreta. Por ejemplo, muchos juegos slo se visualizan bien en modo
horizontal. En este caso, mediante sentencias Java, se puede cambiar la orientacin de la
pantalla con el mtodo setRequestOrientation() de la clase de Activity:
@Override
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
et = (EditText)findViewById(R.id.editText);
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="es.mentor.unidad8.eje3.orientacion"
android:versionCode="1"
android:versionName="1.0" >
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:label="@string/app_name"
android:name=".OrientacionActivity"
android:screenOrientation="landscape" >
<intent-filter >
430
Android Avanzado
</intent-filter>
</activity>
</application>
</manifest>
Nota: por defecto, el Ejemplo 3 funciona en modo Manual. Si quieres cambiar a modo
automtico, debes modificar el archivo AndroidManifest.xml del proyecto.
Sin embargo, la teora y funciones aqu expuestas s son vlidas para un dispositivo real que
funcionar correctamente segn lo esperado.
Esto es muy til si queremos ver en funcionamiento los modelos de las actividades
obligatorias de este curso, ya que nicamente se entregan compiladas.
Para arrancar manualmente un dispositivo virtual desde Eclipse hay que pulsar el
siguiente botn de la barra de herramientas:
431
A continuacin, arrancar el dispositivo virtual.
Adems, el dispositivo real debe estar configurado para admitir la instalacin de aplicaciones
sin firmar por el Android Market. Si accedes en Ajustes->Aplicaciones debes marcar la
siguiente opcin:
432
Android Avanzado
Adems, los dispositivos que aparezcan con la etiqueta "offline" estn conectados,
pero no estn disponibles al ADB (Android Debug Bridge).
433
Tras obtener este identificador del emulador, vamos a instalar la aplicacin mediante el
comando adb -s identificador-del-emulador install nombre-fichero-apk. Fjate en el
siguiente ejemplo:
434
Android Avanzado
En este apartado vamos a explicar los pasos para publicar una aplicacin en el Android
Market.
La primera vez que accedemos a la pgina se muestra un asistente para dar de alta
una nueva cuenta de desarrollador en el Android Market. Introducimos los datos que se
solicitan (nombre del desarrollador, correo electrnico, URL del sitio Web y nmero de
telfono). Despus, pulsamos el enlace "Seguir":
Para poder darnos de alta como desarrolladores del Android Market y publicar
aplicaciones, hay que abonar 25,00$. Se trata de una cuota nica sin caducidad. Para pagar
436
Android Avanzado
esta cuota podemos usar el servicio Google Checkout o pulsar en "Continuar" para pagar con
tarjeta de crdito:
437
Si todo est correcto, el asistente mostrar la siguiente ventana, indicando que "Su
pedido se ha enviado al Android Market". Para continuar con el proceso, pulsamos en el enlace
"Vuelve al sitio de desarrolladores de Android Market para completar el registro":
438
Android Avanzado
439
Los filtros de permisos permiten a una aplicacin solicitar acceso a recursos de
Android. Ya hemos estudiado que si, por ejemplo, una aplicacin requiere acceder a la cmara
de fotos, debemos indicarlo en el archivo AndroidManifest.xml:
Adems, existen otros filtros con las caractersticas del dispositivo en el archivo
AndroidManifest.xml que hacen que la aplicacin aparezca o no en el Market para un
dispositivo determinado:
Es importante tener en cuenta que cuanto mayores sean los requisitos de hardware (cmara,
bluetooth, GPS, brjula, sensor de movimiento, etctera), la aplicacin ser visible e instalable
en un menor nmero de dispositivos Android.
Siempre hay que tener en cuenta que estamos desarrollando aplicaciones para
dispositivos con pantalla muy pequea, si son telfonos, lo que no ocurre en
los tablets, y teclado limitado, por lo que las aplicaciones deberan mostrar
pocos campos de texto y opciones reducidas.
440
Android Avanzado
Las aplicaciones deben ser rpidas. Si es necesario realizar algn proceso que
pueda tardar unos segundos, es recomendable avisar al usuario o, incluso,
usar hilos de ejecucin, servicios, etctera. El usuario de un dispositivo mvil
espera siempre rapidez de respuesta.
Market does not accept apks signed with the debug certificate. Create a new certificate
that is valid for at least 50 years. Market requires that the certificate used to sign the apk be
valid until at least October 22, 2033. Create a new certificate. Market requires the
minSdkVersion to be set to a positive 32-bit integer in AndroidManifest.xml.
En primer lugar, una vez desarrollada y probada la aplicacin Android con Eclipse,
hacemos clic con el botn derecho del ratn sobre la carpeta del proyecto y seleccionamos la
opcin "Export" del men emergente:
441
Abrimos la carpeta "Android" y seleccionamos "Export Android Application";
despus, pulsamos el botn "Next":
442
Android Avanzado
443
Alias: identificador de la clave.
Password: contrasea de la clave, debemos guardarla o recordarla pues la
necesitaremos cada vez que vayamos a publicar una nueva aplicacin o
actualizar una ya existente en el Android Market.
Confirm: reescribimos la contrasea anterior.
Validity (years): validez del certificado, al menos 25 aos.
First and Last Name: nombre del desarrollador o de la empresa.
Organization Unit: departamento.
Organization: nombre de la empresa.
City or Locality: ciudad.
State or Province: provincia.
Country Code: cdigo postal de la ciudad.
444
Android Avanzado
Si hemos seguido bien los pasos anteriores, ya dispondremos del fichero APK firmado
con el certificado que podemos publicar en el Android Market:
Vamos a explicar cmo publicar una aplicacin firmada con el certificado para que
aparezca en Android Market y los usuarios puedan descargarla e instalarla.
https://market.android.com/publish/Home
445
Aparecer una pagina donde podemos seleccionar el fichero APK pulsando en
"Examinar" para elegir el fichero APK de nuestra aplicacin Android firmada con el certificado:
Si el paquete APK est correcto y cumple con todos los requisitos (versin de Android,
certificado, compilacin, etctera), el asistente muestra el botn "Guardar" y los datos del APK
(nombre de la aplicacin, nombre de la versin, cdigo de la versin, permisos que necesita,
funciones que necesita, tamao, nombre de la clase Java). Pulsamos el botn "Guardar" para
almacenar la aplicacin:
446
Android Avanzado
Tras subirlo, pulsamos en el enlace "Activar" para introducir los datos necesarios para
publicar la aplicacin en el Android Market. Desde esta pgina podemos activar o desactivar la
publicacin de las aplicaciones subidas. Por ejemplo, si hemos detectado algn error y no
queremos que los usuarios se descarguen una aplicacin hasta solucionar el problema,
podremos desactivarla:
447
Icono de la aplicacin: la aplicacin se identifica con un icono que aparece en
la parte izquierda de la pantalla del Android Market cuando los usuarios buscan
aplicaciones.
448
Android Avanzado
Informacin de contacto:
o Sitio web.
o Correo electrnico.
o Telfono.
En la siguiente ventana se muestra parte de los datos que hay que incluir:
Una vez introducidos los datos, pulsamos en el botn "Guardar" de la parte superior
derecha. A continuacin, se comprueba si los datos son completos y correctos y, si no hay
errores, se guardarn los datos asociados al archivo APK.
449
Despus, pulsamos en el botn "Publicar" (a la izquierda del botn "Guardar") para
publicar definitivamente la aplicacin en Android Market:
450
Android Avanzado
Para poder utilizar la API de Google Maps, es necesario disponer de una clave de
uso (API Key) que estar asociada al certificado con el que firmamos digitalmente
las aplicaciones.
El paquete de instalacin APK de una aplicacin del Android Market debe estar
firmado con un certificado vlido de al menos 25 aos.
451