Sie sind auf Seite 1von 174

PROGRAMACIN ANDROID:

HOLA MUNDO
2014/09/04/ B Y ALEJANDRO ALCALDE , 9 COMMENTS ,
IN ANDROID, OPENSOURCE

Como dije, voy a comenzar a escribir tutoriales sobre programacin Android.


Antes de comenzar es necesario tener configurado correctamente eclipse con el Android
SDK, que se puede encontrar en este mismo blog, mediante el primer videotutorialde una
entrada que publiqu hace tiempo, o simplemente buscando en google.
Antes de empezar, quiero comunicar que todas las entradas relacionadas con los tutoriales
de Android los colocar en la pgina Android.
En esta entrada vamos a empezar directamente con el tpico Hola Mundo (Hello World):

CREANDO EL PROYECTO
Arrancamos eclipse con todo configurado correctamente y vamos a Archivo->nuevo>Proyecto Android:

Despues de esto se nos mostrar un dialogo para configurar el proyecto, debemos


introducir:

El nombre del proyecto. en este caso Hola Mundo

Donde queremos crear el proyecto (normalmente dentro del workspace).

Versin Android a la que ir destinada la aplicacin, en este caso Android 2.2

Nombre de la aplicacin (El que se mostrar al usuario una vez instalada, Hola
Mundo).

El Nombre del paquete que se usa como espacio de nombres y estructura de


organizacin del cdigo, app.tutorial.holaMundo

Marcamos la opcin Crear Actividad para que eclipse cree la clase que se lanzar al
ejecutar la aplicacin. Normalmente a esta clase se le llamaMainActivity.

Versin Mnima del SDK es la versin mnima necesaria del SDK para ejecutar la
aplicacin, a menor nmero, la aplicacin correr en ms terminales, pero no podremos
usar las ltimas caracteristicas de Android.

Una vez rellenado todo, le damos a finalizar.

Ya hemos creado nuestro primer proyecto Android, ahora vamos a ver de qu se compone:

COMPONENTES DEL PROYECTO


Los proyectos de Android siguen una estructura fija de carpetas que debemos respetar.
Podemos ver esta estructura con la vista Package Explorer que proporciona eclipse:

CARPETA SRC (DE FUENTES)


Esta carpeta contiene el cdigo fuente organizado en paquetes. Aqu irn las clases java de
nuestra aplicacin.

CARPETA GEN (ARCHIVOS GENERADOS)


Aqu van los archivos que genera el compilador en sus pasadas, como el archivo de
recursos R, esta carpeta normalmente no se debe tocar.

CARPETA ASSETS (DE RECURSOS VARIOS)


Almacena recursos que pueda necesitar nuestra aplicacin, como ficheros de msica etc.
Podremos acceder a ellos fcilmente con la clase del sistema AssetManager

CLASE DE RECURSOS (RES)


Esta carpeta es una de la que ms vamos a usar junto con src, contiene todos los recursos
necesarios para la aplicacin. Todos los archivos de esta carpeta son indexados por el

compilador y se genera el fichero de recursos R, que nos permite acceder a ellos de una
forma rpida.
Est dividida en subcarpetas:

anim: Ficheros XML para la definicin de Animaciones.

color: Ficheros XML de definicin de colores.

drawable: Ficheros bitmap(.png, .9.png, .jpg, .gif) o XML con contenidos que se
dibujarn (fondos, botones etc).

layout: Ficheros XML que definen la capa de interfaz de usuario.

menu: Ficheros XML con la definicin de los mens de la aplicacin.

raw: Binarios que no se pueden colocar en las otras carpetas.

values: Ficheros XML para la definicin de estilos, cadenas de texto para


localizacin etc.

xml: Ficheros XML que pueden ser accedidos en tiempo de ejecucin.

Algunas carpetas pueden tener varias versiones para adaptarse a diferentes tamaos de
pantallas, idiomas etc.

EL ARCHIVO MANIFEST (ANDROIDMANIFEST.XML)


Todos los proyectos tienen un archivo como este, en l se detallan las caractersticas
principales (mdulos, permisos, nombre, icono).
Ahora que hemos explicado la estructura de un proyecto Android, veamos el ejemplo Hola
Mundo al detalle

PROFUNDIZANDO EN EL HOLA
MUNDO
package app.tutorial.holaMundo;

import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}

Al crear el proyecto dimos nombre a una Actividad (MainActivity), estas clases son las
encargadas de mostrar las interfaz grfica al usuario, deben extender de la clase Activity.
Al crear una activity Android llama a su mtodo onCreate() que hace lo necesario para
mostrar la pantalla al usuario. Tal y como est la actividad al crear el proyecto. Hace una
llamada a setContentView(), que tiene como parmetro el identificador de una vista ya
creada.
Por lo tanto, R.layout.main referencia a un archivo xml situado en la carpeta ./res/layout
(ficheros de definicin de pantalla).

ARCHIVO ./RES/LAYOUT/MAIN.XML
< ?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<textview android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
</linearlayout>

En este archivo se define una pantalla en la que los elementos se agruparn de forma lineal
(LinearLayout) y con un componente de texto (TextView). Al componente de texto le
fijamos el texto a mostrar con la referencia @string/hello(valor del item en
./res/values/strings.xml)

ARCHIVO ./RES/VALUES/STRINGS.XML
< ?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World, MainActivity!</string>
<string name="app_name">Hola Mundo</string>
</resources>

Para que la aplicacin funcione es necesario crear el AndroidManifest:


< ?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="app.tutorial.holaMundo"
android:versionCode="1"
android:versionName="1.0">
<uses -sdk android:minSdkVersion="8" />
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".MainActivity"
android:label="@string/app_name">
<intent -filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent>
</activity>
</application>
</manifest>

En este archivo se definen el paquete por defecto, datos de versin, icono (mediante una
referencia). El nombre de la aplicacin (otra referencia al fichero strings.xml). Despues se
define el comportamiento de la aplicacin. Se aaden dos filtros para que la actividad que
definimos anteriormente sea usada como principal (android.intent.action.MAIN) y para
que sea incluida en el men de aplicaciones (android.intent.category.LAUNCHER)

FUNDAMENTOS
PROGRAMACIN ANDROID:
CONCEPTOS BSICOS Y
COMPONENTES
2014/09/04/ B Y ALEJANDRO ALCALDE , 3 COMMENTS ,
IN ANDROID, OPENSOURCE

CONCEPTOS BSICOS
Hemos visto que un proyecto Android est formado por varias carpetas estructuradas, pero
lo que se instala en los dispositivos es un fichero con extensin .apk (application package).
Estos ficheros se generan con la herramienta apk (En el directorio tools del SDK) al
terminar de compilar.
Las aplicaciones en Android tienen su propio entorno seguro de ejecucin:

Cada aplicacin se ejecuta en su propio proceso Linux. El sistema lo crea cuando


ejecutamos la aplicacin y lo destruye cuando no se use pasado un rato o cuando el
Sistema necesite recursos para otra aplicacin

Cada proceso se ejecuta en su propia mquina virtual, de esta manera est aislada
del resto. De esta forma ante cualquier fallo en la aplicacin solo afecta a su mquina
virtual, no al resto.

A cada aplicacin se le asigna un identificador de usuario (uid) distinto. Los


permisos de los archivos que refieren a la aplicacin (cach, datos etc) son solo
accesibles por dicho usuario. Es posible asignar un mismo uid a dos aplicaciones para
que compartan una misma mquina virtual y recursos.

COMPONENTES DE LAS
APLICACIONES
La caracterstica principal de Android es la reutilizacin de componentes de una aplicacin
por otra.
Por ejemplo, imaginemos que estamos desarrollando una aplicacin que almacena datos de
libros junto con una fotografa de su portada. En lugar de tener que escribir el cdigo para
capturar o seleccionar la imagen de la portada, podemos pasar el control a la aplicacin de
la cmara del telfono, o a la galera, as una vez tomemos una foto o seleccionemos una
imagen de la galera se nos devuelve el control a nuestra aplicacin con la imagen
seleccionada.
Para poder realizar estas operaciones, estamos obligados a dividir nuestras aplicaciones en
mdulos independientes que solo realicen una tarea concreta.
Veamos ahora otro ejemplo, muchos terminales tienen la opcin de compartir algo en las
redes sociales, por ejemplo Twitter, un mdulo claramente definido de esta aplicacin es
por ejemplo la opcin de enviar un mensaje o tweet, si seguimos la filosofa de dividir
nuestras aplicaciones en mdulos, la funcin de enviar un mensaje sera una actividad
independiente que recibe como parmetro el mensaje a enviar, si no recibe parmetro se
mostrar el formulario para escribirlo. Dicha actividad usar la API de Twitter para enviar
el mensaje y finalmente cerrar la actividad devolviendo el control a la aplicacin que la
llam. De esta forma, y con los filtros adecuados en el AndroidManifest.xml, cada
aplicacin que quiera compartir algo en twitter llamar a esta actividad pasandole como
parmetro el mensaje.
Con esto llegamos a la conclusin de que las aplicaciones Android no tienen un punto de
entrada y otro de salida, podemos definir todos los que necesitemos.
Para realizar todas estas operaciones, Android proporciona cuatro tipos decomponentes
bsicos:

ACTIVIDADES (ACTIVITY)
Son las encargadas de mostrar la interfaz de usuario e interactuar con l. Responden a los
eventos generados por el usuario (pulsar botones etc). Heredan de la clase Activity
El aspecto de la actividad se aplica pasando un objeto View(Encargado de dibujar una parte
rectangular en la pantalla, pueden contener ms objetos View, adems todos los
componentes de la interfaz (botones, imagenes etc) heredan de View) al
mtodo Activity.setContentView(), que es el mtodo encargado de dibujar la pantalla.
Normalmente las vistas ocupan toda la pantalla, pero se pueden configurar para que se
muestren como flotantes. Las actividades tambin pueden llamar a componentes que se
mostrarn sobre su View (como dialogos o mens).
Por cada pantalla distinta hay una actividad distinta, normalmente las aplicaciones tienen
una actividad fijada como punto de entrada. Por ejemplo:
Una aplicacin que lee el correo tendr las siguientes actividades:

RecibidosActivity: muestra el listado de mensajes recibidos.

LeerMensajeActivity: Muestra el contenido de un mensaje.

CrearMensajeActivity: recibe como parmetro los datos necesarios, si no hay,


muestra el formulario para rellenarlos y envia el mensaje.

Para esta aplicacin definimos como punto de


entrada recibidosActivity yCrearMensajeActivity, para que otras aplicaciones puedan
reutilizarlas.

SERVICIOS
No tienen interfaz visual y se ejecutan en segundo plano, se encargan de realizar tareas que
deben continuar ejecutandose cuando nuestra aplicacin no est en primer plano. Todos los
servicios extienden de la clase Service
Continuando con el ejemplo anterior, la aplicacin de correo tendr un servicio que
comprobar y descargar nuevos correos. Es posible lanzar o conectar con un servicio en
ejecucin con la interfaz que proporciona la clase Service.
Los servicios disponen de un mecanismo para ejecutar tareas pesadas sin bloquear la
aplicacin ya que se ejecutan en un hilo distinto.

RECEPTORES DE MENSAJES DE DISTRIBUCIN


(BROADCASTRECEIVER)
Simplemente reciben un mensaje y reaccionan ante l, extienden de la
claseBroadcastReceiver, no tienen interfaz de usuario, pero pueden lanzar Actividades
como respuesta a un evento o usar NotificationManager para informar al usuario.
Android habitualmente lanza muchas notificaciones de sistema (llamadas entrantes, nuevos
correos, nuevos sms etc). Si ponemos como ejemplo la aplicacin del correo mencionada
anteriormente, esta tendra unBroadcastReceiver escuchando el mensaje nuevo_correo,
que lanzara el servicio cada vez que detectara uno. Cuando esto sucediera, se mandara un
aviso a la barra del sistema para alertar al usuario.

PROVEEDORES DE CONTENIDO
(CONTENTPROVIDER)
Ponen un grupo de datos a disposicin de distintas aplicaciones, extienden de la
clase ContentProvider para implementar los mtodos de la interfaz, pero para acceder a
esta interfaz se ha de usar una clase llamada ContentResolver
Con esta clase se permite acceder al sistema de ficheros, bases de datos SQLite o cualquier
otra fuente de datos unificada.
Un lector de correo podra disponer de un ContentProvider para acceder a la bandeja de
entrada y los datos del mensaje.

FUNDAMENTOS
PROGRAMACIN ANDROID:
INTENTS Y
ANDROIDMANIFEST
2014/09/04/ B Y ALEJANDRO ALCALDE , 0 COMMENTS ,
IN ANDROID, OPENSOURCE

INTENTS
Las Actividades, Servicios y BroadcastReceiver se activan a travs de mensajes asncronos
llamados intent
Un intent es un objeto que contiene todos los datos del mensaje.
Hay tres mtodos para activar cada uno de los componentes:

Las actividades se muestran pasando un Intent al


mtodoContext.startActivity() o Activity.startActivityForResult(). Una vez lanzada la
actividad, dentro de la misma podemos abrir el objeto Intent para obtener los
parmetros usando el mtodo getIntent()

Para lanzar servicios o interactuar con ellos pasaremos el intent al


mtodoContext.startService(). Para analizar el Intent dentro del proceso
usaremosonBind().

Para pasar los intents a un mensaje de difusin debemos pasar el Intent al


mtodo Context.sendBroadcast(), Context.sendOrderedBroadcast() oContext.sendSti
ckyBroadcast(), as el intent se entregar a todas las clasesBroadcastReceiver que estn
escuchando, y podrn analizarlo con onReceive().

ANDROIDMANIFEST
Para poder usar un componente, adems de crearlo extendiendo de su clase
correspondiente, es necesario definirlo en el AndroidManifest.xml. Este archivo podemos
editarlo directamente como XML, o con el formulario que nos facilita eclipse.
Cada componente tiene su propia etiqueta xml:

: Para actividades.

: Para servicios.

: Para receptores de mensajes de difusin.

: Para proveedores de contenidos.

: Para categorizar componentes, as cuando se les llame no hay que saber el nombre
del intent, android lo elige basandose en su categora y parmetros.

FUNDAMENTOS
PROGRAMACIN ANDROID:
ACTIVIDADES, TAREAS,
PROCESOS E HILOS
2014/09/04/ B Y ALEJANDRO ALCALDE , 0 COMMENTS ,
IN ANDROID, OPENSOURCE

ACTIVIDADES Y TAREAS
Las actividades conforme se van ejecutando van apilandose en una pila. Cuando
finalizamos una actividad, con el mtodo finish() o con la tecla atrs del telfono, la
actividad se extrae de la pila, quedando encima de la pila la actividad que se abri
anteriormente.
Si ejecutamos una actividad varias veces sin cerrarla, sta aparecer en la pila tantas veces
como la hayamos ejecutado. La pila de actividades se enva al segundo plano cuando la
aplicacin pierde el foco, y vuelve al primer plano cuando la aplicacin vuelve a tomar el
control.

Podemos modificar este comportamiento con flags que pasamos al objeto Intent a partir de
las propiedades de la activity descritas en el AndroidManifest
Si una pila de tareas se abandona por el usuario durante un periodo de tiempo y el sistema
necesita ms recursos, se limpia la pila de actividades (excepto la actividad principal), este
comportamiento se puede modificar en el manifiesto:

Atributo

Funcin

alwaysRetainTaskStat
e

Si vale true, se mantiene la pila aunque se abandone durante mucho


tiempo

clearTaskOnLaunch

Si es true, se limpia la pila (excepto la actividad principal) cada vez


que se lleve al segundo plano

finishOnTaskLaunch

Similar a la anterior, pero solo se aplica a la actividad con este


atributo fijado a true.

PROCESOS E HILOS
Como cada aplicacin se ejecuta en un proceso Linux distinto, todos los componentes y
procesos de dicha aplicacin corren en el mismo hilo. Esto se puede modificar con el
atributo process de cada componente (activity, provider, receiver y service). En la
etiqueta application del manifest podemos poner este atributo para que sea aplicado a todos
sus elementos.
Para gestionar tareas pesadas podemos usar hilos para ejecutar dichas tareas en un hilo
aparte (ejecutarlas en segundo plano). Para llevar a cabo esta operacin usaremos el
objeto Thread de java, aunque Android proporciona otros objetos para facilitar el trabajo,
como Handler, AsyncTask o Looper. (entre otros).

FUNDAMENTOS
PROGRAMACIN ANDROID:
CICLO DE VIDA DE LOS
COMPONENTES
2014/ 09/ 04/ B Y ALEJANDRO ALCALDE , 1 COMMENT ,
IN ANDROID, OPENSOURCE

Cada componente tiene un ciclo de vida distinto. Las superclases de estos componentes
tienen mtodos callbackque les permiten reaccionar ante un cambio de estado. Cada
mtodo callback est obligado a llamar al mismo mtodo de su padre.

CICLO DE VIDA DE LOS RECEIVER


Tienen un ciclo de vida muy corto, ya que se activan al producirse un mensaje de difusin,
que capturan con el mtodo callback:

void onReceive(Context curContext, Intent broadcastMsg)

Si este mtodo se est ejecutando, se considera el Receiver activo. Esto supone un


problema si la tarea a ejecutar es pesada y la lanzamos a un hilo aparte. Cuando termine el

mtodo, Android considera que el receiver est inactivo, pero el hilo sigue ejecutndose y
puede ser eliminado de la pila, interrumpiendo la tarea.
Se soluciona lanzando un servicio desde este mtodo que se encarga de hacer las tareas
pesadas, as, al tener el servicio su propio ciclo de vida, seguir ejecutandose, aunque el
receiver se considere inactivo.

CICLO DE VIDA DE LOS CONTENTPROVIDER


Su ciclo es muy corto, permanecen activos mientras sean referenciados por un
ContentResolver.

CICLO DE VIDA DE LAS ACTIVIDADES


Tiene tres estados:

Activo: La actividad se encuentra en primer plano (Encima de la pila de tareas) e


interactuando con el usuario.

Pausado: La actividad sigue siendo visible para el usuario, pero ha perdido el foco.
Por ejemplo que se haya mostrado un cuadro de dialogo delante de nuestra actividad.
Debemos guardar el estado de la interfaz y los datos de esta actividad antes de entrar en
este estado, ya que podramos perderlos si el sistema necesita ms recursos de memoria.

Parado: La actividad no es visible para el usuario, queda a disposicin del sistema


para borrarla de la pila en caso de necesitar memoria.

La clase Activity dispone de mtodos que se llaman cada vez que sta cambia de estado,
para permitirnos realiar tareas como guardar los datos antes de cambiar de estado, y cargar
la actividad ms rpido la proxima vez que se muestre. A continuacin un diagrama con los
distintos estados por los que pasa una actividad:

onCreate(Bundle savedInstanceState): Este mtodo se llama al crear la actividad.


Siempre se sobreescribe para configurar la vista, crear adaptadores, rellenar los objetos
con sus valores etc. Puede recibir como parmetro el estado anterior de la actividad
para que podamos restaurarla.

onPause(): Es llamado justo antes de que se traiga a primer plano otra actividad.
Aqu es donde debemos guardar los datos para no perder la informacin de la actividad
si esta es sacada de la pila. Dentro de este mtodo tambin se suele parar las tareas
pesadas que consuman CPU.

onStop(): Es llamado cuando la actividad se va a ocultar durante un largo periodo


de tiempo. Si el sistema necesita recursos, puede que este mtodo no sea llamado, por
lo que es recomendable guardar los datos en el mtodoonPause().

onDestroy: ltimo en llamarse antes de destruir la actividad. Puede llamarse a


travs del mtodo finish() o llamarlo el sistema para conseguir ms memoria. Para
saber quin lo llam, podemos usar isFinishing().

Un ejemplo de uso de estos mtodos puede ser cuando tenemos una aplicacin que carga
datos de internet, deberamos cargar los datos al inicio de la actividad, una vez descargados,
guardaramos el estado de la actividad para que si es destruida, no sea necesario volver a
descargar los datos.
Para realizar esta operacin usaramos el mtodo onSavedInstanceState(), que creara
un Bundle con los datos necesarios que pasaramos al mtodo onCreate()la segunda vez
que cargaramos la actividad, sin necesidad de volver a descargar los datos.

CICLO DE VIDA DE SERVICE


Los servicios se pueden usar de dos formas, dependiendo de como lo lancemos, su ciclo
ser uno u otro.

Si lo lanzamos con startService() se ejecurar hasta que termine. Los servicios se


configurar en el mtodo onCreate() y se liberan en el onDestroy(). Podemos terminar
un servicio externamente con Context.stopService() o dentro del mismo servicio
con Service.stopSelf() o Service.stopSelfResult().

Si lo lanzamos con Context.bindService() podremos interactuar con l mediante una


interfaz que el servicio debe exportar. Terminaremos el servicio
conContext.unbindService().

A continuacin el diagrama con el ciclo de vida de los servicios:

FUNDAMENTOS
PROGRAMACIN ANDROID:
LIMPIEZA DE PROCESOS
2014/09/04/ B Y ALEJANDRO ALCALDE , 0 COMMENTS ,
IN ANDROID, OPENSOURCE

Android va destruyendo componentes inactivos para liberar memria, pero los elminia
teniendo en cuenta cual es el de menor importancia:

Los primeros en ser elmininados son los procesos vacos (Son aplicaciones cerradas
que se mantienen en memoria para cargar rpidamente la aplicacin la proxima vez que
se abra.)

Procesos en segundo plano, estos son las aplicaciones que ya han ejecutado su
mtodo onStop(), Android confecciona una lista con los procesos en este estado y
elimina en primer lugar el ms antiguo.

Despues elminina los procesos de servicio. (si sigue necesitando ms memoria.)

Si an necesita ms memoria, elimina los procesos pausados.

Si con esto sigue necesitando, finalmente elimina el proceso en primer plano.

Es muy importante implementar bien los mtodos de estado, para evitar perden
informacin.

PROGRAMACIN ANDROID:
TRABAJAR CON
ACTIVIDADES Y PASAR
PARMETROS ENTRE ELLAS
2014/ 09/ 05/ B Y ALEJANDRO ALCALDE , 41 COMMENTS ,
IN ANDROID, OPENSOURCE

En el primer captulo, vimos como crear nuestro primer proyecto en Android, el


conocido Hola Mundo, en esta entrada, vamos a ver como crear varias actividades y cmo
hacer que se pasen parmetros las unas a las otras.
El proyecto con este ejemplo est disponible para su descarga (Comentado paso a paso):

Download Capitulo2 Intents Y Bundlescapitulo2_intents_y_bundles.zip


Downloaded 650 times

Voy a explicar un poco por encima que hace cada fichero del proyecto:

./RES/LAYOUT/MAIN.XML

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


<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<textview android:id="@+id/textView1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
<button android:id="@+id/button1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/cadena1" />
<button android:id="@+id/button2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/cadena2" />
</linearlayout>

En este layout principal vamos a aadir dos botones que nos servirn para lanzar las nuevas
actividades que creemos despus.

./RES/LAYOUT/SEGUNDA_ACTIVIDAD.XML
< ?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<textview android:id="@+id/textView1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/cadena1" />
<textview android:id="@+id/params"
android:layout_width="fill_parent"

android:layout_height="wrap_content"
android:text="@string/hello" />
<button android:id="@+id/boton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/cadena1" />
</linearlayout>

Este layout vamos a usarlo para mostrar los parmetros que pasemos de una actividad a otra

./SRC/MAINACTIVITY.JAVA
package com.elbauldelprogramador.actividades;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class mainActivity extends Activity {
protected static final int REQUEST_CODE = 10;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Capturamos los objetos grficos que vamos a usar
Button button1 = (Button) findViewById(R.id.button1);
Button button2 = (Button) findViewById(R.id.button2);
// Fijamos un evento onclick para el button1, cada vez que
// lo pulsemos se llamar a este mtodo (que abrir una actividad)
button1.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent intent = new Intent(mainActivity.this, Activity1.class);

startActivity(intent);
}
});
//button2 pasar parmetros a otra actividad, y los devolver
button2.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent intent = new Intent(mainActivity.this, ParametrosActivity.class);
// damos valor al parmetro a pasar
intent.putExtra("param1", "valor del parmetro 1 (viene de mainActivity)");
/*
* Inicia una actividad que devolver un resultado cuando
* haya terminado. Cuando la actividad termina, se llama al mtodo
* onActivityResult() con el requestCode dado.
* El uso de un requestCode negativo es lo mismo que llamar a
* startActivity(intent) (la actividad no se iniciar como una
* sub-actividad).
*/
startActivityForResult(intent, REQUEST_CODE);
}
});
}
/*
* ste mtodo se llama cuando la actividad que iniciamos con un startActivityForResult
* finaliza, dandi el REQUEST_CODE con el que llam, el resultCode se devuelve, junto con
* algunos datos adicionales, el resultCode ser RESULT_CANCELED si la actividad devuelve
* eso explcitamente, si no devuelve ningn resultado o si la operacin finaliz de forma
inesperada.
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE) {
// cogemos el valor devuelto por la otra actividad
String result = data.getStringExtra("result");
// enseamos al usuario el resultado
Toast.makeText(this, "ParametrosActivity devolvi: " + result, Toast.LENGTH_LONG).show();
}
}
}

En esta clase vamos a modificar el comportamiento de los botones, aadiendoles listeners


para cuando el usuario haga click en ellos.
Por ltimo, vamos a crear otras dos actividades, la primera (./src/activity1.java), no va a
hacer nada, solo mostrarse. La segunda (./src/ParametrosActivity.java), va a recibir un
parmetro y devolver otro.

./SRC/ACTIVITY1.JAVA
package com.elbauldelprogramador.actividades;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class Activity1 extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.segunda_actividad);
// Capturamos los objetos grficos que vamos a usar
TextView text = (TextView) findViewById(R.id.textView1);
Button button = (Button) findViewById(R.id.boton);
// Agregamos al textView un texto
text.setText(R.string.cadena1);
// Cambiamos el texto al botn
button.setText(R.string.salir);
// Evento onclick del botn, cuando se pulse, cerramos la actividad
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
finish();
}
});

}
}

./SRC/PARAMETROSACTIVITY.JAVA
package com.elbauldelprogramador.actividades;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class ParametrosActivity extends Activity {
private static final int OK_RESULT_CODE = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.segunda_actividad);
// Capturamos los objetos grficos que vamos a usar
TextView text = (TextView) findViewById(R.id.textView1);
Button button = (Button) findViewById(R.id.boton);
TextView params = (TextView) findViewById(R.id.params);
text.setText(R.string.cadena2);
button.setText(R.string.salir);
//Al pulsar el botn cerramos la ventana y volveremos a la anterior
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
//Cierra la actividad y la saca de la pila
returnParams();
}
});
// Mostramos los parmetros recibidos de la actividad mainActivity
Bundle reicieveParams = getIntent().getExtras();
params.setText(reicieveParams.getString("param1"));

}
protected void returnParams() {
Intent intent = new Intent();
intent.putExtra("result", "'Valor devuelto por ParametrosActivity'");
setResult(OK_RESULT_CODE, intent);
finish();
}
}

PROGRAMACIN ANDROID:
INTERFAZ GRFICA
CONCEPTOS BSICOS
2014/09/04/ B Y ALEJANDRO ALCALDE , 0 COMMENTS ,
IN ANDROID, OPENSOURCE

Todos los componenetes de la interfaz de usuario de Android descienden de la


clase View. Dichos objetos estn organizados en forma de rbol y pueden contener nuevos
objetos View, permitiendo crear interfaces muy completas.

Los objetos View se pueden definir de dos maneras:


Mediante un fichero XML colocado dentro del directorio res/layout, que es el que
usaremos normalmente.
En tiempo de ejecucin, muy til para crear nuestros propios componentes View
Para dibujar la interfaz, el sistema necesita que le pasemos el objeto View raiz, para ir
descendiendo por cada uno de sus nodos y presentar al usuario toda la interfaz. El mtodo
encargado de esto es Activity.setContentView().
Android se encarga de dibujar los elementos llamando primero al mtodo draw()de cada
vista, podramos decir que cada vista se dibuja a s misma. El proceso de dibujo se hace en
dos veces. Inicialmente se llama al mtodo measure(int, int), que define el tamao de cada
objeto View, posteriormente se llama al mtodolayout(int, int, int, int), que posiciona el
objeto dentro de la vista.
Para que Android sepa dibujar correctamente los objetos, tenemos que pasarle algunos
datos, como son la altura y anchura. Para eso nos servimos de la claseLayoutParams, que
pude tomar los siguientes valores:

Un nmero
La constante FILL_PARENT, que indica que la vista debe intentar ser tan grande
como su padre, quitando el padding.

La constante WRAP_CONTENT, para que intente ser lo suficientemente grande


para mostrar su contenido, mas el padding.

Tambin nos podemos servir de la clase View.MeasureSpec, para especificar el tamao y


cmo deben ser posicionadas.

AT_MOST, el padre fija un tamao mnimo para el hijo. El hijo(y los descendientes
de ste) tienen que ocupar por lo menos ese tamao.

EXACTLY, el padre impone un tamao exacto al hijo.

UNSPECIFIED, el padre fija el tamao deseado del hijo.

Un atributo imprescindible es el id(de tipo entero). Que sirve para identificar nicamente a
un objeto View. Cuando lo declaramos mediante xml podemos referenciarlo a travs de la
clase de recursos R, usando una @.

android:id=@+id/nombreID: Crea un nuevo atributo en la clase R llamado


nombreID

android:id=@id/nombreID:< Hace referencia a un id ya existente asociado a la


etiqueta 'nombreID'/li>

android:id=@android:id/list: Referencia a un a etiqueta definida en la clase R


del sistema llamada list.

Los objetos View pueden tener otros muchos atributos, como padding, colores, imgenes,
fondos, mrgentes etc

CONTEXT
Si ya has programado algo en Android, o has visto alguno de los ejemplos, probablemente
hayas visto que muchos mtodos referidos a la vista piden como parmetro un objeto de
tipo context.
Context es una interfaz para la informacin global de la aplicacin. A travs de l podemos
acceder a recursos, clases y operaciones, como lanzar actividades, manejar intents etc.
Podemos acceder al contexto de diferentes formas en funcin de donde nos encontremos:

Con el mtodo getContext().

Las actividades implementan esta interfaz, por lo que haciendo referencia a ellas
mismas, con (this) o NombreActivity.this, estaremos referenciando el contexto.
Usando otros mtodos como getApplicationContext() o getApplication()

PROGRAMACIN ANDROID:
INTERFAZ GRFICA
LAYOUTS
2014/09/04/ B Y ALEJANDRO ALCALDE , 3 COMMENTS ,
IN ANDROID, OPENSOURCE

Los layout nos permiten posicionar cada objeto grfico en el lugar que queramos de la
pantalla, es decir, nos permite disear el aspecto grfico que va a tener nuestra pantalla. Los
layouts son de tipo ViewGroup, una subclase de View
Existen varios tipos de Layouts para Android, vamos a ver los ms comunes:

FRAMELAYOUT
Este tipo de Layout es el ms bsico, coloca a sus objetos hijos en la parte superior
izquierda de la pantalla.
< ?xml version="1.0" encoding="utf-8"?>
<framelayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<textview android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"/>
<textview android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/app_name"/>
</framelayout>

Como se puede apreciar en en resultado, si hay ms de un hijo, los objetos se amontonan


unos encima de otros.

LINEARLAYOUT
Este tipo de layout coloca sus hijos unos detras de otros, tambin comenzando por la
esquina superior izquierda de la pantalla. Podemos colocarlos alineados horizontalmente o
verticalmente mediante su propiedadandroid:orientation=horizontal | vertical
< ?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"

android:layout_width="fill_parent"
android:layout_height="fill_parent">
<textview android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:background="#0ff"/>
<textview android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello"
android:background="#ff0"/>
</linearlayout>

En este caso, he pueso un fondo de color a cada texto (con la


propiedadandroid:background) para diferenciarlo bien, y he usado la horientacin
horizontal, de haber usado la orientacin vertical, los textos apareceran uno debajo del
otro:

RELATIVELAYOUT

Este Layout permite que coloquemos los elementos en un lugar con respecto a la posicin
de otro, es decir, colocar un botn a la derecha de un texto, o centrarlo en la pantalla, o por
ejemplo, colocar un texto encima de tal elemento y a la derecha de este otro.
Para conseguir esto, RelativeLayout proporciona propiedades
comoandroid:layout_toRightOf o android:layout_alignLeft, que toman como valores los
identificadores de los objetos, o valores booleanos.
< ?xml version="1.0" encoding="utf-8"?>
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<textview android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:background="#0ff"
android:layout_centerInParent="true"
android:id="@+id/text1"/>
<textview android:id="@+id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello"
android:background="#ff0"
android:layout_below="@id/text1"/>
</relativelayout>

Como vemos, hemos centrado el texto1 en la pantalla


conandroid:layout_centerInParent=true y hemos puesto debajo del texto1 al texto2
con android:layout_below=@id/text1

Para saber ms acerca de todos los tipos de layouts que hay podis
visitarhttp://developer.android.com/guide/topics/ui/layout-objects.html

PROGRAMACIN ANDROID:
INTERFAZ GRFICA
COMPONENTES GRFICOS Y
EVENTOS
2014/09/04/ B Y ALEJANDRO ALCALDE , 7 COMMENTS ,
IN ANDROID, OPENSOURCE

Ya hemos visto que todos los componentes visuales descienden del objeto View, que
proporciona una interfaz para que podemos interactuar con ellos.
Para que nuestras aplicaciones sean funcionales, necesitamos responder a los eventos que el
usuario dispare al interactuar con nuestro programa, en Android, esto se consigue mediante
los Listeners, que sern llamados cada vez que se produzca un evento.

Por ejemplo, un listener muy comn ser setOnClickListener(), que responder cada vez
que se pulse sobre la vista a la que se lo aplicamos, como un botn, o una imgen. Hay
muchos tipos de listener, setOnKeyListener() (Para eventos de
teclado), setOnItemClicklistener() (Para eventos al seleccionar un elemento de una lista)
etc etc.

En los ejemplos mostrados hasta ahora, solo hemos visto objetos de tipo TexView, vamos a
ver unos cuantos ms (En cada ejemplo pondr la definicin XML del objeto, y su
manipulacin mediante cdigo):

BUTTON
Botones simples, para realizar acciones al pulsar sobre ellos.
<button android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Plsame"
android:layout_centerInParent="true"
android:id="@+id/button1"/>
//Recoger el botn en una variable para usarlo
final Button button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Toast.makeText(
button1.getContext()
, "Me has pulsado " + ++contador + " veces."
, Toast.LENGTH_SHORT)
.show();
}
});

En este caso, hemos declarado una variable como miembro de la clase, (public int
contador = 0;), para que cada vez que pulsemos el botn nos salga un mensaje con el
nmero de veces que lo hemos pulsado:

EDITTEXT
Son campos de texto en los que el usuario puede escribir.
<edittext android:layout_width="200dip"
android:layout_height="wrap_content"
android:layout_above="@id/button1"
android:id="@+id/editText1"
android:layout_centerInParent="true"/>
final EditText editText1 = (EditText) findViewById(R.id.editText1);
editText1.setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View arg0, int arg1, KeyEvent arg2) {
if (arg1 == KeyEvent.KEYCODE_ENTER){
Toast.makeText(
editText1.getContext()
, "Escribiste: " + editText1.getText()
, Toast.LENGTH_SHORT)
.show();

return true;
}
return false;
}
});

Lo que hemos hecho con este EditText, es fijarle un onKeyListener, que comprobar (con el
if), que hemos pulsado la tecla enter, y si es cierto, mostrar el texto escrito:

IMAGEVIEW
Nos permite mostrar imgenes en la pantalla.
<imageview android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/icon"/>
final ImageView imageView1 = (ImageView) findViewById(R.id.imageView);
imageView1.setImageResource(R.drawable.icon);

El icono es el que viene por defecto al crear un proyecto. Este es el resultado:

CHECKBOX
Es un tipo de botn con dos estados, activo o inactivo, practicamente tiene el mismo
comportamiento de un botn, una de sus caractersticas es que podemos comprobar si el
botn esta activo o no:
<checkbox android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="CheckBox"
android:layout_centerInParent="true"
android:layout_below="@id/button1"
android:id="@+id/checkBox1" />
final CheckBox checkbox1 = (CheckBox) findViewById(R.id.checkBox1);
checkbox1.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton arg0, boolean checked) {
if (checked) Toast.makeText(checkbox1.getContext(), "Activo", Toast.LENGTH_LONG).show();
else Toast.makeText(checkbox1.getContext(), "Inactivo", Toast.LENGTH_SHORT).show();
}
});

En este caso, hemos usado como listener onCheckedChanged, que se ejecutar cada vez
que el estado del checkbox cambie.

Estos son los componentes grficos bsicos, tambin disponemos de RadioButton,


ToggleButton (Parecidos a los checkBox, pero con una luz que se ilumina al estar activos, y
con la caracterstica de que el texto cambia dependiendo de su estado, aunque esto se puede
conseguir con el checkbox facilmente.)
En general con echar un vistazo a los mtodos y listeners de cada componente, y con la
documentacin que ofrece javadoc en eclipse, lograremos entender como funciona cada
uno, y podremos usarlos fcilmente.

Download EjemploComponentesGraficosejemploComponentesGraficos.zip
Downloaded 1124 times

PROGRAMACIN ANDROID:
INTERFAZ GRFICA
ADAPTERS I
2 0 1 4 / 0 9 / 0 4 / B Y A L E J A N D R O AL C A L D E , 11 C O M M E N T S ,
IN ANDROID, OPENSOURCE

Un objeto Adaptador acta como puente entre un AdapterView y los datos de una Vista
(View). El adaptador permite el acceso a los elementos de datos, ste tambin es
responsable de crear una vista para cada elemento en la coleccin de datos.
Se puede decir, que los adaptadores son colecciones de datos, que asignamos a una vista
para que sta los muestre, por ejemplo, podemos crear un ArrayAdapter a partir de un array
de string ya creado y con datos, y asignar este adaptador a un ListView, as, el ListView
mostrar los datos del array.

Mediante el uso de Adapters definimos una forma comn de acceder a colecciones de


datos.
Para que quede ms claro este concepto, vamos a verlo mediante un ejemplo:
Primero creamos el layout, que va a contener un ListView con un Id ya definido por
android, y un TextView tambin con un id ya definido.

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


<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<listview android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@android:id/list" />
<textview android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@android:id/empty" />
</linearlayout>

Ahora, el cdigo donde creamos el adaptador, y lo asociamos al ListView:


package app.elbauldelprogramador.adapters;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
public class AdaptadoresActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//Array que asociaremos al adaptador
String[] array = new String[] {
"Elemento 1"
,"Elemento 2"
,"Elemento 3"
,"Elemento 4"
,"Elemento 5"
,"Elemento 6"
};

//Creacin del adaptador, vamos a escoger el layout


//simple_list_item_1, que los mostr
ListAdapter adaptador = new ArrayAdapter<string>(
this, android.R.layout.simple_list_item_1, array);
//Asociamos el adaptador a la vista.
ListView lv = (ListView) findViewById(android.R.id.list);
lv.setAdapter(adaptador);
}
}
</string>

Como vemos, al crear el arrayAdapter, tenemos que pasar tres parmetros, el contexto, un
layout que se usar para dibujar cada item (en este casosimple_list_item_1, que ya viene
definido por android), ms adelante veremos como crear los nuestros propios, y como
tercer parmetro la coleccin de datos.
Aprovechando que en la anterior entrada hablamos de los eventos, voy a explicar como
fijar un evento onclick para cada elemento de la lista.
//Evento que se disparar al pulsar en un elemento de la lista
lv.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView< ?> arg0, View arg1, int arg2,
long arg3) {
ListAdapter la = (ListAdapter) arg0.getAdapter();
Toast.makeText(
arg1.getContext()
,la.getItem(arg2).toString()
,Toast.LENGTH_LONG)
.show();
};
});

Para realizar este tipo de cosas, android proporciona una clase llamada ListActivity, este
tipo de clase necesita que exista una vista con el id ya definido por
Android@android:id/list y otra con el id @android:id/empty (Tal y como lo definimos en
nuestro layout), as, si el adaptador que le asiganamos a la lista no tiene datos, se mostrar
al usuario la vista empty, el cdigo quedara de la siguiente manera:

package app.elbauldelprogramador.adapters;
import android.app.ListActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Toast;
public class AdaptadoresActivity extends ListActivity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//Array que asociaremos al adaptador
String[] array = new String[] {
"Elemento 1"
,"Elemento 2"
,"Elemento 3"
,"Elemento 4"
,"Elemento 5"
,"Elemento 6"
};
//Creacin del adaptador, vamos a escoger el layout
//simple_list_item_1, que los mostr
ListAdapter adaptador = new ArrayAdapter<string>(
this, android.R.layout.simple_list_item_1, array);
//Asociamos el adaptador a la vista.
ListView lv = (ListView) findViewById(android.R.id.list);
lv.setAdapter(adaptador);
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Toast.makeText(
AdaptadoresActivity.this

,l.getItemAtPosition(position).toString()
,Toast.LENGTH_LONG)
.show();
}
}
</string>

El resultado de este cdigo es el siguiente, para una adaptador con datos:

Y para un adaptador sin datos:

El ejemplo de esta entrada esta disponible para su descarga:

Download Interfaz grfica Adapters IAdaptadores.zip Downloaded 687


times

PROGRAMACIN ANDROID:
INTERFAZ GRFICA
ADAPTERS II
2014/ 09/ 04/ B Y ALEJANDRO ALCALDE , 26 COMMENTS ,
IN ANDROID, OPENSOURCE

En Programacin Android: Interfaz grfica Adapters I, se vio como crear y usar


adaptadores simples para nuestras aplicaciones. Ahora vamos a ver como crear los nuestros
propios.

Para explicar el funcionamiento nos vamos a basar en un ejemplo que listar las distintas
versiones de Ubuntu.
Lo primero que debemos hacer es crearnos una clase que almacenar los datos de cada una
de las versiones:
package app.elbauldelprogramador.adapters2;
public class VersionesUbuntu {
private String nombre;
private String version;

private int logotipo;


public VersionesUbuntu(String nombre, String version, int logotipo) {
this.nombre = nombre;
this.version = version;
this.logotipo = logotipo;
}
public void setNombre(String nombre) { this.nombre = nombre; }
public String getNombre() { return nombre; }
public void setVersion(String version) { this.version = version; }
public String getVersion() { return version; }
public void setLogotipo(int logotipo) { this.logotipo = logotipo; }
public int getLogotipo() { return logotipo; }
}

A continuacin vamos a crear el adaptador desde cero, siendo necesario extender de la clase
BaseAdapter.
package app.elbauldelprogramador.adapters2;
import java.util.ArrayList;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class VersionesUbuntuAdapter extends BaseAdapter{
private ArrayList<versionesubuntu> listadoVersiones;
private LayoutInflater lInflater;

public VersionesUbuntuAdapter(Context context,


ArrayList</versionesubuntu><versionesubuntu> versiones) {
this.lInflater = LayoutInflater.from(context);
this.listadoVersiones = versiones;
}
@Override
public int getCount() { return listadoVersiones.size(); }
@Override
public Object getItem(int arg0) { return listadoVersiones.get(arg0); }
@Override
public long getItemId(int arg0) { return arg0; }
@Override
public View getView(int arg0, View arg1, ViewGroup arg2) {
ContenedorView contenedor = null;
if (arg1 == null){
arg1 = lInflater.inflate(R.layout.lista_versiones_ubuntu, null);
contenedor = new ContenedorView();
contenedor.nombreVersion = (TextView) arg1.findViewById(R.id.nomVersion);
contenedor.numeroVersion = (TextView) arg1.findViewById(R.id.numVersion);
contenedor.logoVersion = (ImageView) arg1.findViewById(R.id.logo);
arg1.setTag(contenedor);
} else
contenedor = (ContenedorView) arg1.getTag();
VersionesUbuntu versiones = (VersionesUbuntu) getItem(arg0);
contenedor.nombreVersion.setText(versiones.getNombre());
contenedor.numeroVersion.setText(versiones.getVersion());
contenedor.logoVersion.setImageResource(versiones.getLogotipo());
return arg1;
}
class ContenedorView{
TextView nombreVersion;
TextView numeroVersion;
ImageView logoVersion;

}
}
</versionesubuntu>

Lo que hace esta clase es lo siguente, en su constructor, carga un objeto LayoutInflater, que
infla los objetos XML del layout para que podamos usarlos, convirtindolos en un objeto
java.
Tambin tenemos tres mtodos sencillos (getCount, getItem, getItemId), que se encargan
de devolver el nmero de elementos de la coleccin, un elemento en concreto y el
identificador de un elemento.
El mtodo getView() se invoca cada vez que hay que dibujar la lista.
En este caso, para la lista, hemos usado un layout personalizado, que es el siguiente:
< ?xml version="1.0" encoding="utf-8"?>
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<imageview android:id="@+id/logo"
android:src="@drawable/ocelot"
android:layout_alignParentLeft="true"
android:padding="5px"
android:layout_width="128px"
android:layout_height="128px"/>
<textview android:id="@+id/nomVersion"
android:layout_toRightOf="@id/logo"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="15dip"
android:text="VersionNom" />
<textview android:id="@+id/numVersion"
android:layout_below="@id/nomVersion"
android:layout_toRightOf="@id/logo"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="VersionNum" />

</relativelayout>

Por ltimo, tan solo queda usar el adaptador que hemos creado en los pasos anteriores en
nuesta Actividad principal, en este caso, una ListActivity:
package app.elbauldelprogramador.adapters2;
import java.util.ArrayList;
import android.app.ListActivity;
import android.os.Bundle;
public class Adaptadores2Activity extends ListActivity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ArrayList<versionesubuntu> versiones =
new ArrayList</versionesubuntu><versionesubuntu>();
versiones.add(
new VersionesUbuntu("Lucid Lynx", "10.4 LTS", R.drawable.lucid));
versiones.add(
new VersionesUbuntu("Maverick Meerkat", "10.10", R.drawable.u1010));
versiones.add(
new VersionesUbuntu("Natty Narwhal", "11.04", R.drawable.natty));
versiones.add(
new VersionesUbuntu("Oneiric Ocelot", "11.10", R.drawable.ocelot));
VersionesUbuntuAdapter adaptador = new VersionesUbuntuAdapter(
Adaptadores2Activity.this, versiones);
setListAdapter(adaptador);
}
}
</versionesubuntu>

Ya solo resta ejecutarlo y ver el resultado:

Como siempre, se puede descargar este ejemplo:

Download Interfaz grfica Adapters IIAdaptadores2.zip Downloaded


815 times

PROGRAMACIN ANDROID:
INTERFAZ GRFICA MENS
2014/09/04/ B Y ALEJANDRO ALCALDE , 4 COMMENTS ,
IN ANDROID, OPENSOURCE

Los mens en las aplicaciones son algo que encontramos frecuentemente, de hecho, casi
todos los terminales Android tienen un botn especfico para desplegarlos.
Se dispone de distintos tipo de mens:

Options Menu: El men tpico, que se despliega al pulsar la tecla men, que se
divide en dos grupos:
Icon menu:Muestra un men con iconos, 6 elementos como mximo.
Expanded Menu: Se usa cuando hay ms de 6 elementos, mostrando un elemento
con la palabra Ms.

Context Menu: Mens contextuales desplegados al realizar una pulsacin larga en una
View.
Submens: Mens desplegados al pulsar sobre un elemento de otro men.

OPTIONS MENU

Lo ms simple y sencillo es definir los mens en XML, colocado en ./res/menu, para este
ejemplo he definido el siguiente menu, que contiene dos elementos, un About y un Quit:
< ?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/about"
android:icon="@drawable/about"
android:title="About App">
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/submenu"
android:title="Submen de &amp;quot;About App&amp;quot;"/>
</menu>
</item>
<item android:id="@+id/quit"
android:title="Quit App"
android:icon="@drawable/quit"/>
</menu>

Bien, voy a explicar un poco la estructura de este men, Empezamos declarando el men
con la etiqueta
, que contendr todos sus elementos bajo la etiqueta , en este caso, tambin tenemos
un submenu, que se declara igual que el men principal.
Los atributos de cada elemento son su identificador, el icono a mostrar y el ttulo.
Para poder usar este men, necesitamos inflarlo (Convertir el fichero XML en un objeto
java), para hacer esto, hay que llamar a MenuInflater.inflate(), el cdigo siguiente infla el
fichero xml anterior en el mtodo callbackonCreateOptionsMenu().
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.ejemplo_menu, menu);
return true;
}

Ahora, tenemos que responder a las acciones del usuario cuando pulse algn elemento de
nuestro men, para ello vamos a sobreescribir el mtodoonOptionsItemSelected()
@Override

public boolean onOptionsItemSelected(MenuItem item) {


switch (item.getItemId()) {
case R.id.about:
Toast.makeText(
MenusActivity.this
,"Ejemplo Mens App"
,Toast.LENGTH_LONG)
.show();
return true;
case R.id.quit:
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}

CONTEXT MENU
Los mens contextuales son similares a los mens mostrados al hacer click con el botn
derecho de un ratn en un PC, para crearlos, debemos sobreescribir el
mtodo onCreateContextMenu(), donde inflaremos el archivo xml.
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.ejemplo_menu, menu);
}

Al igual que en los options menu, tenemos que responder a las acciones del usuario:
@Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.about:
Toast.makeText(
MenusActivity.this
,"Ejemplo Mens App"

,Toast.LENGTH_LONG)
.show();
return true;
case R.id.quit:
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}

Pero este men contextual no se va a mostrar, ya que tenemos que asociarlo para que se
lanze al realizar una pulsacin prolongada sobre una view, en este caso un botn:
final Button boton = (Button) findViewById(R.id.button1);
registerForContextMenu(boton);

Aqu dejo algunas capturas de pantalla de la aplicacin:

Podis encontrar ms informacin sobre Mens en la pgina oficial de Android


Y, como no, descargar este proyecto: Mens en android. Espero que os sirva de ayuda.

PROGRAMACIN ANDROID:
INTERFAZ GRFICA
DILOGOS Y
NOTIFICACIONES
2014/ 09/ 04/ B Y ALEJANDRO ALCALDE , 21 COMMENTS ,
IN ANDROID, OPENSOURCE

En ocasiones hay que mostrar mensajes al usuario para informarle del estado de la
aplicacin, o del estado de las operaciones que se estn llevando a cabo.
Android dispone de tres tipos de notificaciones:

Notificaciones Toast.

Notificaciones en la barra de estado.

Ventanas de dilogo.

NOTIFICACIONES TOAST

Una notificacin Toast es un mensaje que se muestra superpuesto en la pantalla. Solo ocupa
el espacio necesario para mostrar la alerta, mientras tanto, la actividad que estaba visible
puede seguir usndose. Este tipo de notificaciones se muestran durante un periodo de
tiempo y desaparecen, no permiten interactuar con ellas. Debido a que un Toast se crea
mediante un servicio en segundo plano, puede aparecer aunque la aplicacin no est visible.
A lo largo de todas las entradas sobre Android, se ha usado mucho este tpo de
notificaciones:
Toast.makeText(context, text, duration).show();

Para pasar el contexto, tenemos varias posibilidades, NombreActividad.this,


ogetApplicationContext().
Para fijar la duracin del mensaje, usamos una de las dos constantes
predefinidas,Toast.LENGTH_SHORT Toast.LENGTH_LONG
En este caso, vamos a crear un layout personalizado para mostrar el Toast:
< ?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/toastLayout"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:background="#DAAA"
>
<imageview android:layout_width="48px"
android:layout_height="48px"
android:src="@drawable/ok"
android:padding="5dip"
android:id="@+id/ok"/>
<textview android:id="@+id/textview"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:text="Toast con layout personalizado"
android:textColor="#fff"
android:gravity="center_vertical|center_horizontal"/>

</linearlayout>

Hay que asignar un id al LinearLayout, que usaremos posteriormente. Tambin hemos


creado un ImageView para mostrar un icono, y un TextView para mostrar el mensaje.
El siguiente paso es inflar este layout desde el cdigo:
LayoutInflater inflater = getLayoutInflater();
View layout = inflater.inflate(
R.layout.toast_layout
,(ViewGroup) findViewById(R.id.toastLayout));
Toast toast = new Toast(getApplicationContext());
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
toast.setDuration(Toast.LENGTH_LONG);
toast.setView(layout);
toast.show();

Listo, al ejecutar la aplicacin tendremos un Toast como este:

Para saber ms acerca de los mensajes toast puede


visitar:http://developer.android.com/guide/topics/ui/notifiers/toasts.html

NOTIFICACIONES EN LA BARRA DE ESTADO


Este tipo de notificaciones muestran un icono en la barra de estado, cuando desplegamos
esta barra, veremos el icono acompaado de un texto descriptivo indicando que ha pasado
algo (Como que hemos recibido un nuevo mensaje, o un correo electrnico).
Los pasos necesarios para crear este tipo de notificaciones son, usar el gestor de
notificaciones del sistema (NotificationManager) y posteriormente crear un objeto
Notification en el que configuraremos nuestra notificacin. Vamos a ver como hacerlo.
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
//Agregando el icono, texto y momento para lanzar la notificacin
int icon = R.drawable.ok;
CharSequence tickerText = "Notification Bar";
long when = System.currentTimeMillis();
Notification notification = new Notification(icon, tickerText, when);
Context context = getApplicationContext();
CharSequence contentTitle = "Notificacin en barra";
CharSequence contentText = "Mensaje corto de la notificacin";
//Agregando sonido
notification.defaults |= Notification.DEFAULT_SOUND;
//Agregando vibracin
notification.defaults |= Notification.DEFAULT_VIBRATE;
Intent notificationIntent = new Intent(this, NotificacionesActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
mNotificationManager.notify(HELLO_ID, notification);

El resultado es el siguiente:

Al igual que los Toast, se puede crear un layout personalizado, para ms informacin
visite:http://developer.android.com/guide/topics/ui/notifiers/notifications.html

DILOGOS
Por ltimo, vamos a ver los dilogos, que son ventanas que se muestran delante de las
actividades, y pueden recibir acciones del usuario, hay varios tipos:

AlertDialog

ProgressDialog

DatePickerDialog

TimePickerDialog

Si necesitamos un Dilogo que no sea uno de los de arriba, podemos extender de la


clase Dialog, y crear el nuestro propio.
La clase Activity implementa mtodos para gestionar los dialogos, son:

onCreateDialog(int): Encargado de crear el dilogo.

onPrepareDialog(int): Llamado justo antes de mostrarlo.

showDialog(int): Para mostrarlo.

dismissDialog(int): cierra el dilogo, guardando su estado.

removeDialog(int): cierra el dialogo elminndolo por completo.

Vamos a ver un ejemplo de AlertDialog, que preguntar si queremos salir de la aplicacin:


AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setMessage("Salir?");
dialog.setCancelable(false);
dialog.setPositiveButton("Si", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
NotificacionesActivity.this.finish();
}
});
dialog.setNegativeButton("No", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
dialog.show();

Tambin vamos a ver un ProgressDialog, indefinido (Que nunca termina).


ProgressDialog.show(
NotificacionesActivity.this
,"ProgressDialog"
,"Ejemplo dilogo de progreso"
,true
,true);

Los dos ltimos parmetros son para que el dilogo sea indeterminado, y para que se pueda
cerrar con la flecha de atrs del terminal.

Se puede descargar el proyecto de esta entrada aqu:

Download NotificacionesNotificaciones.zip Downloaded 1546 times

PROGRAMACIN ANDROID:
INTERFAZ GRFICA
ESTILOS Y TEMAS
2014/09/04/ B Y ALEJANDRO ALCALDE , 2 COMMENTS ,
IN ANDROID, OPENSOURCE

Un estilo es una coleccin de propiedades que especifican que aspecto ha de tener un objeto
View o una ventana. Con los estilos podemos definir propiedades como la altura, relleno,
color del texto, fondo etc. Los estilos en Android comparten la filosofa de las hojas de
estlo en cascada (CSS), permitiendo separar el diseo del contenido.
Como podemos comprobar con este ejemplo, el cdigo queda mucho ms lmpio usando
estilos:

Sin estilos:
<textview android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#00FF00"
android:typeface="monospace"
android:text="@string/hello" />

Con estilos:
<textview style="@style/CodeFont"
android:text="@string/hello" />

DEFINIENDO ESTILOS
Los estilos se definen en un fichero de recursos distinto al layout, colocado en el
directorio /res/values. stos ficheros no tienen porqu tener un nombre en concreto, pero
por convencin se suelen llamar styles.xml themes.xml
El nodo raiz de estos archvos debe ser .
Para cada estilo que queramos definir, hay que aadir un elemento
y un atributo name para ese estlo (es obligatorio), despus, aadiremos un elemento para
cada propiedad del estilo, que debe tener obligatoriamente el atributo name que declara la
propiedad del estilo y su valor. Los valores parapueden ser una palabra clave, valor
hexadecimal, una referencia a un recurso u otro valor dependiendo de la propiedad del
estilo. Veamos un ejemplo:
<style name="CodeFont" parent="@android:style/TextAppearance.Medium">
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textColor">#00FF00</item>
<item name="android:typeface">monospace</item>
</style>

En tiempo de compilacin, los elementos se convierten en un recurso que podremos


referenciar posteriormente mediante el atributo name del estilo, como vimos en el primer
ejemplo (@style/CodeFont).
El atributo parent es opcional y especifica el ID de otro estilo del cual queremos heredar
sus propiedades, pudiendo as sobreescribirlas.

HERENCIA
Como acabamos de ver, el atributo parent sirve para heredar propiedades de otros estilos,
podemos heredar tanto de estilos del sistema como de los nuestros propios:

Del sistema:
<style name="GreenText" parent="@android:style/TextAppearance">
<item name="android:textColor">#00FF00</item>
</style>

De nuestros propios estilos:


<style name="CodeFont.Red">
<item name="android:textColor">#FF0000</item>
</style>
<style name="CodeFont.Red.Big">
<item name="android:textSize">30sp</item>
</style>

En este caso, no usamos el atributo parent, ya que estamos usando un estilo propio, tambin
se puede apreciar que podemos heredar cuantas veces queramos, como es el caso de
(CodeFont.Red.Big)

APLICAR ESTILOS Y TEMAS A LA INTERFAZ


GRFICA
Hay dos formas de aplicar estilos a la UI:

A una View individual, aadiendo el atributo style a un elemento del layout.

A una aplicacin o actividad completa, mediante el atributo android:theme del


elemento o en el Android manifest.

Como vimos al principio, para aplicar un estilo a una View concreta


usamosstyle=@style/NombreDelEstilo
Para aplicar un tema a una actividad o aplicacin usaremos:
<application android:theme="@style/CustomTheme">
</application>

Para aplicarlos sobre actividades, usamos:


<activity android:theme="@android:style/Theme.Dialog">
</activity><activity android:theme="@android:style/Theme.Translucent">

</activity>

En este caso, estos temas ya vienen predefinidos, y se ven as, respectivamente:

A continuacin, dejo una captura del ejemplo que he hecho para esta entrada, que se puede
descargar desde:

Download EstilosyTemasEstilosyTemas.zip Downloaded 1243 times

Para saber ms acerca de los estilos y temas visite la pgina oficial: Applying Styles and
Themes

PROGRAMACIN ANDROID:
RECURSOS INTRODUCCIN
2014/09/04/ B Y ALEJANDRO ALCALDE , 0 COMMENTS ,
IN ANDROID, OPENSOURCE

Ya hemos visto que Android separa los recursos (imgenes, sonidos etc) del cdigo
colocndolos organizados dentro del directorio ./res. Esto nos facilita su mantenimiento,
adems de permitirnos usar diferentes recursos dependiendo de la configuracin del
terminal.

Separar los recursos permite proporcionar alternativas para dar soporte a las distintas
configuraciones de dispositivos, como idiomas o tamaos de pantalla. Para conseguir
compatibilidad con las diferentes configuraciones, debemos organizar los recursos en el
directorio ./res de nuestros proyectos, dentro de subdirectorios para agruparlos por tipo y
configuracin.
Un recurso puede usarse por defecto (Se mostrar en cualquier dispositivo,
independientemente de su configuracin), o pueden especificarse recurosos alternativos
(Que se mostrn en los dispositivos para configuraciones determinadas), vemoslo con unas
imgenes de ejemplo:
Dos dispositivos distintos, usando recursos por defecto:

Dos dispositivos distintos, usando recursos alternativos:

Por ejemplo, podemos crear iconos ms pequeos para que sean mostrados en los
terminales con pantallas ms pequeas o disear una disposicin de pantalla diferente para
cuando la aplicacin se est ejecutando en modo apaisado, para conseguir esto,
simplemente creamos una carpeta de recursos aadiendo el sufijo que indica la situacin en
la que debe usarse.
Para el caso del idioma, crearamos un archivo xml con las cadenas traducidas a dicho
idioma, en este caso ingls, y lo colocaramos dentro de ./res/values-en/strings.xml.
Para el caso del layout personalizado cuando la pantalla est en modo apaisado, meteramos
nuestro layout dentro de res/layout-land/
.
Para saber ms acerca de los tipos de sufijos que se pueden usar, visita Providing Resources
en la pgina oficial de Android.

PROGRAMACIN ANDROID:
RECURSOS USANDO
RECURSOS
2014/ 09/ 04/ B Y ALEJANDRO ALCALDE , 1 COMMENT ,
IN ANDROID, OPENSOURCE

A todos los recursos que colocamos en las subcarpetas de ./res/ se puede acceder a travs de
la clase R de nuestro proyecto.
Esta clase R la genera el comando aapt en una pasada anterior a la compilacin
(Eclipse, por defecto, la va generando continuamente conforme cambiamos los
recursos). Contiene todos los identificadores de recursos para poder referenciarlos.
Al igual que la carpeta res, la clase R se organiza en subclases, as por ejemplo el icono
que colocamos en ./res/drawable/icon tiene su correspondencia en
R.drawable.icon (que es un identificador esttico de tipo int y sirve para acceder
al recurso).

As pues los ID de recurso estn compuestos de:


Clase R que contienen todos los recursos.

Subclase de recurso, cada grupo tiene la suya (drawable, string, style, layout).
Nombre del recurso que, segn el tipo, ser: el nombre del fichero sin la extensin o el
atributo xml android:name si es un valor sencillo (cadena, estilo, etc.).
Tenemos dos formas de acceder a los recursos definidos en la clase R:
En el cdigo, accediendo a las propiedades de la clase R directamente
(R.string.nombre).
En los ficheros XML, usando una notacin especial: @grupo_recursos/
nombre_recurso, es decir, el recurso anterior se accedera con @string/nombre.
Si lo que queremos es acceder a un recurso definido por el sistema antepondremos el
prefijo android:

Desde el cdigo: android.R.layout.simple_list_item_1.

En los ficheros XML: @android:layout/simple_list_item_1.

REFERENCIANDO ATRIBUTOS DE ESTILO


Cuando aplicamos estilos a nuestros layout puede interesarnos acceder a un
atributo concreto de un estilo, para eso tenemos una sintaxis especfica que
podemos usar en nuestros XML:
?[:][/]
As por ejemplo si queremos colocar un texto pequeo usaremos:
?android:attr.textAppearanceSmall
Si queremos, tambin podemos utilizar nuestros propios atributos.
Primero lo definimos con un tag attrdentro de ./res/values/attr.xml.
< ?xml version="1.0" encoding="utf-8"?>
<resources>

<attr name="cabecera" format="reference" />


</resources>

Ahora ya podemos usar esa propiedad en nuestros estilos:


Primero definimos un estilo de texto llamado TituloRojo, y luego lo aplicamos
al atributo que hemos creado llamado Cabecera. Obsrvese que como es un
atributo propio, no usamos el espacio de nombres android:.
Si luego quisiramos acceder a este atributo al definir un layout podramos usar
la sintaxis mencionada:
./res/layout/milayout
<
pre lang=xml>
< ?xml version=1.0 encoding=utf-8?>
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<textview android:layout_width=fill_parent
android:layout_height=fill_parent
android:text=No hay datos disponibles
style=?attr/cabecera />

./res/values/style.xml
&lt; ?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="MiTema" parent=
"@android:style/Theme.Light">
<item name="android:windowBackground">
@drawable/fondo</item>
<item name="cabecera">
@style/TituloRojo</item>
</style>
<style name="TituloRojo"
parent="@android:style/TextAppearance.Large">
<item name="android:textColor">#FF0000</item>

<item name="android:textStyle">bold</item>
</style>
</resources></p>

PROGRAMACIN ANDROID:
RECURSOS STRINGS
2014/ 09/ 04/ B Y ALEJANDRO ALCALDE , 1 COMMENT ,
IN ANDROID, OPENSOURCE

Como ya hemos visto, los recursos juegan un papel muy importante en la arquitectura
Android. Un recurso en Android es un archivo (como un fichero de msica) o un valor
(como el ttulo de un Dilogo) que est ligado a una aplicacin ejecutable. Estos archivos
estn ligados a un ejecutable de tal manera que podemos cambiarlos sin necesidad de
recompilar la aplicacin.
Los ejemplos de recursos ms familiares son cadenas de texto, colores e imgenes. En lugar
de escribir las cadenas de texto en el cdigo fuente, usamos sus IDs. Esta indireccin nos
permite cambiar el valor de la cadena sin tener que cambiar el cdigo fuente.
Existen mucho recursos en Android, que vamos a ver a lo largo de las etradas posteriores.
Empezaremos por un recurso muy comn, los string:

RECURSOS STRING
Android permite definir strings en uno o ms archivos XML de recursos. Estos archivos
estn bajo el directorio ./res/values. El nombre del archivo XML para este tipo de recurso

puede ser cualquiera, pero por convencin se suele llamarstrings.xml. Veamos un ejemplo
de este fichero:
< ?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello</string>
<string name="app_name">Hello app_name</string>
</resources>

Cuando este archivo se crea o modifica, el plugin ADT de eclipse automticamente crear o
actualizar una clase java de nuestra aplicacin llamada R.java alojada en el directorio
./gen, que contiene los IDs nicos para las dos cadenas que acabamos de crear.
Para el fichero strings.xml que acabamos de crear, tendremos lo siguiente en la clase R:
package nombre.de.nuestro.paquete;
public final class R {
//.. otras entradas dependiendo de tu proyecto y aplicacin
public static final class string {
//.. otras entradas dependiendo de tu proyecto y aplicacin
public static final int app_name=0x7f040001;
public static final int hello=0x7f040000;
//.. otras entradas dependiendo de tu proyecto y aplicacin
}
//.. otras entradas dependiendo de tu proyecto y aplicacin
}

Como vemos como primero R.java define una clase principal en el paquete raiz:public
final class R. Depues, define una clase interna llamada public static final class string.
R.java crea esta clase esttica interna como espacio de nombres para guardar los IDs de los
recursos string.
La definicin de los dos public static final int llamados app_name y hello son los IDs de
los recursos que representan nuestras cadenas de texto. Podemos usar estos IDs en
cualquier lugar de nuestro cdigo mediante R.string.hello o R.string.app_name
La estructura del fichero string.xml es muy fcil de seguir. Tenemos un elemento raiz
llamado , seguido por uno o ms elementos hijos . Cada elemento tiene una propiedad
llamada name que ser la que usar como identificador R.java.

Para comprobar que se permiten varios recursos de string en el directorio values, vamos a
crear otro fichero llamado strings1.xml con lo siguiente:
< ?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello1">Hello</string>
<string name="app_name1">Hello app_name</string>
</resources>

Ahora, el plugin ADT de eclipse se encargar de actualizar el fichero R.java, que contendr
lo siguiente:
package nombre.de.nuestro.paquete;
public final class R {
//.. otras entradas dependiendo de tu proyecto y aplicacin
public static final class string {
//.. otras entradas dependiendo de tu proyecto y aplicacin
public static final int app_name=0x7f040001;
public static final int hello=0x7f040000;
public static final int app_name1=0x7f040006;
public static final int hello1=0x7f040005;
//.. otras entradas dependiendo de tu proyecto y aplicacin
}
//.. otras entradas dependiendo de tu proyecto y aplicacin

PROGRAMACIN ANDROID:
RECURSOS LAYOUT
2014/09/04/ B Y ALEJANDRO ALCALDE , 0 COMMENTS ,
IN ANDROID, OPENSOURCE

En Android, cada pantalla de una aplicacin habitualmente se carga desde un fichero XML
que actua de recurso. Un recurso layout es un recurso clave que se usa en Android para
componer la UI de nuestra aplicacin. Vamos a considerar el segmenteo de cdigo siguiente
como ejemplo de una actividaden Android.

public class PrincipalActivity extends Activity {


/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}

La lnea setContentView(R.layout.main); seala que hay una clase esttica


llamadaR.layout y, que dentro de esa clase hay una constante entera llamada main que
apunta a una vista definida por un fichero de recursos layout xML. El nombre del fichero
XML es main.xml, el cual debe estar en el directorio ./res/layout. El contenido de este
fichero es el siguiente:

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


<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<textview android:id="@+id/text1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
<button android:id="@+id/b1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"/>
</linearlayout>

Vamos a ver la composicin de este fichero, como elemento raiz tenemos un elemento
llamado , que contiene un TextView seguido de un Button. Un LinearLayout coloca a sus
hijos uno detrs de otro vertical u horizontalmente.
Para cada pantalla que queramos hacer, necesitaremos ficheros layout distintos, mejor
dicho, cada layout debe estar en un fichero, como ./res/layout/screen1.xml y
./res/layout/screen2.xml
Por cada archivo de layout que tengamos en ./res/layout, se generar una entrada en R.java.
Por ejemplo, si tenemos estos dos archivos, file1.xml y file2.xml, en R.java aparecer:
// ...
public static final class layout {
public static final int file1=0x7f030000;
public static final int file2=0x7f030001;
}
// ..

Las vistas definidas en estos layout, como el TextView son accesibles mediante cdigo java
a travs sus IDs de recursos generadas en R.java.
TextView tv = (TextView) this.findViewById(R.id.text1);
tv.setText("Texto para el TextView")

En este ejemplo, hemos localizado el TextView usando el mtodo findViewById()de la


clase Activity. La constante R.id.text1 corresponde al ID definido para el TextView en el
fichero XML, que creamos de la siguiente manera:
<textview android:id="@+id/text1"
....
/>

El valor del atributo id, indica que la constante llamada text1 ser usada para identificar
nicamente a esa vista. El signo + de +id/text1 significa que el ID text1 ser creado si no
existe. En la siguiente entrada se tratar la sintaxis de los recursos.

PROGRAMACIN ANDROID:
RECURSOS SINTAXIS DE
LOS RECURSOS
2014/09/04/ B Y ALEJANDRO ALCALDE , 0 COMMENTS ,
IN ANDROID, OPENSOURCE

Independientemente del tipo de recurso (de string y layout son los dos que hemos visto
hasta ahora), Todos los recursos Android estn identificados (o referenciados) por sus ids en
cdigo fuente Java.
La sintaxis que usamos para crear un ID de recurso en un fichero XML se llama resourcereference syntax (sintaxis de referencia a recurso). La sintaxis del atributo id que vimos
anteriormente (@+id/text1) tiene la siguiente estructura formal:

@[package:]type/name

El tipo (type), corresponde a uno de los espacios de nombres de tipos de recursos


disponible en R.java, que son algunos de los siguientes:

R.drawable

R.id

R.layout

R.string

R.attr

R.plural

R.array

En sintaxis de referencia a recursos XML, estos tipos se nombraran as:

drawable

id

layout

string

attr

plurals

string-array

La parte name en @[package:]type/name corresponde al nombre dado al recurso (text1 en


nuestro ejemplo anterior), este nombre, tambin ser represantado como una constante
entera en R.java.
Si no se especifica ningn paquete en @[package:] (de ah que en la representacin formal
de la sintaxis aparezca entre corchetes, que quiere decir que es opcional); el par
tipo/nombre ser resuelto basndose en los recursos locales y en el archivo R.java local de
nuestra aplicacin.
Si especificamos android:type/name, el id referenciado ser resuelto usando el paquete
android y especficamente a travs del archivo android.R.java. Podemos usar cualquier
nombre de paquete java en el lugar de @[package:] para localizar el archivo R.java correcto
y resolver la referencia al recurso en cuestin. Ahora que conocemos la sintaxis, vamos a
analizar unos ejemplos. Ntese que la parte izquierda del ID android:id no es parte de la
sintaxis. android:id simplemente indica que vamos a crear un id para un control como lo
es el TextView.

<textview android:id=text>
<!-- Error de compilacin, como id no tomar cadenas de texto sin formato. -->
</textview><textview android:id=@text>
<!-- Sintaxis incorrecta. No disponde de tipo y nombre-->
<!-- debera ser @id/text, @+id/text o @string/string1-->
<!-- obtendremos el siguiente error: No resource type specified-->
</textview><textview android:id=@id/text>
<!-- Error: No hay ningn recurso que coincida con el id text-->
<!-- a no ser que lo hayamos definido nosotros mismos con anterioridad.-->
</textview><textview android:id=@android:id/text>
<!-- Error: el recurso no es pblico-->
<!-- lo que indica que no hay tal identificacin en android.R.id-->
<!-- Por supuesto esto ser vlido si el archivo android R.java defini un id con este nombre.-->
</textview><textview android:id=@+id/text>
<!-- Correcto: crea un id llamado text en el paquete R.java local.-->
</textview>

En la sintaxis @+id/text, el signo + tiene un significado especial. Le dice a Android que


el ID puede no existir an y, que en ese caso, cree uno nuevo y lo llame text.

PROGRAMACIN ANDROID:
RECURSOS COMPILADOS Y
NO COMPILADOS
2014/09/04/ B Y ALEJANDRO ALCALDE , 0 COMMENTS ,
IN ANDROID, OPENSOURCE

Android soporta principalmente dos tipos de recursos: archivos XML y archivos raw (como
imgenes, audio y vdeo). Incluso dentro de los archivos XML, has podido ver que en
algunos casos los recursos estn definidos como valores dentro del archivo XML (las
cadenas de texto, por ejemplo). En otras ocasiones, un archivo XML es un recurso por s
mismo, como los Layout.
Podemos encontrar dos tipos de archivos XML: uno se compilar a un formato binario y el
otro se copiar tal como es al dispositivo. Como ejemplo podemos poner los ficheros XML
de recursos string y los ficheros de layout, ambos se compilarn a un formato binario antes
de formar parte del paquete de instalacin. Estos ficheros XML tienen formatos
predefinidos en los que los nodos XML se traducen a IDs.

Por supuesto, tambin se pueden elegir archivos XML para guardarlos con su formato
propio, estos archivos no sern interpretados y se les asignar un ID de recurso para poder
identificarlos. Sin embargo, podemos querer que estos ficheros tambin se compilen a un
formato binario. Para lograrlo, hay que colocarlos bajo el directorio ./res/xml. En tal caso,
deberemos usar los lectores XMl que proporciona Android para acceder a los nodos del
fichero.
Si colocamos cualquier tipo de archivo, incluyendo ficheros XML, bajo ./res/raw, no se
compilarn a un formato binario. Deberemos entonces, para leerlos, usar Stream. Los
archivos de audio y vdeo entran en esta categora tambin.

Hay que destacar, que al formar parte el directorio rar de ./res/*, incluso estos archivos raw
de audio y vdeo sern localizados como todos los dems recursos.
Como vimos, los recursos se almacenan en varios subdirectorios segn su tipo, a
continuacin vemos algunos de los subdirectorios ms importantes de la carpeta res.:

anim: Archivos compilados de animaciones.

drawable: Bitmaps

layout: definicin de vistas y UI.

values: Arrays, colores, dimensiones, strings y estilos.

xml: fircheros XML arbitrarios.

raw: ficheros raw no compilados.

El compilador de recursos AAPT (Android Asset Packaging Tool), compila todos los
recursos excepto los raw y lo coloca en el fichero .apk (Android package) final. Este
fichero es el que contiene el cdigo de las aplicaciones Android y los recursos, similar a
los .jar de Java. Estos ficheros son los que usamos para instalar aplicaciones en Android.

PROGRAMACIN ANDROID:
RECURSOS ARRAYS DE
STRINGS
2014/09/04/ B Y ALEJANDRO ALCALDE , 9 COMMENTS ,
IN ANDROID, OPENSOURCE

Se pueden definir arrays de strings como recursos en cualquier archivo bajo el subdirectorio
./res/values. Para definirlos, usaremos un nodo XML llamado string-array. Este nodo es un
hijo de resources, al igual que el nodo string. A continuacin, vamos a ver como crear un
array de strings:

<resources>
<string -array name="test_array">
<item>uno</item>
<item>dos</item>
<item>tres</item>
</string>
</resources>

Una vez definido el recurso, podemos usarlo en el cdigo Java de la siguiente manera:
//Accedemos al objeto 'Recursos' desde la Activity
Resources res = nombre-de-la-actividad.getResources();

String strings[] = res.getStringArray(R.array.test_array);


//Mostramos el array
for (String s: strings)
Log.d("ejemplo", s);

PROGRAMACIN ANDROID:
RECURSOS PLURALES
2014/09/04/ B Y ALEJANDRO ALCALDE , 0 COMMENTS ,
IN ANDROID, OPENSOURCE

Los recursos Plurals son un conjunto de strings. Estos strings representan una forma de
escribir cantidades numricas, por ejemplo, cuantos huevos hay en una cesta. Vamos a ver
un ejemplo:
Hay 1 huevo
Hay 2 huevos
Hay 10 huevos
Como puedes notar, las frases son iguales para los nmeros 2 y 10. Sin embargo, la frase
para 1 huevo es diferente. Android permite representar esta variacin con el recurso
llamado plurals. En el siguiente ejemplo vemos como se representan estas dos variaciones.

<resources>
<plurals name="huevos_en_una_cesta">
<item quantity="one">Hay 1 huevo</item>
<item quantity="other">Hay %d huevos</item>
</plurals>
</resources>

Las dos variaciones se representan como dos cadenas de texto diferentes bajo el mismo
plural. Ahora, podemos usarlo desde el cdigo Java para mostrar correctamente la cadena
correspondiente a la cantidad de huevos. El primer parmetro de getQuantityString() es el
ID del plural. El segundo selecciona la cadena a usar. Cuando el valor de la cantidad es 1,
usamos la cadena tal como es. Cuando el valor es distinto a 1, debemos pasar un tercer
parmetro que ser el valor a colocar en el lugar de %d. Siempre deber haber al menos 3
parmetros si usamos cadenas formateadas en los plurales.
Resources res = tu_actividad.getResources();
String s1 = res.getQuantityString(R.plurals.huevos_en_una_cesta, 0, 0);
String s2 = res.getQuantityString(R.plurals.huevos_en_una_cesta, 1, 1);
String s3 = res.getQuantityString(R.plurals.huevos_en_una_cesta, 2, 2);
String s4 = res.getQuantityString(R.plurals.huevos_en_una_cesta, 10, 10);

Con este cdigo, cada cantidad se mostrar con su correcta cadena de texto.
Existen otras posibilidades que podemos aplicar al atributo quantity del elemento item. Para
ello, recomiendo que lean el cdigo fuente de Resources.java y PluralsRules.java para
entenderlo correctamente. An as, dejo lo fundamental de estas dos ficheros para que
entiendan bien el funcionamiento:

PLURALRULES.JAVA
abstract class PluralRules {
static final int QUANTITY_OTHER = 0x0000;
static final int QUANTITY_ZERO = 0x0001;
static final int QUANTITY_ONE = 0x0002;
static final int QUANTITY_TWO = 0x0004;
static final int QUANTITY_FEW = 0x0008;
static final int QUANTITY_MANY = 0x0010;
static final int ID_OTHER = 0x01000004;
abstract int quantityForNumber(int n);
final int attrForNumber(int n) {
return PluralRules.attrForQuantity(quantityForNumber(n));
}
static final int attrForQuantity(int quantity) {
// see include/utils/ResourceTypes.h

switch (quantity) {
case QUANTITY_ZERO: return 0x01000005;
case QUANTITY_ONE: return 0x01000006;
case QUANTITY_TWO: return 0x01000007;
case QUANTITY_FEW: return 0x01000008;
case QUANTITY_MANY: return 0x01000009;
default:

return ID_OTHER;

}
}
static final String stringForQuantity(int quantity) {
switch (quantity) {
case QUANTITY_ZERO:
return "zero";
case QUANTITY_ONE:
return "one";
case QUANTITY_TWO:
return "two";
case QUANTITY_FEW:
return "few";
case QUANTITY_MANY:
return "many";
default:
return "other";
}
}
static final PluralRules ruleForLocale(Locale locale) {
String lang = locale.getLanguage();
if ("cs".equals(lang)) {
if (cs == null) cs = new cs();
return cs;
}
else {
if (en == null) en = new en();
return en;
}
}
private static PluralRules cs;
private static class cs extends PluralRules {
int quantityForNumber(int n) {
if (n == 1) {

return QUANTITY_ONE;
}
else if (n >= 2 && n < = 4) {
return QUANTITY_FEW;
}
else {
return QUANTITY_OTHER;
}
}
}
private static PluralRules en;
private static class en extends PluralRules {
int quantityForNumber(int n) {
if (n == 1) {
return QUANTITY_ONE;
}
else {
return QUANTITY_OTHER;
}
}
}
}

RESOURCES.JAVA
public CharSequence getQuantityText(int id, int quantity) throws NotFoundException {
PluralRules rule = getPluralRule();
CharSequence res = mAssets.getResourceBagText(id, rule.attrForNumber(quantity));
if (res != null) {
return res;
}
res = mAssets.getResourceBagText(id, PluralRules.ID_OTHER);
if (res != null) {
return res;
}
throw new NotFoundException("Plural resource ID #0x" + Integer.toHexString(id)
+ " quantity=" + quantity
+ " item=" + PluralRules.stringForQuantity(rule.quantityForNumber(quantity)));
}
private PluralRules getPluralRule() {
synchronized (mSync) {

if (mPluralRule == null) {
mPluralRule = PluralRules.ruleForLocale(mConfiguration.locale);
}
return mPluralRule;
}
}

En la mayoria de los idiomas normalmente hay dos posibles valores, one y other, pero
para el Checo, los valores son one para 1, few del 2 al 4 y other para el resto.

PROGRAMACIN ANDROID:
RECURSOS TRABAJAR
CON RECURSOS XML
ARBITRARIOS
2014/09/04/ B Y ALEJANDRO ALCALDE , 6 COMMENTS ,
IN ANDROID, OPENSOURCE

Adems de los recursos estructurados que hemos ido viendo, Android permite usar archivos
XML arbitrarios como recursos. Esto proporciona una forma rpida de referenciar los
archivos basandose en su Id de recurso as como permitirnos localizar estos archivos de una
manera sencilla. Como ltima ventaja, nos permite compilar y almacenar estos archivos en
el dispositivo eficientemente.
Los ficheros XML que se lean de esta manera, tiene que almacenarse bajo el directorio
./res/xml. A continuacin vamos a ver un ejemplo:

<rootelem1>
<subelem1>
Hello World from an xml sub element
</subelem1>
</rootelem1>

Como hace con cualquier otro fichero XML de recursos, el AAPT compila este fichero
antes de colocarlo en el paquete de la aplicacin. Ahora necesitamos usar una instancia
de XmlPullParser para poder parsear el archivo. Para obtener la instancia
de XmlPullParser usando el siguiente cdigo desde cualquier contexto (incluyendo una
activity):
Resources res = activity.getResources();
XmlResourceParser xpp = res.getXml(R.xml.test);

El XmlResourceParser devuelto es una instancia de XmlPullParser, y tambin implementa


java.util.AttributeSet. En el siguiente fragmento de cdigo se muestra como leer el fichero:
private String getEventsFromAnXMLFile(Context activity)
throws XmlPullParserException, IOException
{
StringBuffer sb = new StringBuffer();
Resources res = activity.getResources();
XmlResourceParser xpp = res.getXml(R.xml.test);
xpp.next();
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT)
{
if(eventType == XmlPullParser.START_DOCUMENT)
{
sb.append("******Start document");
}
else if(eventType == XmlPullParser.START_TAG)
{
sb.append("nStart tag "+xpp.getName());
}
else if(eventType == XmlPullParser.END_TAG)
{
sb.append("nEnd tag "+xpp.getName());
}
else if(eventType == XmlPullParser.TEXT)
{
sb.append("nText "+xpp.getText());
}
eventType = xpp.next();
}//eof-while
sb.append("n******End document");
return sb.toString();

}//eof-function

Lo que hacemos en el cdigo de arriba es obtener el XmlPullParser, usarlo para navegar a


travs de los elementos del archivo y usar mtodos adicionales de XmlPullParser para
acceder a detalles de los elementos XML. Para ejecutar este cdigo, se debe crear un
archivo XML como el mostrado anteriormente y llamar al
mtodo getEventsFromAnXMLFile desde cualquier men o botn. Devolver un string, el
cual se podr usar para mostrarlo por el Log usando el mtodo de debug Log.d

PROGRAMACIN ANDROID:
RECURSOS TRABAJAR
CON RECURSOS RAW
2014/09/04/ B Y ALEJANDRO ALCALDE , 2 COMMENTS ,
IN ANDROID, OPENSOURCE

Los recursos Raw se colocan bajo el directorio ./res/raw. Son recursos raw archivos como
ficheros de audio, vdeo o archivos de texto que requieran localizacin o ser referenciados
mediante IDs de recursos.
A diferencia de los archivos XML, colocados en ./res/xml, estos archivos no se compilan,
se mueven al paquete de la aplicacin tal y como son. Sin embargo, a cada fichero se le
asignar un identificador en la clase R.java. Si colocamos un archivo de texto
en ./res/raw/test.txt, podremos leerlo usando el cdigo de abajo:

private String getStringFromRawFile(Context activity)


throws IOException
{
Resources r = activity.getResources();
InputStream is = r.openRawResource(R.raw.test);
String myText = convertStreamToString(is);
is.close();
return myText;

}
private String convertStreamToString(InputStream is)
throws IOException
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int i = is.read();
while (i != -1)
{
baos.write(i);
i = is.read();
}
return baos.toString();
}

Los nombres de ficheros con el mismo nombre base generan un error en el plugin ADT de
eclipse.

PROGRAMACIN ANDROID:
RECURSOS TRABAJAR
CON RECURSOS ASSETS
2014/09/04/ B Y ALEJANDRO ALCALDE , 0 COMMENTS ,
IN ANDROID, OPENSOURCE

Android ofrece ms de un directorio en el que guardar ficheros que se incluirn en el


paquete.: /assets. Est en el mismo nivel que el directorio /res, lo que significa que no es
parte de los subdirectorios del mismo. A los archivos colocados en en el
directorio /assets no se les generan IDs en R.java. Somos nosotros los que debemos
especificar la ruta para leerlo. La ruta al fichero es una ruta relativa que comienza
con /assets. Debemos usar la calse AssetManager para acceder a estos ficheros, como se
muestra en el cdigo de abajo:

String getStringFromAssetFile(Context activity)


throws IOException
{
AssetManager am = activity.getAssets();
InputStream is = am.open("test.txt");
String s = convertStreamToString(is);
is.close();
return s;
}

PROGRAMACIN ANDROID:
RECURSOS REPASANDO LA
ESTRUCTURA DEL
DIRECTORIO DE RECURSOS
2014/09/04/ B Y ALEJANDRO ALCALDE , 0 COMMENTS ,
IN ANDROID, OPENSOURCE

En resumen, en el siguiente listado muestra la estructura global del directorio de recursos:


/res/values/string.xml

/colors.xml

/dimens.xml

/attrs.xml

/styles.xml

/drawable/*.png

/*.jpg

/*.gif

/*.9.png

/anim/*.xml

/layout/*.xml

/raw/*.*

/xml/*.xml

/assets/*.*/*.*

Debido a que no se encuentra bajo el directorio /res, solo el directorio /assetspuede


contener una lista arbitrria de directorios. Cualquier otro directorio solo puede contener
ficheros en ese nivel, y no mas subdirectorios

PROGRAMACIN ANDROID:
RECURSOS RECURSOS Y
CAMBIOS DE
CONFIGURACIN
2014/ 09/ 04/ B Y ALEJANDRO ALCALDE , 1 COMMENT ,
IN ANDROID, OPENSOURCE

Los recursos ayudan a la localizacin. Por ejemplo, podemos tener valores para
strings que cambien en funcin del idioma configurado en el terminal. Los recursos
Android generalizan esta idea para cualquier configuracin del dispositivo, el
idioma es tan solo otra configuracin ms. Otro ejemplo de cambios de
configuracin se d cuando el dispositivo cambia de posicin (de vertical a
horizontal o viceversa). El modo vertical se suela llamar portrait y el horizontal
landscape.
Android permite elegir distintas configuraciones de layout basandose en el tipo de
layout. Y ambos tendrn el mismo ID de recurso. Esto se consigue usando
directorios diferentes para cada configuracin. Vamos a verlo con un ejemplo:

/res/layout/main_layout.xml

/res/layout-port/main_layout.xml

/res/layout-land/main_layout.xml

Aunque hay tres archivos layout separados, solo se generar un nico ID para ellos
en R.java. Este ID ser de la forma R.layout.main_layout
Sin embargo, cuando recuperemos el layout correspondiente a este ID,
obtendremos el layout apropiado para la configuracin actual del dispositivo.
En el ejemplo anterior, las extensiones de los directorios -port y -land se
llamanconfiguration qualifiers. Estos clasificadores (qualifiers), se tienen que
separar del directorio original aadiendo un guin (-) a su nombre. Los recursos
para los que especificamos estos clasificadores de configuracin se llaman recursos
alternativos. Los recursos almacenados en el directorio de recursos sin los
clasificadores de configuracin se llaman recursos por defecto.
En la siguiente lista se muestran los clasificadores de configuracin disponibles:

mccAAA: AAA es el cdigo del pais del dispositivo

mncAAA: AAA es el cdigo de red

en-rUS: Idioma y regin

small, normal, large, xlarge: Tamao de pantalla.

long, notlong: Tipo de pantalla

port, land: Posicion vertical o horizontal (portrait y landscape)

car, desk: Tipo de acomplamiento.

night, notnight: Da o noche

ldpi, mdpi, hdpi, xhdpi, nodpi: Densidad de pantalla.

notouch, stylus, finger: Tipo de pantalla

keysexposed, keysoft, keyshidden: Tipo de teclado

nokeys, qwerty, 12key: Cantidad de teclas

navexposed, navhidden: Teclas de navegacin ocultas o al descubierto

nonav, dpad, trackball, wheel: Tipo de navegacin del dispositivo (trackball


es la bolita que se usa como ratn)
v3, v4, v7: Nivel API

Con los clasificadores mostrados arriba, podemos crear directorios de recursos


como los siguientes:
/res/layout-mcc312-mnc222-en-rUs

/res/layout-ldpi

/res/layout-hdpi

/res/layout-car

Para saber nuestra localizacin actual podemos ejecutar una aplicacin que viene
instalada en el emulador android. La encontramos en el men de aplicaciones y se
llama Custom Locale.

Dado un ID de recurso, Android usa un algoritmo para elegir el adecuado. Si deseas


saber ms acerca de este tema puedes visitar la siguiente
direccinhttp://developer.android.com/guide/topics/resources/providingresources.html#AlternativeResources, pero voy a dar unas reglas bsicas.
La primera regla es que los clasificadores mostrados arriba estn en orden de
precedencia. Veamos un ejemplo de como funciona la precedencia:
/res/layout/main_layout.xml

/res/layout-port/main_layout.xml

/res/layout-en/main_layout.xml

En este listado, el archivo main_layout.xml est disponible para dos variaciones


posibles. Una variacin para el idioma y otra para la orientacin. Veamos que
layout se selecciona si tenemos el dispositivo en vertical.

Incluso si el dispositivo est en vertical, Android va a elegir el layout que reside en


la carpeta layout-en, ya que en la lista de clasificadores que vimos anteriormente el
idioma est por delante del modo portrait (Vertical). En la direccin dada
anteriormente se encuentra todo esto explicado ms detalladamente.
Ahora vamos a ver algunas reglas de precedencia con unos ejemplos de recursos
strings. Pero los recursos string son recursos basados en ids individuales, mientras
que los ids de layout se basan en los archivos. Para probar la precedencia con
recursos string, vamos a definir cinco Ids para las siguientes variaciones:default,
en, en_us, port, en_port. Los cinco recursos son los siguientes:

teststring_all: Este ID estar en todas las variaciones del directorio values,


incluyendo el default.

testport_port: Estar en las variaciones default y -port

t1_enport: Estar en las variaciones default, -en y -port

t1_1_en_port: Estar en las variaciones defaul y en -en-port

t2: Solo en default

El siguiente cdigo muestra las variaciones del directorio values:


// values/string.xml
<resources xmlns="http://schemas.android.com/apk/res/android">
<string name="teststring_all">teststring in root</string>
<string name="t1_enport">t1 in root</string>
<string name="t1_1_en_port">t1_1 in root</string>
<string name="t2">t2 in root</string>
<string name="testport_port">testport in root</string>
</resources>
// values-en/string_en.xml
<resources xmlns="http://schemas.android.com/apk/res/android">
<string name="teststring_all">teststring-en</string>
<string name="t1_enport">t1_en</string>
<string name="t1_1_en_port">t1_1_en</string>
</resources>

// values-en-rUS/string_en_us.xml
<resources xmlns="http://schemas.android.com/apk/res/android">
<string name="teststring_all">test-en-us</string>
</resources>
// values-port/string_port.xml
<resources xmlns="http://schemas.android.com/apk/res/android">
<string name="teststring_all">test-en-us-port</string>
<string name="testport_port">testport-port</string>
<string name="t1_enport">t1_port</string>
<string name="t1_1_en_port">t1_1_port</string>
</resources>
// values/string_en_port.xml
<resources xmlns="http://schemas.android.com/apk/res/android">
<string name="teststring_all">test-en-port</string>
<string name="t1_1_en_port">t1_1_en_port</string>
</resources>

El listado siguiente muestra el archivo R.java para estos recursos:


public static final class string {
public static final int t1_1_en_port=0x7f070004;
public static final int t1_enport=0x7f070003;
public static final int t2=0x7f070005;
public static final int testport_port=0x7f070006;
public static final int teststring_all=0x7f070002;
}

Como se aprecia, aunque hemos definido muchos strings, solo se han generado
cinco IDs. A continuacin se describe el comportamiento para cada string, que se
ha probado con en_US y el modo portrait:

teststring_all: Este ID aparece en las cinco variaciones, ya que aparece en


todas. En nuestra configuracin, se escoger del directorio values-en-rUS.
Basandonos en las reglas de precedencia, tener un directorio concreto para un
idioma va por delante de las variaciones en, port y en-port

testport_port: Este ID est en las variaciones default y -port. Ya que no hay


ningn directorio que no se encuentra en ningn directorio que empiece por
-en, la variacin -port tendr precedencia frente a default, por lo tanto se

escoger el valor desde la variacin -port. SI estuviera en en una de las


variaciones -en, sera esta ltima la que se escogera.

t1_enport: Este Id est en tres variaciones, default, -en y -port. Debido a que
se encuentra en -en y -port al mismo tiempo, se escoger el valor de -en

t1_1_en_port: Este ID se encuentra en cuatro variaciones, default, -port, -en


y -en-port. El que toma precedencia frente a todos aqu es -en-port

t2: Este ID se encuentra solo en default, por lo tanto se elige el valor de ah.

El SDK de Android tiene algoritmos ms detallados an, sin embargo, con este
ejemplo se ha mostrado lo esencial. La clave est en darse cuenta de la precedencia
de cada variacin sobre otra. A continuacin, para aquellos que deseen ampliar
informacin dejo unas URLs de referencia:

REFERENCIAS

ndice de recursos Android

Tipos de recursos Android

Distintos mtodos para leer recursos.

Recursos definidos en el nucleo de la plataforma Android

Understanding Android Resource Arrays, Plurals, Configuration qualifiers

Descargar proyecto de ejemplo para testear los recursos

SEGUNDA PARTE:

PROGRAMACIN ANDROID:
STRICTMODE
2 0 1 4 / 0 9 / 11/ B Y A L E J A N D R O AL C A L D E , 3 C O M M E N T S ,
IN ANDROID, OPENSOURCE

Android 2.3 introdujo una funcionalidad de depuracin llamada StrictMode. Segn Google,
usaron esta caracterstica para hacer cientos de mejoras a sus aplicaciones Android. Lo que
hace el StrictMode es informar de las violaciones de polticas relacionadas con los hilos y la
mquina virtual. Si se detecta dicha violacin, obtenemos una alerta que nos lo indica.
Junto a la alerta tendremos tambin una traza de la pila de ejecucin (Stack Trace), donde
podremos comprobar el lugar en el que se produjo la violacin. En ese momento, podemos
forzar el cierre de la aplicacin o simplemente escribirla en el log y dejar que la aplicacin
continue su ejecucin.
Actualemente hay dos tipos de polticas disponibles para usar con StrictMode. La primera
de ellas es referente a los hilos y est destinada principalmente a ejecutarse en el hilo
principal, tambin conocido como el hilo de UI (User Interface).

No es una buena prctica hacer lecturas/escrituras a disco desde el hilo principal, como
tampoco lo es realizar accesos a red. Google ha aadido al cdigo del disco y de
red hooks o ganchos, que son algoritmos abstractos que invocan a mtodos abstractos. Por
lo tanto, si activamos StrictMode para uno de nuestros hilos, y ese hilo realiza cualquiera de
las dos acciones mencionadas anteriormente, seremos informados. Podemos elegir sobre
qu aspectos de la poltica de hilos queremos ser informados, as como el mtodo por el
cual se nos informar. Normalmente las que usaremos sern accesos a disco y red. En
cuanto al mtodo por el que seremos informados, pordemos elegir: Escribirlo en el LogCat,
mostrar un dilogo, hacer un destello en la pantalla, escribir en el archivo log de DropBox o
forzar el cierre de la aplicacin. Normalmente se usa el LogCat o forzar el cierre. A
continuacin vemos un ejemplo de como configurar StrictMode para polticas de hilos:
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyLog()
.build());

Podemos ver que la calse Builder hace que configurar StrictMode sea bastante fcil. Todos
los mtos que definen las polticas devuelven una referencia al objeto Builder. El
ltimo, build(), devuelve un objeto ThreadPolicy, que es el argumento
quesetThreadPolicy() espera recibir. setThreadPolicy() es un mtodo esttico, por ello no es
necesario instanciar un objeto de tipo StrictMode. En el ejemplo anterior, se ha configurado
la poltica para que avise de lecturas-escrituras a disco, acceso a red y vamos a ser
informados a travs del logCat. Podemos usar detectAll() para ahorrarnos escribir los otros
mtodos de deteccin. Tambin podemos usar el mtodo penaltyDeath() para forzar el
cierre de la aplicacin una vez escrita la alerta StrictMode al LogCat.
Debido a que con el cdigo de arriba hemos activado el StrictMode, no necesitamos seguir
activndolo. Por lo tanto, podemos habilitar StrictMode al principio del
mtodo onCreate() de nuestra actividad principal, ya que se ejecuta en el hilo principal,
avisndonos de todo lo que pase en ese hilo.
StrictMode tambin dispone de VmPolicy, que comprueba prdidas de memoria si un
objeto SQLite finaliza antes de que haya sido cerrado, o si cualquier objeto que pueda ser
cerrado ha finalizado antes de ser cerrado. Las VmPolicy se crean de una forma similar
como se muestra a continuacin. Sin embargo, hay una diferencia entre stas y
ThreadPolicy, que no pueden usar alertas a travs de dilogos.

StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.penaltyLog()
.penaltyDeath()
.build());

Ya que la configuracin ocurre en un hilo, quizs nos sorprenda que el cdigo se est
ejecutando en el hilo principal, pero la traza de la pila (Stack Trace) est ah para ayudarnos
a averiguar en qu punto se produjo la violacin. Una vez detectada la violacin, podemos
resolverla moviendo el cdigo que la produce a un hilo en segundo plano propio. O por otra
parte podemos decidir que el cdigo est bien en esa parte y no moverlo.
Algo importante que debemos hacer a la hora de distribuir nuestra aplicacin es apagar el
StrictMode. Hay varias formas de conseguir esto. La ms sencilla es eliminar las llamadas a
los mtodos, pero hacer esto dificulta seguir desarrollando la aplicacin. Podemos declarar
un booleano al nivel de la aplicacin y comprobar su valor antes de llamar a StrictMode.
De este modo, al enviar la aplicacin a produccin, simplemente daramos a esta variable el
valor false y de se llamar nunca a StrictMode.
Un mtodo ms elegante para resolver este problema, es usar el atributodebuggable en
nuestro AndroidManifest. Este atributo se coloca en el tag de la forma android:debuggable.
Una vez activado este atributo, puede fijarse como verdadero o falso dependiendo de si
queremos depurar la aplicacin o no. Podemos comprobar el estado de este atributo como
se muestra ms abajo. De modo que cuando est activado, tendremos StrictMode activo, y
cuando no lo est, no.
//Devuelve si la aplicacin est en modo debug o no
ApplicationInfo appInfo = context.getApplicationInfo();
int appFlags = appInfo.flags;
if ((appFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0){
//Aqu configuraramos el StrictMode
}

Si usamos eclipse como IDE, el plugin ADT configura el atributo debuggable


automticamente. Es decir, cuando usamos el emulador o un dispositivo real, eclipse fija el
atributo debuggable a verdadero, lo que nos permite usar StrictMode y depurar nuestra
aplicacin. Cuando exportamos la aplicacin para crear una versin de produccin, eclipse
lo fija a falso. Hay que tener cuidado con esto, ya que si nosotros aadimos el atributo a
mano, eclipse no lo cambiar.
StrictMode no funciona en versiones Android anteriores a la 2.3. Si queremos usarlo con
versiones anteriores, podemos usar tcnicas espejo para llamar indirectamente a los
mtodos de StrictMode:

try{
Class strictMode = Class.forName("android.os.StrictMode");
Method enableDefaults = strictMode.getMethod("enableDefaults");
enableDefaults.invoke(null);
}catch(Exception e){
//Este dipositivo no soporta StrictMode
Log.v("StrictMode", "no soportado, ignorando...");
}

El cdigo de arriba determina si la clase StrictMode existe, y si existe, llama


aenableDefaults(). En caso de no existir la aplicacin no finalizar, puesto que hemos
tratado la excepcin y el bloque catch se invocar con una excepcin del
tipoClassNotFoundException.
Si el StrictMode no est disponible para nuestra aplicacin, se lanzar un error del
tipo VerifyError al intentar acceder a l. Si envolvemos a StrictMode en una clase y
capturamos el error, lo prodremos ignorar si StrictMode no est habilitado. A continuacin
vamos a ver un ejemplo creando esta clase.
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.StrictMode;
public class StrictModeWrapper{
public static void init(Context context){
ApplicationInfo appInfo = context.getApplicationInfo();
int appFlags = appInfo.flags;
if ((appFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0){
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
}
}

Como se puede apreciar, simplemente hemos metido todos los ejemplos que vimos
anteriormente en una clase. Ahora para configurar StrictMode tenemos que hacer lo
siguiete:
try{
StrictModeWrapper.init(this);
}catch(Throwable throwable){
Log.v("StrictMode", "no soportado, ignorando...");
}

this es el contexto local del objeto desde el que llamemos al mtodo init, por ejemplo
podra ser desde el mtodo onCreate() de nuestra actividad principal.

EJEMPLO DE USO
MEDIANTE EL PENALTYLOG():

MEDIANTE PENALTYDIALOG():

PROGRAMACIN ANDROID:
PROVEEDORES DE
CONTENIDO
INTRODUCCIN
2 0 1 4 / 0 9 / 11/ B Y A L E J A N D R O AL C A L D E , 0 C O M M E N T S ,
IN ANDROID, OPENSOURCE

Android llama Proveedor de contenido a la abstraccin de datos en Servcios. Digamos que


un proveedor de contenido (CV a partir de ahora [de Content Provider]) es como un
envoltorio para los datos. Una base de datos SQLite en un dispositivo Android es un
ejemplo de fuente de datos (data source) que se encapsula en un CV.
Para recuperar datos de un cv o guardarlos en l, se necesitan usar un conjunto de Uris
REST. Por ejemplo, si quisiramos recuperar un conjunto de notas desde un cv que es una
encapsulacin de una base de datos de Notas, necesitaramos usar una URI parecida a esta:

content://com.android.note.NoteProvider/notes

REST (REpresentational State Transfer). Es simplemente un concepto que, como usuarios


de la web, casi todos estamos famirializados con l. Cuando introducimos una URL en el
navegador y el servidor web responde con HTML, esencialmente estamos realizando una
consulta basada en REST sobre el servidor web. De manera similar, cuando actualizamos
algn contenido de un formulario web, estamos haciendo una actualizacin (update) basada
en REST en el servidor web y cambiando su estado.

Para recuperar una nota especfica de la base de datos de notas, (la nota 25 por ejemplo), se
necistar una URI como la siguiente:
content://com.android.note.NoteProvider/notes/25

En futuras entradas veremos como estas Uris se traducen para usarse con los mecanismos
de acceso a la base de datos. Cualquier aplicacin del dispositivo puede usar estas URIs
para acceder y manipular datos. Como consecuencia, los CV desempean un papel
importante compartiendo datos entre aplicaciones.
Estrictamente hablando, la responsabilidad de los CV abarcan ms mecanismos de
encapsulacin que el acceso a datos. Se necesitar un mecanismo de acceso a datos como
por ejemplo SQLite o acceso a red para recibir datos.Debido a esto, La abstraccin de los
CV se requiere solamente si queremos compartir datos externamente o entre aplicaciones.
Para el acceso interno a los datos, nuestra aplicacin puede usar cualquier mecanismo de
almacenamiento/acceso de datos de entre los siguientes:

Preferencias: Es un conjunto de pares clave/valor que se usa para almacenar de


manera persistente las preferencias de la aplicacin.

Archivos: Archivos internos de la aplicacin que podemos almacenar en un medio


de almacenamiento extraible.

SQLite: Bases de datos SQLite, Cada una es privada al paquete que la crea.
Red: Un mecanismo que permite recibir o almacenar datos externamente a travs de
internet.

A pesar de los numerosos mecanismos de acceso a datos permitidos en Android, en


este segundo tomo del Curso de programacin Android, me voy a centrar en SQlite y la
abstraccin del CV, ya que los CV componen la base del intercambio de datos, el cual es
mucho ms comn en Android que en cualquier otro Framework.

PROGRAMACIN ANDROID:
ARQUITECTURA DE LOS
PROVEEDORES DE
CONTENIDOS
2 0 1 4 / 0 9 / 11/ B Y A L E J A N D R O AL C A L D E , 4 C O M M E N T S ,
IN ANDROID, OPENSOURCE

Vamos a examinar algunos de los elementos que componen los proveedores de


contenidos(CV) y cmo stos se relacionan con otras abstracciones de acceso a datos.
En conujunto, los CV tienen un enfoque paralelo a las siguientes abstracciones:

Sitios webs

REST

Servicios web

Procedimientos Almacenados

Cada CV de un dispositivo se registra a s mismo de manera similar a como lo hace un sitio


web con cadenas de texto (similar a los nombres de domnio, pero para los CV se
llama authority). Esta cadena asenta las bases del conjunto de URIs que este CV puede
ofrecer. No es diferente a como un sitio web con un dominio ofrece un conjunto de URls
que muestran sus documentos o contenido en general.

El registro de la authority se hace en el androidManifest. A continuacin se muestran dos


ejemplos de como se deben registrar proveedores (en este caso de la aplicacin FavSItes):
<provider android:name=".SitesProvider"
android:authorities="com.elbauldelprogramador.provider.FavSites" />

Un authoroty es como un nombre de dominio para ese CV. Con el authority anterior, las
urls de nuestro proveedor comenzarn con ese prefijo:
content://com.elbauldelprogramador.provider.FavSites

Como se ve, los CV, como los sitios web, tienen un nombre de dominio base que acta
como URL inicial.
Los CV tambin proporcionan URLs del tipo REST para recuperar o manipular datos. Para
el registro que acabamos de ver, el URI para identificar un directorio o una coleccin de
datos en la base de datos de FavSites ser:
content://com.elbauldelprogramador.provider.FavSites/sites

Y para identificar un dato especfico:


content://com.elbauldelprogramador.provider.FavSites/sites/#

Donde # es el id del dato especfico, en el caso de la mi aplicacin, un lugar en el mapa. A


continuacin algunos ejemplos de URIs que se aceptan:
content://media/internal/images

content://media/external/images

content://contacts/people

content://contacts/people/23

Ntese que estos CV (content://media y content://contacts) no tienen una estructura


completa como los vistos ms arriba. Se debe a que no son CV de terceros, son propios de
Android y l es quin los controla.

PROGRAMACIN ANDROID:
PROVEEDORES DE
CONTENIDOS LEER DATOS
MEDIANTE URIS
2 0 1 4 / 0 9 / 11/ B Y A L E J A N D R O AL C A L D E , 1 C O M M E N T ,
IN ANDROID, OPENSOURCE

Ya que las URIs definidas por un Proveedor de contenidos (CV) son nicas para ese
proveedor, es muy importante que estas URIs estn bien documentadas. Los proveedores
que Android proporciona hacen esto definiendo constantes que representan las cadenas de
la URI.
Consideremos estas tres URIs definidas en el SDK de Android:
MediaStore.Images.Media.INTERNAL_CONTENT_URI

MediaStore.Images.Media.EXTERNAL_CONTENT_URI

Contacts.People.CONTENT_URI

Sus URIs equivalentes sern como las siguientes:


content://media/internal/images

content://media/external/images

content://contacts/people

El proveedor MediaStore define dos URIs y Contacts uno. Si te das cuenta, las constantes
estn definidas usando esquema jerrquico. Por ejemplo, la URI de los contactos se seala
como Contacts.People.CONTENT_URI. Esto se debe a que las bases de datos de contactos
pueden tener muchas tablas para representar la entidad de un contacto. People es una de las
tablas o colecciones. Cada entidad primaria de una base de datos puede llevar su propia
URI de contenido.
En Contacts.Pople.CONTENT_URI, Contacts es un paquete de java y People es una clase
dentro de ese paquete. Hay que saber que Contacts y Contacts.People estn obsoletos desde
Android 2.0 y que ahora poseen nuevas URIs equivalentes que veremos ms adelante. Sin
embargo, para propositos educativos podemos seguir usandolos aunque estn obsoletos.
Dadas estas URIs, el cdigo para recuperar una nica fila del proveedor de contactos sera:
Uri uriBase = Contacts.People.CONTENT_URI;
Uri uriPersona = Uri.withAppendedPath(Contacts.People.CONTENT_URI, "21");
//Consultamos el registro
//managedQuery es un mtodo de la clase Activity
Cursor c = managedQuery(uriPersona, null, null, null);

En este ejemplo, cogemos la uri base Contacts.People.CONTENT_URI, le aadimos un id


de contacto y llamamos al mtodo managedQuery.
En el mtodo managedQuery podemos especificar el orden de los resultados, las columnas
a seleccionar y una condicin (en el ejemplo estn a null.)
Vamos a ver ahora como crear un cursor que devuelva una lista de columnas de la tabla
People del proveedor contatcs.
// Array que especifica la proyeccin (columnas a seleccionar)
String[] projection = new String[] {
People._ID,
People.NAME,
People.NUMBER,
};

//obtenemos la URI base


Uri mContactsUri = Contacts.People.CONTENT_URI;
Cursor managedCursor = managedQuery(mContactsUri,
projection, // Qu columnas devolver
null,

// Clusula where

Contacts.People.NAME + " ASC"); //Clausula order by

PROGRAMACIN ANDROID:
USANDO CURSORES
2 0 1 4 / 0 9 / 11/ B Y A L E J A N D R O AL C A L D E , 0 C O M M E N T S ,
IN ANDROID, OPENSOURCE

Cosas a saber sobre un cursor Android:

Un cursor es una coleccin de filas.


Es necesario usar moveToFirst() antes de leer cualquier dato del cursor ya que ste
comienza posicionado antes de la primera fila.

Es necesario conocer los nombres de las columnas.

Tambin es necesario conocer los tipos de las columnas.

Todos los mtodos de acceso a los campos se basan en nmeros de columnas, por lo
que se necesita convertir el nombre de la columna a un nmero.

El cursor es un cursor aleatorio, es decir, podemos movernos por l hacia delante,


hacia atrs o saltar de una posicin a otra.

Dado que el cursor es de tipo aleatorio, podemos solicitar el nmero de registros


que contiene.

El cursor tiene unos mtodos que nos permiten navegar por l. A continuacin vemos como
comprobar si el cursor est vaco y cmo navegar por l fila a fila en caso de no estarlo.

if (cur.moveToFirst() == false){
//el cursor est vaco
return;
}
//El cursor ahora apunta a la primera fila
// Accedemos a las columnas
int nameColumnIndex = cur.getColumnIndex(People.NAME);
String name = cur.getString(nameColumnIndex);

//Veamos ahora como iterar sobre un cursor


while(cur.moveToNext()){
//El curosr se ha movido correctamente
//Accedemos a los campos
}

En el ejemplo de arriba se asume que el cursor est posicionado antes de la primera fila.
Para posicionarlo en la primera fila usamos moteToFirst(). Este mtodo devuelve false si el
cursor est vaco. Despus usamos moveToNext()repetitivamente para avanzar en el cursor.
Para ayudarnos a saber donde se encuentra el cursor, Android proporciona los siguientes
mtodos:
isBeforeFirst()

isAfterLast()

isClosed()

Haciendo uso de estos mtodos podemos recorrer el cursor con un for en lugar de con
un while:
// Obtenemos los ndices de las columnas
int nameColumn = cur.getColumnIndex(People.NAME);
int phoneColumn = cur.getColumnIndex(People.NUMBER);
//Recorremos el cursor
for(cur.moveToFirst(); !cur.isAfterLast(); cur.moveToNext()){
String name = cur.getString(nameColumn);
String phoneNumber = cur.getString(phoneColumn);

Por ltimo, para obtener el nmero de registros que hay en el cursor, podemos usar el
mtodo getCount().

PROGRAMACIN ANDROID:
USANDO LA CLUSULA
WHERE
2 0 1 4 / 0 9 / 11/ B Y A L E J A N D R O AL C A L D E , 0 C O M M E N T S ,
IN ANDROID, OPENSOURCE

Los proveedores de conteido ofrecen dos formas de pasar una clusula where:

A travs de la URI

Combinando una cadena y un conjunto de argumentos string-array reemplazables.

En esta entrada vamos a ver ambas.

CLUSULA WHERE MEDIANTE LA URI


Imaginemos que queremos recuperar un lugar (De la aplicacin FavSites) cuyo id sea 23.
Usaramos el siguiente cdigo:

Activity activity;
//... Inicializamos la actividad...
String siteUri = "content://com.elbauldelprogramador.provider.FavSites/sites/23";
Cursor managedCursor = activity.managedQuery( siteUri,
projection, //Columnas a devolver

null,

//Clusula WHERE

null);

//Clusula ORDER BY

En este ejemplo se ha dejado el argumento que hace referencia a la clusula where a null ya
que hemos especificado el ID del registro que queremos en la URI. En este caso el ID est
embebido en la URI. Se usa la URI como vehculo para pasar la clusula where. Esto se
hace evidente cuando nos fijamos cmo se implementa el proveedor para los Sites de la
aplicacin, que corresponde al mtodo query. A continuacin un fragmento de cdigo del
mtodo query:
//Devuelve un id de sitio
//content://.../sites/23
int siteId = uri.getPathSegments().get(1);
queryBuilder.setTables(favSitesTableMetaData.TABLE_NAME);
queryBuilder.appendWhere(favSitesTableMEtaData._ID + "=" + siteId);

Como vemos la id del sitio se extrae de la URI. La Uri se divide en segmentos (path) del a
forma content:///seg1/seg2/seg3, en nuestro ejemplo el primer segmento es el id 23.
Las clases Uri y UriMatcher se usan para identificar las URIs y extraer parmetros de ellas,
ms adelante las veremos. SQLiteQueryBuilder es una clase asistente en
android.database.sqlite que permite construir consultas SQL que se ejecutarn por
SQLiteDatabase en una instancia de una base de datos SQLite.

CLUSULAS WHERE EXPLCITAS


Vamos a ver una vez ms la estructura del mtodo managedQuery de la clase Activity:
public final Cursor managedQuery(Uri uri,
String[] projection,
String selection,
String[] selectionArgs,
String sortOrder)

El parmetro selection, es el que acta como clusula Where (Representa un filtro en el que
elegimos qu filas queremos que se nos devuelvan). Si en este argumento pasamos null se
nos devolvern todas las filas para la URI dada. En este parmetro podemos incluir ?, que
sern reemplazados por los valores del parmetro selectionArgs en el orden que vayan
apareciendo.

Los siguientes cdigos que se muestran son equivalentes:


//Mtodo Uri
managedQuery("content://com.elbauldelprogramador.provider.FavSites/sites/23"
,null
,null
,null
,null);
//Mtodo explcito
managedQuery("content://com.elbauldelprogramador.provider.FavSites/sites"
,null
,"_ID=?"
,new String[] {23}
,null);

Te preguntars qu mtodo usar en segn que situacin. Por convencin se suele usar el
mtodo mediate URI cuando sea posible aplicarlo, y el explcito en casos especiales (Como
en el ejemplo de arriba.)

PROGRAMACIN ANDROID:
INSERTANDO REGISTROS
2 0 1 4 / 0 9 / 11/ B Y A L E J A N D R O AL C A L D E , 2 C O M M E N T S ,
IN ANDROID, OPENSOURCE

En la entrada anterior hablamos de cmo obtener regstrosde los proveedores de contenidos


(CV) usando URIs. Ahora vamos a ver como insertar registros.
Anroid utiliza una clase llamada android.content.ContentValuespara retener los valores de
un solo registro, que ser el que se insertar. Los ContentValues son un dicionario de pares
clave/valor, al igual que los nombres de columnas y valores de las bases de datos. La forma
de insertar un registro es rellenando el ContentValues primero y despues decir
a android.content.ContentResolver que lo inserte usando una URI.
Necesitamos usar ContentResolver porque a este nivel de abstraccin, no le estamos
pidiendo a una base de datos que inserte un registro, estamos pidiendo insertar un registro
en un CV identificado por una URI. El contentResolver es el responsable de resolver la
referencia a la URI al proveedor correcto.
Abajo se muestra un ejemplo de cmo rellenar una fila con un ContentValues y prepararlo
para un insert:

ContentValues cv = new ContentValues();


cv.put("NombreColumna1", "valor1");
cv.put("NombreColumna2", "valor2");

//Ahora el objeto cv est preparado para insertarse en la BD

Podemos obtener una referencia al ContentResolver mediante un mtodo de la clase


Activity:
ContentResolver cr = miActivity.getContentResolver();

Todo lo que necesitamos en este punto es una URI para decirle al ContentResolver que
inserte la fila. En entradas anteriores vimos que nuestra URI era:
content://com.elbauldelprogramador.provider.FavSites/sites

Con esta URI y nuestro ContentValues podemos ahora hacer una llamada para insertar
nuestra fila:
Uri uri = cr.insert(FavSitesProviderMetaData.CONTENT_URI, cv);

La lnea anterior devuelve una uri apuntando al nuevo registro aadido, la URI devuelta se
parecer a esto:
FavSitesProviderMetaData.CONTENT_URI/Nuevo_ID

Con esta Uri, podemos por ejemplo mostrar en el log del sistema qu registro hemos
aadido:
Log.d("Nuevo registro aadido","Uri:" + insertedUri);

AADIR ARCHIVOS A UN CONTENT


PROVIDER
Puede que en ocasiones necesitemos aadir un archivo a una base de datos. Lo normal es
guardar el archivo en disco y actualizar el registro en la base de datos para que apunte al
archivo correspondiente. Android usa este protocolo y lo automatiza mediante la definicin
de un procedimiento especfico para guardar y recibir estos archivos. Usa un convenio en el
cual una referencia a un nombre de archivo se guarda en un registro con un nombre de
columna reservado llamado_data.
Cuando se inserta un registro en esta tabla, se devuelve la uri. Una vez guardamos el
registro mediante este mecanismo hay que realizar un seguimiento al archivo en esa
direccin. Para hacer esto, Android permite al ContentResolver coger la URI del registro de
la base de datos y devolver un writable output stream. Android asigna un archivo interno y
almacena la referencia a ese nombre de archivo en el campo_data.

Para poder realizar estas operaciones necesitamos crear una columna adicional llamada
_data, hacer una llamada al mtodo insert para obtener una URI. EL siguiente cdigo
demuestra como se hara:
ContentValues cv = new ContentValues();
cv.put("NombreColumna1", "valor1");
cv.put("NombreColumna2", "valor2");
//Usamos el content resolver para insertar un registro
ContentResolver cr = miActivity.getContentResolver();
Uri nuevaUri = cr.insert(FavSitesProviderMetaData.CONTENT_URI, cv);

Una vez tenemos la uri del registro, pedimos a ContentResolver que obtenga una referencia
al output stream del archivo:
//Usamos el ContentResolver para obtener el output stream directamente
//El content resolver oculta el acceso al campo _data donde se almacena realmente la referencia
al archivo
OutputStream outStream = miActivity.getContentResolver().openOutputStream(nuevaUri);
algunArchivoDeImagen.compress(Bitmap.CompressFormat.JPEG, 50, outStream);
outStream.close;

El cdigo usa el flujo de salida para escribir.

PROGRAMACIN ANDROID:
ACTUALIZAR Y BORRAR
REGISTROS
2 0 1 4 / 0 9 / 11/ B Y A L E J A N D R O AL C A L D E , 1 C O M M E N T ,
IN ANDROID, OPENSOURCE

Vimos cmo insertar registros y consultarlos, bien, pues actualizar y borrar registros es
bastante sencillo. Realizar unupdate (Actualizar registros) es muy similar a hacer una
insercin, en la cual los valores de la columna a modificar se pasan mediante un
objeto ContentResolver. Abajo se muestra una sentencia para realizar dicho update:

int numeroDeLineasActualizadas =
activity.getContentResolver().update(
Uri uri,
ContentValues values,
String whereClause,
String[] selectionArgs )

El argumento whereClause, restringe la actualizacin a los registros de la BD que cumplan


esa condicin.
La sentencia para borrar registros es:
int numeroDeLineasBorradas =
activity.getContentResolver().delete(

Uri uri,
String whereClause,
String[] selectionArgs )

Logicamente, el mtodo delete no necesita un argumento que contenga elContentValues.


Casi todas las llamadas que se hacen desde managedQuery y ContentResolver se dirigen a
las clase provider. Saber cmo un proveedor implementa cada uno de estos mtodos no d
suficientes pistas de cmo se usan dichos mtodos. En entradas posteriores, veremos cmo
implementar desde cero un content provider.

PROGRAMACIN ANDROID:
IMPLEMENTANDO UN
CONTENT PROVIDER (PARTE
1)
2 0 1 4 / 0 9 / 11/ B Y A L E J A N D R O AL C A L D E , 0 C O M M E N T S ,
IN ANDROID, OPENSOURCE

Esta es la primera entrada de un total de 4 en la que se irn describiendo los pasos a dar
para crear nuestro propio proveedor de contenidos.
Ya hemos visto cmo interactuar con un Content provider, pero no hemos visto an cmo
escribir nuestro propio Content Provider. Para hacerlo, es necesario extender
deandroid.content.ContentProvider e implementar los siguientes mtodos:

query

insert

update

delete

getType

Tambin necesitamos configurar unas cuantas cosas antes de implementarlo. Los paso a
seguir para implementarlo son los siguientes:
1.

Plantear nuestra base de datos, URIs, nombres de columnas y crear clases de


metadatos que definirn constantes para todos estos elementos de metadatos.

2.

Extender la clase abstracta ContentProvider.

3.

Implementar los mtodos query, insert, update, delete y getType.

4.

Registrar el proveedor en el Android Manifest.

PLANTEAMIENTO DE LA BASE DE DATOS


El siguiente planteamiento de la base de datos pertenece a la aplicacin FavSites, proyecto
que realic tiempo atrs y registrado bajo licencia GPLv3. Esta base de datos contiene una
nica tabla con las columnas nombre, descripcin, latitud, longitud y foto. Estos nombres
de columnas pasarn a formar parte de los metadatos en nuestra clase
FavSitesProviderMetadata.
//FavSites for Android
//

Copyright (C) 2011 Alejandro Alcalde Barros

//
//This file is part of FavSites.
//
//

FavSites is free software: you can redistribute it and/or modify

//

it under the terms of the GNU General Public License as published by

//

the Free Software Foundation, either version 3 of the License, or

//

(at your option) any later version.

//
// FavSites is distributed in the hope that it will be useful,
//

but WITHOUT ANY WARRANTY; without even the implied warranty of

//

MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

//

GNU General Public License for more details.

//
//

You should have received a copy of the GNU General Public License

//

along with FavSites. If not, see <http: //www.gnu.org/licenses/>.

package com.elbauldelprogramador.favsites.provider;
import android.net.Uri;
import android.provider.BaseColumns;

/**
* @author Alejandro Alcalde
* Definiciones necesarias para almacenar la informacin
*/
public class FavSitesProviderMetaData {
public static final String AUTHORITY = "com.elbauldelprogramador.provider.FavSites";
public static final String DATABASE_NAME = "favsites.db";
public static final int DATABASE_VERSION = 1;
public static final String FAVSITES_TABLE_NAME = "favSites";
private FavSitesProviderMetaData() {}
/**
* Clase interna para describit la tabla favSites
*/
public static final class favSitesTableMEtaData implements BaseColumns{
private favSitesTableMEtaData() {}
public static final String TABLE_NAME = "favSites";
/**
* DEfinicin del Content URI y MIMEs
*/
public static final Uri CONTENT_URI
= Uri.parse("content://" + AUTHORITY + "/sites");
public static final String CONTENT_TYPE
= "vnd.android.cursor.dir/vnd.favsites.site";
public static final String CONTENT_ITEM_TYPE
= "vnd.android.cursor.item/vnd.favsites.site";
/**
* Orden por defecto de la tabla
*/
public static final String DEFAULT_SORT_ORDER = "_ID DESC";
/**
* Orden personalizado
*/

public static final String SORT_BY_NAME_DESC = "name DESC";


public static final String SORT_BY_NAME_ASC = "name ASC";
//Columnas propias
/**
* Nombre del sitio
* Type: TEXT
*/
public static final String NAME = "name";
/**
* Descripcion del sitio
* Type: TEXT
*/
public static final String DESCRIPCION = "descripcion";
/**
* LATIDTUD
* Type: INTEGER (long)
*/
public static final String LATITUD = "latitud";
/**
* LONGITUD
* Type: INTEGER (long)
*/
public static final String LONGITUD = "longitud";
/**
* Fotografia del sitio
* Type: TEXT
*/
public static final String FOTO = "foto";
}
}

Esta clase FavSitesProviderMetaData comienza definiendo que su authority


sercom.elbauldelprogramador.provider.FavSites. Usaremos esta cadena de texto para
registrar el proveedor en el Android Manifest. Esta cadena forma la parte principal de la
URI de este proveedor.

Despues procedemos a definir una tabla (favSites) como una clase interna
deFavSitesProviderMetaData. Posteriormente, la clase favSitesTableMEtaData define una
URI para identificar las colecciones de stios. Dada la authority del prrafo anterior, la URI
para una coleccin de stios ser como la siguiente:
content://com.elbauldelprogramador.provider.FavSites/sites

La constante que se refiere a esta URI es:


FavSitesProviderMetaData.favSitesTableMEtaData.CONTENT_URI

La clase favSitesTableMEtaData define los MIME types para una coleccin de sitios y de
un nico stio. La implementacin del proveedor usar estas constantes para devolver los
MIME types para las URIs entrantes.
favSitesTableMEtaData tambin declara un conjunto de columnas: nombre, descripcin,
latitud, longitud y foto.
Es aconsejable describir los tipos de datos que poseen las columnas mediante comentarios.
Adems, la clase favSitesTableMEtaData hereda de la clase BaseColumns, la cual
proporciona el campo estandar _ID, que representa el identificador de la fila. Con todas
estas definiciones de metadatos, estamos listos para continuar con la implementacin de
nuestro proveedor.

PROGRAMACIN ANDROID:
IMPLEMENTANDO UN
CONTENT PROVIDER (PARTE
1)
2 0 1 4 / 0 9 / 11/ B Y A L E J A N D R O AL C A L D E , 0 C O M M E N T S ,
IN ANDROID, OPENSOURCE

Esta es la primera entrada de un total de 4 en la que se irn describiendo los pasos a dar
para crear nuestro propio proveedor de contenidos.
Ya hemos visto cmo interactuar con un Content provider, pero no hemos visto an cmo
escribir nuestro propio Content Provider. Para hacerlo, es necesario extender
deandroid.content.ContentProvider e implementar los siguientes mtodos:

query

insert

update

delete

getType

Tambin necesitamos configurar unas cuantas cosas antes de implementarlo. Los paso a
seguir para implementarlo son los siguientes:
1.

Plantear nuestra base de datos, URIs, nombres de columnas y crear clases de


metadatos que definirn constantes para todos estos elementos de metadatos.

2.

Extender la clase abstracta ContentProvider.

3.

Implementar los mtodos query, insert, update, delete y getType.

4.

Registrar el proveedor en el Android Manifest.

PLANTEAMIENTO DE LA BASE DE DATOS


El siguiente planteamiento de la base de datos pertenece a la aplicacin FavSites, proyecto
que realic tiempo atrs y registrado bajo licencia GPLv3. Esta base de datos contiene una
nica tabla con las columnas nombre, descripcin, latitud, longitud y foto. Estos nombres
de columnas pasarn a formar parte de los metadatos en nuestra clase
FavSitesProviderMetadata.
//FavSites for Android
//

Copyright (C) 2011 Alejandro Alcalde Barros

//
//This file is part of FavSites.
//
//

FavSites is free software: you can redistribute it and/or modify

//

it under the terms of the GNU General Public License as published by

//

the Free Software Foundation, either version 3 of the License, or

//

(at your option) any later version.

//
// FavSites is distributed in the hope that it will be useful,
//

but WITHOUT ANY WARRANTY; without even the implied warranty of

//

MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

//

GNU General Public License for more details.

//
//

You should have received a copy of the GNU General Public License

//

along with FavSites. If not, see <http: //www.gnu.org/licenses/>.

package com.elbauldelprogramador.favsites.provider;
import android.net.Uri;
import android.provider.BaseColumns;

/**
* @author Alejandro Alcalde
* Definiciones necesarias para almacenar la informacin
*/
public class FavSitesProviderMetaData {
public static final String AUTHORITY = "com.elbauldelprogramador.provider.FavSites";
public static final String DATABASE_NAME = "favsites.db";
public static final int DATABASE_VERSION = 1;
public static final String FAVSITES_TABLE_NAME = "favSites";
private FavSitesProviderMetaData() {}
/**
* Clase interna para describit la tabla favSites
*/
public static final class favSitesTableMEtaData implements BaseColumns{
private favSitesTableMEtaData() {}
public static final String TABLE_NAME = "favSites";
/**
* DEfinicin del Content URI y MIMEs
*/
public static final Uri CONTENT_URI
= Uri.parse("content://" + AUTHORITY + "/sites");
public static final String CONTENT_TYPE
= "vnd.android.cursor.dir/vnd.favsites.site";
public static final String CONTENT_ITEM_TYPE
= "vnd.android.cursor.item/vnd.favsites.site";
/**
* Orden por defecto de la tabla
*/
public static final String DEFAULT_SORT_ORDER = "_ID DESC";
/**
* Orden personalizado
*/

public static final String SORT_BY_NAME_DESC = "name DESC";


public static final String SORT_BY_NAME_ASC = "name ASC";
//Columnas propias
/**
* Nombre del sitio
* Type: TEXT
*/
public static final String NAME = "name";
/**
* Descripcion del sitio
* Type: TEXT
*/
public static final String DESCRIPCION = "descripcion";
/**
* LATIDTUD
* Type: INTEGER (long)
*/
public static final String LATITUD = "latitud";
/**
* LONGITUD
* Type: INTEGER (long)
*/
public static final String LONGITUD = "longitud";
/**
* Fotografia del sitio
* Type: TEXT
*/
public static final String FOTO = "foto";
}
}

Esta clase FavSitesProviderMetaData comienza definiendo que su authority


sercom.elbauldelprogramador.provider.FavSites. Usaremos esta cadena de texto para
registrar el proveedor en el Android Manifest. Esta cadena forma la parte principal de la
URI de este proveedor.

Despues procedemos a definir una tabla (favSites) como una clase interna
deFavSitesProviderMetaData. Posteriormente, la clase favSitesTableMEtaData define una
URI para identificar las colecciones de stios. Dada la authority del prrafo anterior, la URI
para una coleccin de stios ser como la siguiente:
content://com.elbauldelprogramador.provider.FavSites/sites

La constante que se refiere a esta URI es:


FavSitesProviderMetaData.favSitesTableMEtaData.CONTENT_URI

La clase favSitesTableMEtaData define los MIME types para una coleccin de sitios y de
un nico stio. La implementacin del proveedor usar estas constantes para devolver los
MIME types para las URIs entrantes.
favSitesTableMEtaData tambin declara un conjunto de columnas: nombre, descripcin,
latitud, longitud y foto.
Es aconsejable describir los tipos de datos que poseen las columnas mediante comentarios.
Adems, la clase favSitesTableMEtaData hereda de la clase BaseColumns, la cual
proporciona el campo estandar _ID, que representa el identificador de la fila. Con todas
estas definiciones de metadatos, estamos listos para continuar con la implementacin de
nuestro proveedor.

PROGRAMACIN ANDROID:
IMPLEMENTANDO UN
CONTENT PROVIDER (PARTE
3)
2 0 1 4 / 0 9 / 11/ B Y A L E J A N D R O AL C A L D E , 0 C O M M E N T S ,
IN ANDROID, OPENSOURCE

Ya hemos visto como plantear la base de datos para un proveedor y cmo implementar
parte del proveedor de contenidos, en esta tercera parte vamos a implementar los mtodos
query, insert, update, delete y getType.
En el cdigo que vimos en la anterior entrada, se implementa el mtodo getType(), que
devuelve los tipos MIME para una URI dada. Este mtodo, al igual que muchos de los
mtodos del content provider, est sobrecargado con respecto a la URI entrante. La funcin
de este mtodo consiste en distinguir el tipo de URI. Para saber si es una coleccin de
lugares (en el caso de la aplicacinFavSites), o un nico lugar.

Como ya se vi en la entrada anterior, se usa UriMatcher para descifrar el tipo de URI. En


funcin de esta, la clase favSitesTableMEtaData tiene definida las constantes de los tipos
MIME a devolver para cada URI.

IMPLEMENTACIN DEL MTODO QUERY

El mtodo query es el responsable de devolver una coleccin de filas en funcin de una


URI dada y una clusula where.
Al igual que los otros mtodos, usa UriMatcher para identificar el tipo de URI. Si el tipo de
URI es un nico elemento, el mtodo devolver el ID del lugar de la siguiente manera:

Extrae los segmentos del path usando getPathSegments().

El mtodo query usa las proyecciones (projecttions) que creamos para identificar las
columnas devueltas. Bsicamente, query devuelve un cursor. Durante la llamada al mtodo
query, se usa el objeto SQLiteQueryBuilder para formular y ejecutar la consulta.
IMPLEMENTACIN DEL MTODO INSERT

ste mtodo se encarga de insertar registros en la base de datos y devuelve una URI que
apunta al registro insertado. Al igual que el mtodo anterior, usa UriMatcher para
identificar el tipo de URI. Primero comprueba si la URI coincide correctamente con el tipo
de coleccin de datos de dicha URI. Si esta comprobacin falla, se lanza una excepcin.
A continuacin, se validan los parmetros correspondientes a las columnas opcionales y
obligatorias. Es posible asignar valores por defecto a ciertas columnas si no se pas su valor
como parmetro.
Despus, se usa el objeto SQLiteDatabase para insertar el nuevo registro y devolver el ID
que se acaba de insertar. Por ltimo, se crea una URI nueva a partir del ID devuelto por la
base de datos.
IMPLEMENTACIN DEL MTODO UPDATE

Se encarga de actualizar registros que cumplan las condiciones que establece laclusula
where. ste mtodo devuelve el nmero de registros actualizados.
Tambin usa UriMatcher para identificar el tipo de URI. Si la URI es una coleccin de
datos, se aplica la clusula where para aplicar el update a los registros que cumplan las
condiciones de dicha clusula. Si por el contrario, el tipo de URI es de un solo registro, se
extrae el ID (En este caso de un lugar) de la URI y se especifica como una clusula where
adicional para identificar el lugar almacenado en la base de datos que tenga asignado ese
ID. Una vez acabado el proceso de actualizar los registros, Update devuelve el nmero de
registros que se modificaron.

IMPLEMENTACIN DE MTODO DELETE

Se encarga de eliminar registros basandose en la clusula where que se le pasa como


parmetro. Devuelve el nmero de filas eliminadas.
Usa UriMatcher, al igual que los anteriores. Si la URI es una coleccin, se aplica la clusula
where para borrar todos los registros que cumplan esas condiciones. Si no existe la
clusula, se eliminan todos los registros. En el caso de que la URI corresponda a un nico
elemento, se extrae su ID de la URI y se especifca como clusula where adicional (al igual
que en el mtodo update). Por ltimo, devuelve el nmero de registros elminados.
USANDO URIMATCHER PARA CONOCER EL TIPO DE URI

Hemos hablado mucho en esta seccin de UriMatcher, ahora es el momento de profundizar


en l. La mayora de los mtodos de un proveedor de contenidos estn sobrecargados. Por
ejemplo, se llama al mismo mtodo query() tanto para recuperar un nico registro o
mltiples. Es el mtodo el que debe averiguar el tipo de URI. La utilidad que ofrece
Android con UriMatcher ayuda precisamente a realizar este trabajo de identificar el tipo de
URI.
Funciona de la siguiente manera: Se le dice a una instancia de UriMatcher qu tipos de
patrones de URI va a recibir. Hay que asociarle un nmero nico a cada patrn. Una vez
registrados los patrones, podremos preguntarle al UriMatcher si la URI entrante coincide
con un cierto patrn.
En el ejemplo que estamos viendo de FavSites, existen dos patrones de URIs, uno para una
coleccin de lugares y otro para un nico lugar. En el cdigo de abajo se muestra la
implementacin que registra ambos patrones. Asigna un 1 a una coleccin de lugares y un 2
a un nico lugar. Los patrones que se usan para la URI se definieron como meta datos en la
tabla de lugares:
private static final UriMatcher sUriMatcher;
//Define Ids para cada tipo de URI
private static final int INCOMING_SITE_COLLECTION_URI_INDICATOR = 1;
private static final int INCOMING_SINGLE_SITE_URI_INDICATOR = 2;
static {
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//Patrn para varios lugares
sUriMatcher.addURI(FavSitesProviderMetaData.AUTHORITY, "sites",
INCOMING_SITE_COLLECTION_URI_INDICATOR);
//Patrn para un nico lugar.
sUriMatcher.addURI(FavSitesProviderMetaData.AUTHORITY, "sites/#",

INCOMING_SINGLE_SITE_URI_INDICATOR);
}

Conciendo esta implementacin, ahora es ms fcil saber cmo usan los otros mtodos el
UriMatcher, por ejemplo, el mtodo query:
switch (sUriMatcher.match(uri)) {
case INCOMING_SITE_COLLECTION_URI_INDICATOR:
//...
break;
case INCOMING_SINGLE_SITE_URI_INDICATOR:
//...
break;
default:
throw new IllegalArgumentException("Unknow URI " + uri);
}

Como se aprecia en el cdigo de arriba, el mtodo match devuelve el msmo nmero que
hemos registrado en el paso anterior.El constructor de UriMatcher toma un entero que se
usar para la raiz de la URI, se devolver dicho nmero si en la URL no existen segmentos
en el path o authorities. Tambin devolver la constante NO_MATCH cuando los patrones
no coincidan. Es posible crear un UriMatcher sin un identificador para la raiz de la URI, en
tal caso, Android inicializar UriMatcher a NO_MATCH internamente. Por lo tanto, el
cdigo que se muestra a continuacin es equivalente al anterior:
static {
sUriMatcher = new UriMatcher();
sUriMatcher.addURI(FavSitesProviderMetaData.AUTHORITY, "sites",
INCOMING_SITE_COLLECTION_URI_INDICATOR);
sUriMatcher.addURI(FavSitesProviderMetaData.AUTHORITY, "sites/#",
INCOMING_SINGLE_SITE_URI_INDICATOR);
}

USANDO LOS MAPAS DE PROYECCIONES (PROJECTION MAPS)

El proveedor de contenido acta de intermediario entre un conjunto abstracto de columnas


y un conjunto real de columnas en una base de datos, sin embargo los conjuntos de
columnas pueden ser distintos. Mientras construimos consultas (queries), es necesario hacer
un mapeo entre las columnas de la clusula where y las columnas reales de la base de datos.
Para configurar este projection mapnecesitamos la ayuda de la clase SQLiteQueryBuilder.

Si leemos la documentacin del mtodo joins. Por ejemplo, podra asignar nombre a
people.name. Si un mapa de proyeccin se configura para que deba contener todos los
nombres de las columnas que el usuario pueda solicitar, aunque la clave y el valor sean los
mismos.
En nuestro ejemplo, as es como se a configurado el projection map:
private static HashMap<string , String> sSitesProjectionMap;
static{
sSitesProjectionMap = new HashMap</string><string , String>();
sSitesProjectionMap.put(favSitesTableMEtaData._ID,
favSitesTableMEtaData._ID);
//nombre, desc, lati, long, foto
sSitesProjectionMap.put(favSitesTableMEtaData.NAME,
favSitesTableMEtaData.NAME);
sSitesProjectionMap.put(favSitesTableMEtaData.DESCRIPCION,
favSitesTableMEtaData.DESCRIPCION);
sSitesProjectionMap.put(favSitesTableMEtaData.LATITUD,
favSitesTableMEtaData.LATITUD);
sSitesProjectionMap.put(favSitesTableMEtaData.LONGITUD,
favSitesTableMEtaData.LONGITUD);
sSitesProjectionMap.put(favSitesTableMEtaData.FOTO,
favSitesTableMEtaData.FOTO);
}
</string>

El queryBuilder usa la variable sSitesProjectionMap as:


SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(favSitesTableMEtaData.TABLE_NAME);
qb.setProjectionMap(sSitesProjectionMap);

PROGRAMACIN ANDROID:
IMPLEMENTANDO UN
CONTENT PROVIDER (PARTE
4)
2 0 1 4 / 0 9 / 11/ B Y A L E J A N D R O AL C A L D E , 0 C O M M E N T S ,
IN ANDROID, OPENSOURCE

En esta ltima parte de una serie de 4 artculos en los que se ha ido explicando cmo
implementar un ContentProvider desde cero, se va a ver cmo registrar dicho proveedor y
cmo darle uso.
REGISTRAR EL PROVEEDOR

Para poder usar el proveedor es necesario registrarlo en el AndroidManifest:


<provider android:name=".SitesProvider"
android:authorities="com.elbauldelprogramador.provider.FavSites" />

AADIR REGISTROS
String tag = "Insertando registros...";
ContentValues cv = new ContentValues();
Log.d(tag,"Adding a site...");
cv.put(FavSitesProviderMetaData.favSitesTableMEtaData.NAME,
"NombreSitio");
cv.put(FavSitesProviderMetaData.favSitesTableMEtaData.DESCRIPCION,
"Descripcion");
cv.put(FavSitesProviderMetaData.favSitesTableMEtaData.LONGITUD,
paquete.getInt("long"));
cv.put(FavSitesProviderMetaData.favSitesTableMEtaData.LATITUD,
paquete.getInt("lat"));
ContentResolver cr = getContentResolver();
Uri uri = FavSitesProviderMetaData.favSitesTableMEtaData.CONTENT_URI;
Log.d(tag,"site insert uri:" + uri);
Uri insertedUri = cr.insert(uri, cv);
Log.d(tag,"inserted uri:" + insertedUri);

ELIMINAR REGISTROS
ContentResolver cr = getContentResolver();
Uri uri = FavSitesProviderMetaData.favSitesTableMEtaData.CONTENT_URI;
Log.d("Deleting site...","site delete uri:" + uri);
cr.delete(uri,
"_ID=?",
new String[]{"5"});

OBTENER EL NMERO DE REGISTROS

Para realizar esto, debemos crear un cursor y contar el nmero de registros de este:
Uri uri = FavSitesProviderMetaData.favSitesTableMEtaData.CONTENT_URI;
Cursor cur = managedQuery(uri,
null, // projection
null, // selection strings
null, // selection args array of strings
null);// sort order
int numeroRegistros = cur.getCount();
cur.close();

MOSTRAR LA LISTA DE STIOS

Muestra todo el contenido de la tabla sites de la base de datos.


/**
* Funcin que imprime los resultados por el Log.
*/
public void logOutput(Context context){
//Salida por LOG.
String tag = "Retrieve list of sites.";
Uri uri = FavSitesProviderMetaData.favSitesTableMEtaData.CONTENT_URI;
Activity a = (Activity) context;
Cursor c = a.managedQuery(uri
,null //projection
,null //selection string
,null //selection args array of string
,null); //sort order
int iname = c.getColumnIndex(
FavSitesProviderMetaData.favSitesTableMEtaData.NAME);
int iDesc = c.getColumnIndex(
FavSitesProviderMetaData.favSitesTableMEtaData.DESCRIPCION);
int iLat = c.getColumnIndex(
FavSitesProviderMetaData.favSitesTableMEtaData.LATITUD);
int iLong = c.getColumnIndex(
FavSitesProviderMetaData.favSitesTableMEtaData.LONGITUD);
int iFoto = c.getColumnIndex(
FavSitesProviderMetaData.favSitesTableMEtaData.FOTO);
//Informamos de los ndices
Log.d(tag, "name, description, latitude, long, photo: "
+ iname + iDesc + iLat + iLong + iFoto);
//Recorremos las filas basndonos en ndices
for(c.moveToFirst(); !c.isAfterLast();c.moveToNext()){
//Recoger los valores
String id = c.getString(0);
String name = c.getString(iname);
String desc = c.getString(iDesc);

String lat = c.getString(iLat);


String lon = c.getString(iLong);
String foto = c.getString(iFoto);
//informar
StringBuffer cbuf = new StringBuffer(id);
cbuf.append(",").append(name);
cbuf.append(",").append(desc);
cbuf.append(",").append(lat);
cbuf.append(",").append(lon);
cbuf.append(",").append(foto);
Log.d(tag, cbuf.toString());
}
//Numero de registros
int numberOfRecords = c.getCount();
Log.d(tag, "Num of records: " + numberOfRecords);
//cerrar el cursor
c.close();
}

Espero que este conjunto de cuatro artculos os haya servido de ayuda. En los prximos
artculos veremos en profundidad los intents.

PROGRAMACIN ANDROID:
INTENTS CONCEPTOS
BSICOS
2 0 1 4 / 0 9 / 11/ B Y A L E J A N D R O AL C A L D E , 2 2 C O M M E N T S ,
IN ANDROID, OPENSOURCE

Un intent sirve para invocar componentes, en android entendemos por componentes


las activities, Que son componentes de UI [Interfaz grfica], services, Cdigo ejecutndose
en segundo plano, broadcast receivers, Cdigo que responde a un mensaje de transmisin
[Broadcast messages] y proveedores de contenido, cdigo que abstre los datos.

INTRODUCCIN A LOS INTENTS


Como mecanismo para invocar componentes, los intents son bastante fciles de
comprender. Bsicamente nos permiten llamar a aplicaciones externas a la nuestra, lanzar
eventos a los que otras aplicaciones puedan responder, lanzar alarmas etc.
Vamos a mostrar un ejemplo, supongamos que tenemos la siguiente activity:
public class MiActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.MiActivity);
}
}

El layout R.Layout.MiActivity debe estar declarado y ser un archivo de layout valido. Una
vez creado este archivo de layout, es necesario registrarlo en elAndroidManifest, que ser
algo as:
<activity android:name=".MiActivity"
android:label="Mi Activity">
<intent -filter>
<action android:name="nuestra.accion.nombreAccion"/>
<category android:name="android.intent.category.DEFAULT" />
</intent>
</activity>

Al registrar la activity en el AndroidManifest, registramos tambin una accin que


podremos usar para invocar a dicha actividad. El diseador de la actividad puede asignar el
nombre que crea conveniente a la accin. Ahora que ya est todo listo, podemos lanzar un
intent para llamar a esta actividad:
public static void invokeMiActivity(Activity activity){
String actionName= "nuestra.accion.nombreAccion";
Intent intent = new Intent(actionName);
activity.startActivity(intent);
}

La convencin que se usa para nombrar una accin suele


ser.intent.action.NOMBRE_ACCION
Una vez que se invoca a la actividad, sta tiene la posibilidad de recuperar el intent que la
llam. Y podemos recuperarlo del siguiente modo:
//Este cdigo se inserta en el mtodo onCreate() de la actividad.
Intent intent = this.getIntent();
if (intent == null){
Log.d("Tag", "La actividad no se ha llamado mediante un intent.")
}

INTENTS DISPONIBLES EN ANDROID


En developer.android.com/guide/appendix/g-app-intents.html se puede encontrar una lista
con las aplicaciones disponibles en Android junto con los intents que las invocan. Por

ejemplo, para el navegador web, tenemos dos acciones, VIEW yWEB_SEARCH, que
abren el navegador en una url especfica o realizan una bsqueda.
En el caso del dialer (marcador), tenemos las acciones CALL y DIAL, que vienen dadas
por la URI tel:numero_de_telfono, la diferencia entre estas dos acciones, es que CALL
realiza la llamada al nmero de la URI, y DIAL solo lo marca, pero no realiza la llamada.
Vamos a ver ejemplos de intents que invocan a las aplicaciones mencionadas en la
documentacin de Android:
public static void invokeWebBrowser(Activity activity){
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.google.com"));
activity.startActivity(intent);
}
public static void invokeWebSearch(Activity activity){
Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
intent.setData(Uri.parse("http://www.google.com"));
activity.startActivity(intent);
}
public static void dial(Activity activity){
Intent intent = new Intent(Intent.ACTION_DIAL);
activity.startActivity(intent);
}
public static void call(Activity activity){
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:555-555-555"));
activity.startActivity(intent);
}
public static void showMapAtLatLong(Activity activity){
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("geo:0,0?z=4&q=restaurantes"));
activity.startActivity(intent);
}

COMPOSICIN DE LOS INTENTS

Un intent est formado por una accin, datos (que se representan mediante URIs), datos
extra en pares clave/valor y un nombre de clase explcito, llamado nombre del componente.
Es necesario aclarar algo, cuando un intent trae consigo un nombre de componente, se le
llama intent explcito. Cuando no lo lleva y depende de la accin y los datos se llama intent
implcito.

INTENTS Y DATA URIS


Como vimos un poco ms arriba los URIs para las acciones ACTION_DIAL y
ACTION_CALL tienen la estructura tel:nmero, y la manera de usar esta URI en el intent
para pasarla como dato es la siguiente:
intent.setData(Uri.parse("tel:555-555-555"));

El nombre de las acciones normalmente suele ser un String o un String constante con el
nombre del paquete como prefijo.
La seccin de datos de un intent no son datos realmente, se trata de punteros a datos. Est
representado por un string que representa una URI. Una Uri de un intent puede contener
argumentos, como las urls de las web.

ACCIONES GENRICAS
Vamos a volver a ver el cdigo que invoca al navegador web para analizarlo ms en
profundidad:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.google.com"));
activity.startActivity(intent);

En este caso, ACTION_VEW parece una accon muy genrica, Android se las ingenia para
averiguar a qu actividad llamar en base a esta accin haciendo uso de la composicin de la
URI. Para ello, mira el esquema que posee la URI, que en este caso es http y pregunta a
todas las actividades para saber cual de ellas comprende este esquema. Por lo tanto, la
actividad del navegador deber tener registrada la accin VIEW junto con el esquema de
datos de http:
<activity ...>
<intent -filter>
<action android:name="android.intent.action.VIEW"/>

<data android:scheme="http" />


<data android:scheme="https" />
</intent>
</activity>

SE puede obtener ms informacion del


elemento data endeveloper.android.com/guide/topics/manifest/data-element.html

INFOMACIN EXTRA
Los intents admiten adems de las acciones y datos, un atributo adicional llamadoextras.
Este tipo de dato viene dado por la forma clave/valor, en la cual el nombre de la clave
normalmente suele empezar con el nombre del paquete y el valor puede ser de cualqueira
de los tipos fundamentales u objetos arbitrrios, siempre que se implemente la inrefaz
android.os.Parcelable. Esta informacin extra se representa mediante la
clase android.os.Bundle
//Obtener un bundle
Bundle b = intent.getExtras();
//Colocar un bundle en un intent
Bundle b2 = new Bundle();
//Rellenar el bundle con datos fundamentales
putExtra(String name, boolean value);
putExtra(String name, int value);
putExtra(String name, double value);
putExtra(String name, String value);
//Otros tipos de datos
putExtra(String name, int[] value);
putExtra(String name, float[] value);
putExtra(String name, Serializable value);
putExtra(String name, Parcelable value);
putExtra(String name, Bundle value);
//Aadir bundles de otros intents
putExtra(String name, Intent otroIntent);
//Aadir el bundle al intent
intent.putExtras(b2)

getExtras devuelve el bundle que contenga el intent. Si el intent ya tiene un bundle,


putExtras transfiere los pares clave/valor adicionales del bundle nuevo al que ya exista. Si
no existe ningun bundle asociado, putExtras crear uno y copiar todos los valores.
La clase intent tiene declarados unas claves extras que acompaan a ciertas acciones,
pueden verse
endeveloper.android.com/reference/android/content/Intent.html#EXTRA_ALARM_COUN
T. Por ejemplo EXTRA_SUBJECT nos permite almacenar el asunto de un email. El valor
de esta clave es android.intent.extra.SUBJECT.

USAR COMPONENTES PARA INVOCAR


DIRECTAMENTE UNA ACTIVITY
Una forma ms directa de iniciar una actividad es mediante el su ComponentName, que es
una abstraccin del nombre del paquete y de la clase. Existen varios mtodos para realizar
esta accin en la clase Intent:
setComponent(ComponentName name);
SeClassName(String packName, String className);
setClassName(Context context, String ClassName);
setClass(Context context, Class classObject);

Se puede usar el nombre de una clase directamente sin necesidad de construir un


ComponentName. Por ejemplo, si tenemos la siguiente actividad:
public class MiActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.MiActivity);
}
}

Podemos usar el siguiente cdigo para llamarla:


Intent intent = new Intent(activity, MiActivity.class);
activity.start(intent);

As, cualquier intent podr iniciar la actividad, pero para ello, debemos registrar dicha
actividad en el AndroidManifest as:

<activity android:name=".MiActivity" android:label="Mi Activity" />

Sin ningn tipo de intent-filter, ya que estos no son necesarios cuando se invoca a una
actividad directamente mediante el nombre de su clase. Recordad que este intent es de tipo
explcito.

PROGRAMACIN ANDROID:
INTENTS CATEGORAS
2 0 1 4 / 0 9 / 11/ B Y A L E J A N D R O AL C A L D E , 0 C O M M E N T S ,
IN ANDROID, OPENSOURCE

Las actividades se pueden clasificar en categoras para as poder buscarlas basndonos en el


nombre de dicha categora. Por ejemplo, mientras el sistema se est iniciando, busca en las
actividades las que estn bajo la categoraCATEGORY_LAUNCHER.
La convencin usada para nombrar a las categoras es (para el caso de
CATEGORY_LAUNCHER):
android.intent.category.LAUNCHER

La forma de declarar las categoras en el AndroidManifest es la siguiente:

<activity android:name=".PrincipalActivity" android:label="@string/app_name">


<intent -filter>

<action android:name="android.intent.action.MAIN" />


<category android:name="android.intent.category.LAUNCHER" />
</intent>
</activity>

Vamos a ver algunas categoras predefinidas, podis encontrar la lista de todas ellas
endeveloper.android.com/reference/android/content/Intent.html#CATEGORY_ALTERNAT
IVE:

CATEGORY_DEFAULT: Si declaramos una actividad bajo la categora DEFAULT,


podr ser invocada mediante intents implcitos, de lo contrario, habr que llamarla con
intents explcitos.

CATEGORY_BROWSABLE: Si la actividad es de este tipo, podr ser invocada con


seguridad por el navegador para mostrar los datos referenciados por un link, como una
imagen o un email.

CATEGORY_GADGET: La actividad se puede embeber dentro de otra actividad


que pueda albergar gadgets.

CATEGORY_HOME: Suele existir solo una actividad de este tipo, que es la


pantalla principal, esta actividad se muestra al iniciar el telfono o pulsar el botn
home. Si existe ms de una se le pregunta al usuario cual elegir.

Cuando usamos un intent para iniciar una actividad podemos especificar qu tipo de
actividad queremos especificando la categora. Otra opcin es buscar las actividades que
coincidan con una determinada categora, por ejemplo:
Intent i = new Intent(Intent.ACTION_MAIN, null);
i.addCategory(Intent.CATEGORY_LAUNCHER);
PackageManager pm = getPackageManager();
List<resolveinfo> list = pm.queryIntentActivities(i, 0);
</resolveinfo>

PackageManager permite encontrar actividades que coincidan con un intent sin llegar a
invocarlas. Una vez ejecutado lo de arriba, podemos iterar sobre la lista e invocar a la
actividad que coincida con el nombre que deseemos:
for(ResolveInfo ri: list){
Log.d("Info", ri.toString());
String pkgName = ri.activityInfo.packageName;
String className = ri.activityInfo.name;
if(className.equals("nombre.paquete.denuestra.actividad.nombreActividad")){

Intent i = new Intent();


i.setClassName(pkgName, className);
activity.startActivity(i);
}
}

Es posible lanzar una actividad basndonos en el nombre de la categora:


Intent i = new Intent(Intent.ACTION_MAIN, null);
i.addCategory(Intent.CATEGORY_LAUNCHER);
activity.startActivity(i);

Como mencion anteriormente, en el caso de que exista ms de una actividad que satisfaga
las condiciones que impone el intent, se mostrar un dilogo al usuario para que elija cual
lanzar.
Si quisiramos invocar un intent para volver a la pantalla principal, basta con cambiar la
categora del cdigo de arriba
de Intent.CATEGORY_LAUNCHER aIntent.CATEGORY_HOME

PROGRAMACIN ANDROID:
CMO SE RESUELVEN LOS
INTENTS
2 0 1 4 / 0 9 / 11/ B Y A L E J A N D R O AL C A L D E , 0 C O M M E N T S ,
IN ANDROID, HOW TO, OPENSOURCE

Vamos a ver que estratgias usa Android para encontrar la actividad que corresponde a un
intent basndose en los intent-filter. Para resolver este problema, se establece una jerarqua.
En el primer lugar de dicha jerarqua se encuentra el nombre del componente que se adjunta
al intent. Este tipo de intent son explcitos, y dado que ya se sabe el nombre del
componente, el resto de datos asociados al intent se ignoran. Por el contrario, cuando el
intent no disponga de nombre de componente asociado, ser un intent implcito, para este
tipo de intents hay diversas formas de encontrar la actividad correspondiente.

La regla bsica es que la accin, categora o datos caractersticos de un intent deben


coincidir, o estar presentes, en los intent-filter de la actividad. De forma anloga a los
ntents, un intent-filter puede establecer varias acciones, categoras y atributos. Lo que
quiere decir que una misma actividad (con varios intent-filters), puede ser vlida y por lo
tanto responder a intents de distinto tipo. Vamos a ver los distintos critrios que se siguen
para considerar si el intent coincide con el intent-filter:

ACTION

Si un intent tiene una accin establecida, el intent-filter debe tener esa accin declarada o
no tener ninguna accin definida. Es decir, si una actividad no tiene declarado ningn intent
filter, se considera una opcin para cualquier intent con una accin.

DATA
Si no se especifica ningn tipo de dato en un intent-filter, ste no coincidir con ningn
intent que s que tenga establecido cualquier tipo de dato adicional. Es decir, esa actividad
solo se considerar una opcin para los intent que no tengan especificado ningn dato.

DATA TYPE
Para que se considere como coincidencia un tipo de dato, el intent debe ser uno de los tipos
de datos declarados en el intent-filter. Para determinar el tipo de dato android tiene dos
formas. La primera es determinar el tipo de dato a partir de la URI (si la URI es una content
URI) o si la URI es un fichero (file://). La segunda es mirar en el tipo de dato explcito
del intent. Para que esto funcione correctamente el intent no debe tener una URI de datos
adjunta, ya que se toma automticamente cuando llamamos al mtodo setType() en el
intent. Es posible cubrir multitud de subtipos con *.

DATA SCHEME
Para que un esquema de datos coincida (data scheme), el esquema de datos ha de estar
presente tanto en el intent como en el intent-filter de la actividad. Un esquema de datos es
la primera parte de una URI, por ejemplo, http://, file://, content://, ftp:// etc. Los intent no
tienen definido ningn mtodo para establecer el esquema de datos, ya que viene dado por
la URI del intent.
En el caso de que el esquema sea del tipo content: file:, se considera una coincidencia
independientemente de lo que se especifique en el intent-filter. Se aplica esta regla porque
cada componente debe saber como leer datos desde urls del tipo content o file, ya que son
locales al dispositivo.

DATA AUTHORITY
En el caso de que no se especifique authority alguno en el intent-filter, se obtendr una
coincidncia para cualquier data URI authority. Si por el contrario se especifica en el intentfilter, como ejemplo pongamos www.elbauldelprogramador.org, entonces se considerar
una coincidencia un esquema de datos y una authority.

Un ejemplo fcil para comprender esto podra ser que tengamos esto declarado como
authority en el intent-filter www.elbauldelprogramador.org, y como esquema de datos https,
todo intent fallar si usamos lo siguiente, /path, ya que el esquema de datos http no est
declarado.

DATA PATH
Ningn path (ruta) especificado en el intent-filter resultar ser una coincidencia para
cualquier path entrante. Para validar un intent, el esquema de datos, el authority y el path
trabajan juntos. Es decir en http://elbauldelprogramador.org/pathlos tres trabajan juntos
para validar el intent.

INTENT CATEGORIES
Cada categora debe estar presente en el intent-filter. En el caso de que en el intent-filter no
exista ninguna categora especificada, esa actividad solo ser una coincidencia para los
intent que no tengan ninguna categora especificada. Sin embargo, hay que tener en cuenta
que los intent implcitos que se pasanstartActivity() contienen al menos una categora que
es android.intent-category.DEFAULT. Por lo tanto, hemos de tener en cuenta que si
queremos llamar a una actividad mediante un intent implcito debemos registrar dicha
categora en el intent-filter.
Si una actividad no tiene declarada la categora por defecto, pero conocemos su nombre de
componente, podmos llamarle mediante un intent explcito.

PROGRAMACIN ANDROID
EJEMPLO DE USO DE
ACTION_PICK
2 0 1 4 / 0 9 / 11/ B Y A L E J A N D R O AL C A L D E , 0 C O M M E N T S ,
IN ANDROID, OPENSOURCE

La idea de ACTION_PICK es lanzar una actividad que muestre una liste de objetos a
seleccionar para que el usuario elija uno de ellos. Una vez elegido, la actividad devuelve la
URI del elemento elegido. As se permite reusar la interfaz grfica.
Se debe indicar usando MIME types la coleccin de datos que vamos a usar, el cual
apuntar a un cursor parecido a este:
vnd.android.cursor.dir/vnd.favsites.site

LA actividad es la encargada de recuperar los datos desde el CV basandose en la URI.


Para este tipo de accin(ACTION_PICK), no podemos usar startActivity(), ya que no
devuelve resultado alguno debido a que abre la nueva actividad en un hilo separado. Es
decir, startActivity() es una llamada asncrona que no usa callbacks para indicar qu ha
pasado en la actividad invocada.
Por lo tanto debemos usar startActivityForResult(), que s que tiene callback. Este mtodo
acepta dos parmetros, el primero es el intent que queremos lanzar, y el segundo es un
requestCode. Cuando la actividad que hemos lanzado finalize, se llamar en la actividad

desde la que se lanz el intent a onActivityResult() con el requestCode que le


proporcionamos. La cabecera de este mtodo de callback es la siguiente:
protected void onActivityResult (int requestCode, int resultCode, Intent data)

Donde el primer argumento es el cdigo que le hemos pasado astartActivityForResult(),


el segundo argumento (resultcode), puede serRESULT_OK, RESULT_CANCELED o un
cdigo propio. Si decidimos usar un cdigo propio, debe empezar
en RESULT_FIRST_USER. El tercer parmetro contiene cualquier dato adicional que la
actividad invocada devuelva. Para ACTION_PICKtendremos una URI apuntando a un
elemento.
Para este ejemplo de uso de ACTION_PICK, es necesario tener instalada la
aplicacin NotePad, que est disponible para instalar en los ejemplos que vienen al instalar
el SDK:
public static void invokePick(Activity activity)
{
Intent pickIntent = new Intent(Intent.ACTION_PICK);
int requestCode = 1;
pickIntent.setData(Uri.parse(
"content://com.google.provider.NotePad/notes"));
activity.startActivityForResult(pickIntent, requestCode);
}
protected void onActivityResult(int requestCode
,int resultCode
,Intent outputIntent)
{
//Llamamos a super para informar a la clase padre que la llamada a la actividad a finalizado
super.onActivityResult(requestCode, resultCode, outputIntent);
IntentsUtils.parseResult(this, requestCode, resultCode, outputIntent);
}
public static void parseResult(MainActivity activity
, int requestCode
, int resultCode
, Intent outputIntent)
{
if (requestCode != 1){
Log.d("Test", "cualquier otra cosa ha llamado a este mtodo, no nosotros");
return;
}

if (resultCode != Activity.RESULT_OK){
Log.d("test", "ResultCode no es OK: " + resultCode);
return;
}
Log.d("Test", "Resulcode es OK:" + resultCode);
Uri selectedUri = outputIntent.getData();
Log.d("test", "La uri de salida:" + selectedUri.toString());
//Mostramos la nota
outputIntent.setAction(Intent.ACTION_VIEW);
startActivity(outputIntent);
}

Las constantes RESTULT_OK, RESULT_CANCEL Y RESULT_FIRST_USER estn


definidas en la clase de la Actividad y sus respectivos valores son:
RESTULT_OK
RESULT_CANCEL

= -1;
= 0;

RESULT_FIRST_USER = 1;

Por otro lado, en la aplicacin notePad, debe haber un cdigo devuelva un valor en el caso
de que se haya llamado a la actividad desde un intent con ACTION_PICK como accin:
@Override
protected void onListItemClick(ListView l, View v, int position, long id){
Uri uri = ContentUris.withAppendedId(getIntent().getData(), id);
String action = getIntent().getAction();
if(Intent.ACTION_PICK.equals(action) || Intent.ACTION_GET_CONTENT.equals(action))
//devolvemos la nota seleccionada
setResult(RESULT_OK, new Intent().setData(uri));
else
//Lanzamos una actividad para ver, editar la nota
startActivity(new Intent(Intent.ACTION_EDIT, uri));
}

Das könnte Ihnen auch gefallen