Fragments Android

142
Activity – entender y usar una Actividad Aquí vamos a extendernos para explicar completamente que es una actividad. Si quieres ir directamente al código está al final. Y si no te sientes interesado por conocer completamente qué es una actividad será mejor que mires en otros de los cientos de tutoriales más directos que hay por Internet. Empecemos a comprender a fondo que es una Activity de Android (Actividad en español). Todos sabemos que es una aplicación informática. Pero por si lo hemos olvidado lo resumo: una aplicación es un programa, que es una herramienta que permite al usuario realizar algún que otro tipo de trabajo (estos trabajos pueden ser, editar un texto con una herramienta de edición de textos, jugar a un juego, usar una calculadora, todo lo que se nos ocurra). Y una aplicación internamente consta de código, así como ficheros que pueden ser de audio, binarios, entre otros. Si, por ejemplo, una aplicación fuera “el Deporte”, esta contendría internamente todos los deportes posibles como gimnasia rítmica, hacer flexiones, bicicleta, correr, baloncesto, etc. Por cierto, la aplicaciones para dispositivos móviles se llaman App -viene de convertir en chic y estilosa la palabra en inglés “Application”, lo que es quedarnos con las tres primeras letras- es lo mismo con otro nombre más modernamente guay. Entre el código que nos encontramos, en Android, se usa el termino de Activity (“Actividad” en español) para denominar a un tipo de clases java que heredan de Activity -no te preocupes si no entendiste esto, lo explicaremos un poco más adelante. Una actividad, como su propio nombre indica, es algo que el usuario puede hacer. El símil que se me ocurre ahora mismo, con el ejemplo anterior, es el de actividad

Transcript of Fragments Android

Page 1: Fragments Android

Activity – entender y usar una ActividadAquí vamos a extendernos para explicar completamente que es una actividad. Si quieres ir

directamente al código está al final. Y si no te sientes interesado por conocer

completamente qué es una actividad será mejor que mires en otros de los cientos de

tutoriales más directos que hay por Internet. Empecemos a comprender a fondo que es una

Activity de Android (Actividad en español).

Todos sabemos que es una aplicación informática. Pero por si lo hemos olvidado lo

resumo: una aplicación es un programa, que es una herramienta que permite al usuario

realizar algún que otro tipo de trabajo (estos trabajos pueden ser, editar un texto con una

herramienta de edición de textos, jugar a un juego, usar una calculadora, todo lo que se nos

ocurra). Y una aplicación internamente consta de código, así como ficheros que pueden ser

de audio, binarios, entre otros. Si, por ejemplo, una aplicación fuera “el Deporte”, esta

contendría internamente todos los deportes posibles como gimnasia rítmica, hacer

flexiones, bicicleta, correr, baloncesto, etc. Por cierto, la aplicaciones para dispositivos

móviles se llaman App -viene de convertir en chic y estilosa la palabra en inglés

“Application”, lo que es quedarnos con las tres primeras letras- es lo mismo con otro

nombre más modernamente guay.

Entre el código que nos encontramos, en Android, se usa el termino

de Activity (“Actividad” en español) para denominar a un tipo de clases java que heredan

de Activity -no te preocupes si no entendiste esto, lo explicaremos un poco más adelante.

Una actividad, como su propio nombre indica, es algo que el usuario puede hacer. El símil

que se me ocurre ahora mismo, con el ejemplo anterior, es el de actividad física, que es un

conjunto de movimientos del cuerpo que obtienen como resultado un gasto de energía. En

Android una actividad es lo mismo, es un conjunto de acciones (tocar la pantalla para

apretar un botón, para escribir con el teclado, etc) que son una iteración directa con el

usuario y que afectan a una parte de la aplicación. Para concretar más, vamos a suponer que

nuestra actividad física es correr -es una parte activa de la aplicación- con lo que nuestra

actividad será correr.

Para entenderlo mejor, vamos a suponer que tenemos la App mencionada anteriormente se

llama “el Deporte”. Esta tiene actividades en su interior que son:

Page 2: Fragments Android

Correr

Baloncesto

Flexiones

Con éstas nos vale para ejemplificar lo que queremos, podría haber más o menos como se

mencionó antes.

Lo que queremos hacer es correr, con lo que lanzamos nuestra actividad de correr.

Actividad que estará en primero plano y que contará con un ciclo de vida.

Una actividad se caracteriza por tener un ciclo de vida. Un ciclo de vida, al igual que el

ciclo vida de un animal: nace, crece, come, se reproduce y muere; también, es semejante en

una actividad. De la actividad de correr tendría el siguiente ciclo de vida: estiramos antes de

empezar a correr, comenzamos a correr, corremos a tope, en cierto momento decidimos

hacer una pausa para hacer flexiones, luego podemos decidir continuar con la carrera, y en

otro momento dejamos correr para otro día. El siguiente diagrama (modificación del oficial

para realizar el ejemplo, diagrama oficial que mostramos más adelante) muestra como se

desarrolla el ciclo de vida de la actividad de correr. Échale un vistazo rápido y continúa

leyendo la explicación:

Page 3: Fragments Android

 

C

o

m

o

habrás visto, el ciclo de vida no es del todo lineal, sino que podemos tomar decisiones y

actuaremos en consecuencia, por ejemplo, podemos decidir hacer un descanso y luego

continuar corriendo o terminar. Pero lo que es lineal es lineal (en el ciclo de vida animal es

lineal comer antes de hacer cacotas, si no se come lo segundo es imposible de conseguir),

siempre calentaremos antes de correr, sino no es un deporte –decidir saltarnos el

calentamiento no elimina la linealidad, el calentamiento no se puede hacer mientras se

corre, o se hace antes o no se hace (por evitar dudas con el vocabulario, explicamos que

Page 4: Fragments Android

“calentar” siempre es antes de realizar un deporte, no después; para más dudas consular la

RAE).

Puede que existan preguntas acerca de los dos círculos rojos del diagrama, suelen llevar a

confusión, pero son completamente diferentes (También pueden darse dudas en algunos

estados como el de pausar y parar, pero esos y otros que puedan suscitar dudas se

esclarecerán en cuanto veamos un poco más adelante como funciona ese ciclo de vida en

formato App):

El que “mata a la actividad” lo hace porque algo más importante requiere nuestra atención

absoluta, y no tenemos tiempo para seguir corriendo. No se decide terminar de correr pero

se acaba. Retomaremos la actividad de correr desde el principio cuando queramos.

El que “concluye a la actividad” es porque decidimos terminar de correr, entonces acabamos

la actividad.

Hemos comentado otras dos actividades: hacer flexiones y baloncesto. En cierto momento

del ciclo de vida de la actividad de correr, podemos decidir hacer la actividad de hacer

flexiones. Con lo que ponemos en segundo plano a la actividad de correr y empezamos con

la de hacer flexiones –decir que la actividad de hacer flexiones también tiene su propio

ciclo de vida que no dibujaremos por ser innecesario, te lo imaginas   . La actividad de

baloncesto también tiene su propio ciclo de vida, pero en este ejemplo nunca será puesto en

marcha –simplemente hemos puesto la actividad de baloncesto para ejemplificar que puede

haber actividades que nunca se ejecuten.

Ahora vuelve al gráfico anterior e imagínate que has salido a correr, haz un recorrido

mental por el ciclo de vida de la actividad de correr y verás como todo va cobrando sentido.

Cuando acabes mira el siguiente ciclo de vida del mismo diagrama de ejemplo (el diagrama

es una modificación del oficial que mostramos un poco más adelante, paciencia), ésta vez

suponiendo que es una App de verdad en un Smartphone o Tablet. A la que observamos el

siguiente diagrama, imagínate que tienes instalada la aplicación “El Deporte”, y que te

dispones a arrancar la actividad de correr en tu móvil, que la vas a ver en la pantalla, vas a

poder tocar, e interactuar directamente; esta va a ser la actividad activa.

Page 5: Fragments Android

 Si pensamos en todo lo que podemos hacer con nuestro móvil teniendo la aplicación en

marcha, y delante de nosotros la actividad de correr en ejecución es posiblemente (éstas no

son las únicas acciones, otras también desencadenarán los mismos caminos por el ciclo de

vida):

Arrancar la actividad: Pasará por Crear, Empezar y Continuar, para llegar a la ejecución

normal.

Page 6: Fragments Android

Usar de manera normal la actividad: estamos en la actividad en ejecución.

Una ventana emergente se ha abierto: Pasará por Pausar.

Cambiar a otra actividad o bloquear el móvil: Pasará por Pausar y Parar. (Nota aclaratoria: si

se cambia a otra actividad pasa necesariamente por pausar y parar, no ha tenido que surgir

una ventana emergente para pasar por pausar, si se cambia de actividad se pasa por ambos

estados directamente; esto se da con otras acciones que pasen por varios estados).

Apagar el móvil: Pasará por Pausar, Parar y Destruir.

Entendido esto veamos el ciclo oficial de una actividad (así como los anteriores gráficos

son una modificación ofrecida por www.Jarroba.com para ejemplificar sobre el oficial, el

siguiente gráfico es un traducción del oficial de Android, el diagrama oficial está disponible

en http://developer.android.com/reference/android/app/Activity.html).

Page 7: Fragments Android

 Empezamos a hablar en términos de programación. Como vemos en los recuadros existen

varios métodos con nombres en inglés iguales a los que explicamos anteriormente. Estos

métodos están en la clase Activity, clase que tendremos que heredar para que nuestra clase

se convierta en una actividad de la siguiente forma (Nota: hasta que no avise de lo

contrario, esto es código no funcional, con lo justo para entender la teoría; aviso más

adelante cuando aparezca el código funcional):

Page 8: Fragments Android

public class Correr extends Activity { //Aquí estará contenido de toda la actividad de Correr}

Podemos añadir opcionalmente cada uno de los métodos para sobrescribirlos (esto lo indica

el @Override) a la clase padre Activity, no siendo obligatorio ninguno. Dentro

escribiremos el código que queramos que se ejecute, y cada método será llamado por la

clase Activity en el momento indicado anteriormente. A continuación escribimos todos los

métodos posibles, los que no necesitemos ni los escribimos. No ponerlos no les priva de

existencia, siguen existiendo en la clase Activity, por tanto en el ciclo de vida de nuestra

Actividad, pero al ejecutarse a su momento estarán vacíos.

public class actividad_de_correr extends Activity {

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //Nuestro código a ejecutar en este momento }

@Override public void onStart(){ super.onStart(); //Nuestro código a ejecutar en este momento }

@Override public void onRestart(){ super.onRestart(); //Nuestro código a ejecutar en este momento }

@Override public void onResume(){ super.onResume(); //Nuestro código a ejecutar en este momento }

@Override public void onPause(){ super.onPause(); //Nuestro código a ejecutar en este momento

Page 9: Fragments Android

}

@Override public void onStop(){ super.onStop(); //Nuestro código a ejecutar en este momento }

@Override public void onDestroy(){ super.onDestroy(); //Nuestro código a ejecutar en este momento }

}

Para empezar, recomiendo usar únicamente el método onCreate, que es el que se ejecuta al

arrancar la actividad, el resto de métodos los emplearemos en caso de necesitarlos.

Si te preguntas para que se usa el resto de métodos, imagínate un juego en nuestro móvil.

Estamos jugando y en cierto momento nos da por apagar el móvil. Para no perder el

progreso, en el onDestroy recolectamos los datos de la partida y los guardamos en la

memoria persistente. Así la próxima vez que juguemos, en el onCreate preguntará a la

memoria si existen datos guardados del juego, y cargará la partida en el mismo instante en

la que la dejamos.

public class actividad_de_correr extends Activity {

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //Nuestro código a ejecutar en este momento }

}

A partir de aquí expondremos un ejemplo con código completamente funcional.

Realizaremos la aplicación “El_Deporte”. Dentro contendrá dos actividades: Correr y

Flexiones (quitamos la de baloncesto por simplificar el código). Creamos un nuevo

Page 10: Fragments Android

proyecto de Android, le ponemos el nombre de la aplicación “El_Deporte” y pulsamos todo

el rato a “Next” hasta terminar, así tener le proyecto listo para trabajar.

 Vamos a la carpeta “res” y ahí a la carpeta “layout” y creamos dos xml.

Uno llamado “layout_correr.xml” con el siguiente código (como construir y entender este

código xml lo explicaremos en otro tutorial, aquí nos lo creemos):

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" >

<TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content"

Page 11: Fragments Android

android:text="Estamos corriendo" android:textAppearance="?android:attr/textAppearanceLarge" />

<Button android:id="@+id/button_flexiones" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Ponerse a hacer Flexiones" android:onClick="clicEnBoton_IrAFlexiones" />

</LinearLayout>

Este layout muestra al usuario:

Y otro “layout_flexiones.xml”, con el siguiente código:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" >

<TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Estamos haciendo Flexiones" android:textAppearance="?android:attr/textAppearanceLarge" />

</LinearLayout>

Este otro muestra por pantalla:

Page 12: Fragments Android

Por norma general, una actividad está asociada a un Layout. Un Layout es la ventana o

vista que el usuario ve en la pantalla: botones, textos, barras de desplazamiento, etc. La

asociación se hace en el onCreate con el método llamado “setContentView”, como veremos

en el siguiente código.

Si has visto algo del patrón Modelo-Vista-Controlador, decirte que lo que llamamos en

Android Layout es la Vista, la Activity debería de ser el Modelo, y el resto de clases

actuarán de Controlador.

Creamos una nueva actividad que llamamos “Correr” y ponemos el siguiente código.

package com.example.el_deporte;

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

public class Correr extends Activity {

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.layout_correr); }

public void clicEnBoton_IrAFlexiones (View view) { Intent intent = new Intent(this, Flexiones.class); startActivity(intent); }

}

Aquí tenemos el onCreate al que se le pasa por parámetro una variable que se llama

“savedInstanceState” de tipo “Bundle”. Esto lo explicaremos en otro tutorial más

extendidamente. Pero para que vayas tomando conceptos un resumen rápido: un Bundle

son datos que le pasamos a esta actividad para inicializar su estado. En este ejemplo no lo

usamos, pero si pensamos por ejemplo en un juego, en el que se pide en una actividad el

sexo del personaje, y pasamos a la siguiente actividad, en el que se ve nuestro personaje

Page 13: Fragments Android

con el sexo elegido; el sexo como variable hombre o mujer ha tenido que ser pasado de

algún modo de una actividad a otra, se hace con “Bundle”.

El onCreate requiere iniciar al padre antes de todo, de ahí la

línea: super.onCreate(savedInstanceState);

En la línea siguiente establecemos que Layout queremos asociar a esta actividad, como es

la actividad “correr”, es justo asociarle el layout “layout_correr”, se hace como se muestra

en: setContentView(R.layout.layout_correr);

Existe otro método normal y corriente llamado “cliclEnBoton_IrAFelxiones” que es el que

se ejecuta cuando hacemos clic en el botón con el texto “Ponerse a hacer Flexiones”. Este

se llama desde el propio xml en la línea android:onClick=“clicEnBoton_IrAFlexiones” y lo

que hace es que al pulsar el botón, este método se ejecute.

Este método lo único que hace es abrir la otra actividad “Flexiones”. Se hace con algo

llamado “Intent”. Un “Intent” es declarar la intención de lo que se quiere hacer, en este

caso la intención es ejecutar la actividad Flexiones –aunque podría decir que además de

abrir otra actividad lo que queremos es también pasar ciertos datos como en el ejemplo

anterior del juego con el sexo. Dicho de otro modo, un “Intent” es un paquete con todo lo

que queramos configurar la nueva activity a ejecutar. Solo falta decir que el “this” es el

contexto de la actividad actual (para saberlo todo sobre el Context mira este artículo). Esto

lo realiza el código: Intent intent = new Intent(this, Flexiones.class);

Después de declarar la intención hay que lanzar la actividad con: startActivity(intent);

Para llegar a la siguiente actividad y Layout asociado necesitamos su código. Creamos otra

nueva actividad que llamamos “Flexiones” con este código:

package com.example.el_deporte;

import android.app.Activity;import android.os.Bundle;

public class Flexiones extends Activity {

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.layout_flexiones); }

Page 14: Fragments Android

}

Así cuando lleguemos a esta actividad, se mostrará por pantalla el Layout correspondiente.

Pero existe un problema de seguridad que hará que la aplicación falle si intentamos esto sin

más. Nos queda modificar el fichero llamado “AndroidManifest.xml”. Este fichero es el

encargado de manejar los permisos de la aplicación, y aquí hay que declarar todo lo que

nuestra aplicación pueda suponer un problema de seguridad. En este Manifiesto, tendremos

que poner si nuestra aplicación hará uso de Internet, si va a poder realizar llamadas, etc;

estos son los permisos que el usuario tiene que aceptar cuando el usuario se dispone a

instalar una aplicación. La apertura de actividades tiene que estar controlado desde este

fichero.

Para ello vamos al “AndroidManifest.xml”. En la pestaña “Application”, en el apartado

“Application Nodes” pulsamos el botón “Add…”.

 En la ventana que se nos abre, nos aseguramos que esté marcada la casilla “Create a new

element at the top level, in Application”. Elegimos “Activity” y pulsamos “OK”.

Ahora, aseguramos que tenemos seleccionada el nuevo nodo que hemos creado que se

llama “Activity”. Estarán todos sus campos correspondientes a la derecha en blanco,

pulsamos en Name en el botón “Browse…”.

Page 15: Fragments Android

Otra ventana nueva se nos abrirá. Esperamos unos segundos a que carguen todas las

actividades que tenemos –un porcentaje nos avisará de ello- y seleccionamos la actividad

que deseamos, en este ejemplo “Flexiones”. Acabamos pulsando “OK”.

 Guardamos el fichero “AndroidManifest.xml” y ya está. A continuación una pantalla de

cómo queda finalmente:

Page 16: Fragments Android

 Con lo que hemos hecho en el “AndroidManifest.xml” lo único que hemos hecho es añadir

la siguiente línea a su XML:

<activity android:name="Flexiones"></activity>

Si abrimos el XML del “AndroidManifest.xml” nos habrá quedado una estructura parecida

a la que sigue:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.el_deporte" android:versionCode="1" android:versionName="1.0" >

<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15" />

<application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".Correr" android:label=""> <intent-filter> <action android:name="android.intent.action.MAIN" />

Page 17: Fragments Android

<category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="Flexiones"></activity> </application>

</manifest>

Ya podemos ejecutar nuestra aplicación en el emulador y probar como hemos hecho una

actividad que al pulsar el botón se pasa a la otra. Como llevamos diciendo en el ejemplo de

correr, estamos corriendo en la siguiente pantalla:

 Pulsamos el botón de “Ponerse a hacer Flexiones” y pausamos la actividad de Correr para

arrancar la actividad de Flexiones.

Page 18: Fragments Android

Para volver a la actividad anterior pulsamos el botón de atrás del dispositivo, con lo que la

actividad de Correr continuará.

Puedes descargarte el proyecto entero desde aquí: Descargar App El_Deporte

Esperamos que te haya esclarecido lo relacionado con todo lo relacionado sobre actividades

de Android, ya que es lo más gordo que tiene -y más importante- requiere ser bien

entendido. Para cualquier pregunta o aclaración en un comentario que responderemos lo

antes posible.

Recomiendo que prosigas con el estudio de Android en el siguiente artículo sobre el

Context pinchando aquí, donde explicamos en profundidad qué es y para qué sirve.

 

Ver en vivo como funciona el ciclo de vida de una actividad: Para probar como funciona

el ciclo de vida de una actividad, lo mejor es verlo por nosotros mismos. Para ello

sustituimos la actividad Correr por el siguiente código, donde se muestran todos los estados

por los que puede pasar:

package com.example.el_deporte;

import android.app.Activity;import android.content.Intent;

Page 19: Fragments Android

import android.os.Bundle;import android.view.View;import android.util.Log;

public class Correr extends Activity {

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.layout_correr); Log.v("test", "Create"); }

public void clicEnBoton_IrAFlexiones (View view) { Intent intent = new Intent(this, Flexiones.class); startActivity(intent); }

@Override public void onStart(){ super.onStart(); Log.v("test", "Start"); }

@Override public void onRestart(){ super.onRestart(); Log.v("test", "Restart"); }

@Override public void onResume(){ super.onResume(); Log.v("test", "Resume"); }

@Override public void onPause(){ super.onPause(); Log.v("test", "Pause"); }

@Override public void onStop(){ super.onStop(); Log.v("test", "Stop");

Page 20: Fragments Android

}

@Override public void onDestroy(){ super.onDestroy(); Log.v("test", "Destroy"); }

}

Hemos añadido a este código un log en cada método del ciclo de vida para poder ver por el

LogCat de Android la salida.

Trastear un poco con esto nos hará entender como funciona el ciclo de vida de una

actividad.

Por ejemplo:

1º Supongamos que arrancamos la aplicación, se cargará la actividad de Correr, y por el

LogCat se nos mostrará en cada línea: Create, Start y Resume.

2º Ahora pulsamos el botón, como otra actividad se nos pone encima ocultándola

completamente, se mostrará en este orden: Pause y Stop.

3º Si pulsamos el botón de volver, retomamos la actividad de Correr y se mostrará: Restart,

Start y Resume.

4º Si pulsamos el botón de inicio para volver al escritorio, se nos mostrará: Pause y Stop.

Podemos hacer todas las pruebas que queramos, para entender perfectamente el ciclo de

vida de la actividad. Es recomendable que eches un vistazo al diagrama para poder seguir la

vida de una actividad.

Fragments (fragmentos) en Android

Page 21: Fragments Android

Este artículo es tan profundo e ilustrativo que bien podríamos haber titulado como

“Fragment vs Activity”, “Aplicaciones soportadas para varios tamaños de pantalla”,

“Aprende a desarrollar en Android para APPs multi-dispositivo” o incluso “Fragments fácil

o para Dummies”. Antes de empezar he de decir que requiere conocimientos de Android

avanzados. Previa a la descripción, te recomiendo que eches un vistazo a las Activitys

en http://jarroba.com/activity-entender-y-usar-una-actividad/. Para los ejemplo usaremos

listados, que detallamos en http://jarroba.com/listview-o-listado-en-android/, por lo que te

recomiendo que le eches un vistazo si no lo has hecho ya. Empezaremos con unos

conceptos mínimos pero breves, y programaremos nuestro primero programa funcional con

Fragments en Android.

 

Me entusiasmo la frase que una vez dijera el filósofo Confucio (extraída

de http://es.wikiquote.org/wiki/Confucio):Cada cosa tiene su belleza, pero no todos pueden verla

Si la extrapolamos al terreno tecnológico en el que nos movemos, al de las pantallas

táctiles; descubriremos que no es lo mismo un móvil, que una Tablet, que un reloj, que un

monitor de unas pocas pulgadas, a una gran televisión, etc. Todas estas cosas tienen

pantallas de muy diferente tamaño y cada uno es bello por lo que es. No podemos

resistirnos al ver la belleza de los tamaños de las pantallas, pues no todos los usuarios

deberán de ver lo mismo si usan objetos con tamaños de pantalla diferentes. Como

desarrolladores tenemos que ver la belleza completa de cada cosa. Y tiene una razón de ser.

 

Como desarrollador de Android es muy común estar acostumbrados a programar las views

aplicando dp para los tamaños, así las vistas se escalan para cada tipo de pantalla. Está bien

para pantallas de pulgadas aproximadas a la que hemos desarrollado la aplicación en una

primera instancia. Pero para nada está bien en pantallas de una gran diferencia de pulgadas.

Como es diseñar una APP para un televisor de 100 pulgadas, y que también funcione en un

reloj de 2 pulgadas; evidentemente se iba a ver diminuto e inmanejable. Lo mismo ocurre

con la forma, no es lo mismo un Smartphone que tiende a ser rectangular bastante alargado,

a un Tablet que suele respetar las proporciones áureas (misma forma que una tarjeta de

Page 22: Fragments Android

crédito). Debemos hacernos siempre del mejor aprovechamiento del espacio de cada

pantalla –tanto en tamaño como en forma- siempre en virtud de la comodidad del usuario.

Nota: Aquí solo nos interesa -además de la forma de la pantalla- el tamaño de pantalla,

hablamos en pulgadas (normalmente la vemos representada como 7’’ o 7 pulgadas), a más

pulgadas entonces pantalla más grande, a menos más pequeña. No nos interesa, y no

confundir con el tamaño,  la resolución de la pantalla (que solemos ver cómo 1024×768).

¿Y porque no nos interesa? Porque la pantalla es a su vez interfaz táctil, por lo que define la

comodidad de uso. Una pantalla más grande se verá más contenido que en más pequeña con

el mismo esfuerzo; además, una pantalla más grande tiene más espacio para pulsar y

manejar que una más pequeña. De ahí que la resolución carezca de importancia.

Para entenderlo vamos a suponer el siguiente ejemplo. Queremos un listado de animales.

En cada fila de la lista mostrará el nombre del animal y una foto en miniatura.

Al seleccionar un animal (una fila de la lista), me muestre el animal en detalle, es decir la

descripción del animal.

Page 23: Fragments Android

Si te fijas en las imágenes anteriores, vemos el enunciado del ejemplo perfectamente

reflejado en estas imágenes. Pero fíjate en una cosa más, ¿Qué dispositivo es el que ejecuta

la aplicación? Un Smartphone, propiamente. Es decir, un dispositivo que cabe en la mano y

que irá de las 3 a las 5 pulgadas más o menos; además, su forma suele ser rectangular

alargada. Ahora bien, imagínate las mismas dos imágenes anteriores en un Tablet de unas 7

a 10 pulgadas aproximadamente. Si usáramos la aplicación directamente sobre un Tablet, te

muestro como quedaría el listado:

Page 24: Fragments Android

Horrible ¿No crees que es una pérdida de espacio de la pantalla impresionante? Que la

parte de la derecha pudiera dar lugar a mostrar otra cosa al usuario. Teniendo tanta pantalla

se debe de aprovechar. Esto último se escusa en proporcionar una mejor experiencia al

usuario con pantallas de tamaños diferentes, así como la de proporcionar acciones que de

no disponer de cierto tamaño de pantalla no se podrían llevar a cabo, además de evitar la

fragmentación de nuestro programa (seguro que te suena lo de la fragmentación de

Android, pues en un programa Android se corrige con el uso de fragmentos, así nuestra

aplicación será universal y usable para cualquier tamaño de pantalla).

Con el ejemplo, para una Tablet, se me ocurre que podríamos aprovechar el espacio

derecho para mostrar el detalle. Así es mucho más cómodo y no hay que estar volviendo

atrás cada vez.

Page 25: Fragments Android

Mucho mejor. Al seleccionar un elemento del listado, se mostrará a la derecha el detalle del

mismo. Mejor aprovechamiento de la pantalla y mucho más cómodo (no hay que volver

atrás, se puede seleccionar otro inmediatamente).

Cabe aclarar que si mantenemos este diseño para un Smartphone, la comodidad del Tablet

se pierde, pues no se podría leer bien la descripción del pájaro al aparecer muy pequeña y el

listado sería difícil de pulsar al ser muy estrecho. Por esto tiene sentido que en el móvil

tenga un diseño y funcionalidades diferentes al del Tablet.

Por cuestiones de espacio nos limitaremos al ejemplo de Smartphone y Tablet. Pero

imagina que queremos hacer esta misma aplicación para un reloj de unas 2 pulgadas, puede

que en vez de poner un listado pusiéramos un grid con las imágenes de los pájaros para que

el dedo pudiera pulsar mejor los elementos, y la descripción aparecería aparte sin foto. O en

una televisión de 40 pulgadas imagina la cantidad de cosas que entrarían; podríamos, por

ejemplo, poner al lado de la descripción un vídeo del pájaro que vuela y debajo la ficha

técnica, todo en la misma pantalla. Esto quiere decir que nos ajustamos al tamaño para

realizar el diseño y la funcionalidad.

Por suerte para nosotros, no tenemos que hacer un diseño para cada pulgada de cada

pantalla y para cada forma. Nos atendemos a la limitación por las modas. Pongo todos los

dispositivos existentes con Android que pudiera ser bueno prestar atención al diseño y a la

funcionalidad. Siempre claro, dependiendo del alcance de nuestra aplicación (las pulgadas

y formas son orientativas):

Page 26: Fragments Android

Reloj (1 a 2 pulgadas, cuadrado)

Smarphone o dispositivo GPS (3 a 5 pulgadas, rectangular alargado)

Phablet (5 a 7 pulgadas, rectangular alargado)

Tablet (7 a 12 pulgadas, proporción aurea)

Pantalla de un ordenador (14 a 30 pulgadas, rectangular alargado o proporción aurea)

Televisión (30 a 80 pulgadas, proporción aurea)

Proyector (80 a 300 pulgadas, proporción aurea)

Dependerá del desarrollador saber a qué diseños y que funcionalidades atender en cada

momento.

¿Y se puede hacer esto en la misma aplicación? o ¿Se desarrolla una App para cada tamaño

de pantalla? La respuesta es, siempre desde la perspectiva del “buen programador”, se debe

y se puede hacer en la misma aplicación. Los motivos son muy simples, que seguro estás

harto de escuchar: modularidad y reusabilidad. Esto se logra con los Fragments o

Fragmentos.

Si conoces el comportamiento de las Activities (ver en http://jarroba.com/activity-entender-

y-usar-una-actividad/), advertirás que tienen un ciclo de vida y están asociadas a una vista

(Layout en XML).

Un Fragment es un trozo (o un fragmento, si apelamos al mismo nombre que lo viste) de

una actividad. Un Fragment tiene su propio Layout y su propio ciclo de vida.

Puedes apreciar, que programándolo una vez, puedes rehusar el código para que se adapte a

diferentes tamaños de pantalla con la disposición de los elementos que quieras.

Por lo que adivinarás que un Fragment siempre ha de estar en una Activity, no puede existir

independiente. Y el ciclo de vida de la Activity contenedora afectará al del Fragment; por

ejemplo, si se pausa la Activity el o los Fragments, que estén en esta actividad, serán

pausados.

Page 27: Fragments Android

Hablando del ciclo de vida de un Fragment, no podemos continuar sin dedicarle la parte

que se merece. Es el siguiente, y no te asustes de lo largo que es, si entendiste el de Activity

este es parecido (Gráficos extraídos

de http://developer.android.com/guide/components/fragments.html, modificados y

traducidos por www.Jarroba.com):

Page 29: Fragments Android

Para que conozcas a grandes rasgos que es lo que hace cada una de los métodos, te los

detallo en la siguiente imagen (Referencia

en http://developer.android.com/reference/android/app/Fragment.html):

Page 31: Fragments Android

Nota: Como ya hicieramos con Activity en este otro artículo, dejo un ejemplo simple -sirve

como plantilla o snippet- de un Fragment, con todos sus métodos (decir tiene que no todos

son obligatorios; pero el constructor vacío sí que lo es, lo explico en el siguiente artículo) y

el log puesto para ver por donde va entrando:

public class Fragment_de_ejemplo extends Fragment {

private final String LOG_TAG = "test";

public Fragment_de_ejemplo() {}

@Overridepublic void onAttach (Activity activity) {

super.onAttach(activity);Log.v(LOG_TAG, "onAttach");

}

@Overridepublic void onCreate (Bundle savedInstanceState) {

super.onCreate(savedInstanceState);Log.v(LOG_TAG, "onCreate");

}

@Overridepublic View onCreateView (LayoutInflater inflater, ViewGroup

container, Bundle savedInstanceState) {View rootView = inflater.inflate(R.layout.miLayout,

container, false);Log.v(LOG_TAG, "onCreateView");/* Aquí podemos seleccionar las Views contenidas en el

Layout para trabajar con ellas, por ejemplo con: * TipoView miView = (TipoView)

rootView.findViewById(R.id.miViewXML); */return rootView;

}

@Overridepublic void onActivityCreated (Bundle savedInstanceState) {

super.onActivityCreated(savedInstanceState);Log.v(LOG_TAG, "onActivityCreated");

}

@Override

Page 32: Fragments Android

public void onViewStateRestored (Bundle savedInstanceState) {super.onViewStateRestored(savedInstanceState);Log.v(LOG_TAG, "onViewStateRestored");

}

@Overridepublic void onStart () {

super.onStart();Log.v(LOG_TAG, "onStart");

}

@Overridepublic void onResume () {

super.onResume();Log.v(LOG_TAG, "onResume");

}

@Overridepublic void onPause () {

super.onPause();Log.v(LOG_TAG, "onPause");

}

@Overridepublic void onStop () {

super.onStop();Log.v(LOG_TAG, "onStop");

}

@Overridepublic void onDestroyView () {

super.onDestroyView();Log.v(LOG_TAG, "onDestroyView");

}

@Overridepublic void onDestroy () {

super.onDestroy();Log.v(LOG_TAG, "onDestroy");

}

@Overridepublic void onDetach () {

super.onDetach();Log.v(LOG_TAG, "onDetach");

}

Page 33: Fragments Android

}

 

Hemos creído conveniente dividir este artículo en dos partes por comodidad y

optimización. Este primero más teórico, y una segunda parte que dispone de un ejemplo

completo de como programar con Fragments para Android completamente explicado: en

la siguiente página, a la que puedes ir pinchando aquí mismo.

 

Entradas Relacionadas Error Android – Certificado de depuración caducado

Error Android – El emulador se desconecta y no lanza la aplicación

Preparar las herramientas necesarias para programar en Android

Page 34: Fragments Android

Multitarea en Android

Programar Fragments (fragmentos) en AndroidDespués del primer artículo (ver primera parte aquí), continuamos donde lo dejamos y

preparados para programar. Comenzaré con una descripción ilustrada para que no quepan

dudas, o al menos sean mínimas. Continuemos donde lo dejamos.

Retomamos el ejemplo para diseñar el Smartphone. Veamos que tendremos una Activity

con un listado que representaremos el diseño como:

Distinguiré lo verde como Fragment y lo rojo como Activity. El resto de contenido

pertenece al Layout (texto, imágenes, etc).

Y un al pulsar sobre un elemento de la lista, nos abrirá una segunda Activity que será el

detalle de la entrada:

Page 35: Fragments Android

Ahora un par de preguntas sobre la representación del detalle de la entrada (la imagen

anterior), para ver si has entendido la teoría ¿Cuántos Layouts, es decir, ficheros XML de la

carpeta Layout, componen esta Activity? ¿Y dónde estarían ubicados? La solución es 2

Layouts, y están ubicados donde se muestra en la imagen a continuación (la letra más

pequeña en negrita con rectángulo transparente):

Si no has acertado, no te preocupes, a continuación lo explico. Si has dado con la solución a

la primera, me quito el sombrero, ya que has entendido la complejidad de las vistas de los

Fragments.

Page 36: Fragments Android

La Activity tiene asociado un Layout como siempre, que llamaremos

“Layout_activity_detalle” (engloba todo el rectángulo rojo) –es la asociación Layout con

Activity típica. Y hemos dicho que el Fragment es como si fuera una Activity pero

pequeña, con lo que también tiene asociado un Layout que llamamos

“Layout_fragment_detalle”. Espero que los nombres sean acertados y aclaratorios 

Hemos dejado para el final el primero, porque tiene una particularidad. Si te hago las dos

preguntas anteriores ¿Dónde y cuántos Layouts componen el listado? Podrías decirme que

3, pero no son 3 sino 2. Fíjate en la siguiente imagen un segundo y te lo explico.

La actividad tiene, como acostumbra, su Layout asociado llamado

“Layout_activity_principal_listado”. El Fragment no tiene fichero de Layout asociado ¿No

me acabas de decir que sí tenía Layout, además, es el fichero XML tiene un ListView

claramente? En Fragments el listado es un caso especial. Si has tocado listas un poco más

en profundidad sabrás que si extendemos la clase, en vez de con Activity con ListActivity,

no hace falta declarar un fichero con el ListView. Con Fragments es igual si extendemos de

ListFragment. Usaremos este modo por simplificar código. Y aclarar que en este Fragment

existir existe el Layout con el ListView, pero no lo haremos nosotros directamente. Ya

terminamos con el último Layout, el de cada elemento de la lista llamado

“Layout_elemento_listado”.

Page 37: Fragments Android

Aquí acaba el diseño para dispositivos móviles.

Ahora el de la Tablet en el que me explayaré menos en detalles, pasando a lo importante.

Recordamos que queremos que todo se muestra a la vez (listado y detalle).

Notarás cómo están separados los dos Fragments y que solo necesitamos una Activity.

También te hago las mismas preguntas anteriores sobre los Layouts ¿Cuántos y dónde están

ubicados? Esta es fácil habiéndolo visto anteriormente.

Page 38: Fragments Android

Bueno, existía una trampa al tratarse de una sola Activity. En el diseño de móvil cada

actividad tenía un Layout que contenía un Fragment. En el diseño para Tablets, solo existe

una Activity que tiene asociado un Layout que contiene a los dos Fragments. Es razonable

que sea así; quería dejar clara esta diferencia.

Por cierto, estos van a ser todos los Layouts que vamos a utilizar y ninguno más (todos los

has visto en las imágenes anteriores):

Ya llega el código, tranquilo. Es importante no perderse con tanto archivo y tener las ideas

muy claras.

Previo a revelarte el código XML, hay un simplismo que debes conocer. Se suele pasar por

alto y no debería, es fuente de muchos errores, por consiguiente se obtiene desesperación al

tratar con Fragments (ya nunca más   ). El tema de Fragments estáticos o finales y

dinámicos:

Un Fragment estático o final es aquel que se declara en el fichero XML de la carpeta

Layout directamente. Este Fragment tendrá la cualidad de no ser eliminado o

sustituido por nada -De lo contrario tendremos errores.

Un Fragment dinámico es el que se crea desde código Java y se asocia a un

ViewGroup (Se recomienda el uso de FrameLayout). Éste sí que se podrá eliminar o

sustituir por otro Fragment u otro contenido.

Para entenderlo bien, con el ejemplo del Tablet nos bastará.

Page 39: Fragments Android

Tenemos el listado, que es algo que siempre va a ser igual (podrán aumentar o disminuir los

elementos contenidos en el listado, pero siempre será un listado), por lo que podemos

declarar su fragment en el XML, es estático. Y luego tenemos la parte derecha del detalle,

dependiendo del seleccionado en el listado mostrará el detalle de un pájaro (descripción,

nombre y foto) diferente. Con lo que cada detalle de cada pájaro tendrá su propio Fragment

-si los equiparamos a las hojas de un libro, pasar cada Fragment equivaldría a pasar las

hojas del libro de pájaros- por lo que nos interesa que estos Fragments sean dinámicos. Lo

que nos dibujará el siguiente Layout con un Fragment estático en XML y un FrameLayout

que contendrá a los Fragments dinámicos.

Page 40: Fragments Android

Por lo que el código de layout_activity_principal_dospaneles.xml será:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" android:baselineAligned="false" android:divider="?android:attr/dividerHorizontal" android:orientation="horizontal" android:showDividers="middle" >

<!-- Este fragmento estático contiene al listado --> <fragment android:id="@+id/fragment_listado" android:name="jarroba.invarato.fragmentos.Fragment_Lista" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1"/>

<!-- Este FrameLayout contendrá un fragmento dinámico con el contenido del elemento pulsado del listado --> <FrameLayout android:id="@+id/framelayout_contenedor_detalle" android:layout_width="0dp" android:layout_height="match_parent"

Page 41: Fragments Android

android:layout_weight="3" />

</LinearLayout>

Del mismo modo, para móvil, tenemos por separado el listado con el Fragment estático.

Cuyo código de layout_activity_principal_listado.xml es:

<!--Este fragmento estático contiene al listado--><fragment xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/fragment_listado"    android:name="jarroba.invarato.fragmentos.Fragment_Lista"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:layout_marginLeft="16dp"    android:layout_marginRight="16dp" />

Y por otra parte, de manera separada pero con la misma lógica al que hemos explicado de

la Tablet, tenemos el Fragment dinámico para el detalle. Paralayout_activity_detalle.xml:

<!--Este FrameLayout contendrá un fragmento dinámico con el contenido del elemento pulsado del listado--><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/framelayout_contenedor_detalle"    android:layout_width="match_parent"    android:layout_height="match_parent" />

También tenemos la estructura simple de la vista de cada elemento del listado

en layout_elemento_listado.xml:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center|left" android:orientation="horizontal" >

<ImageView android:id="@+id/imageView_imagen_miniatura"

Page 42: Fragments Android

android:layout_width="80dp" android:layout_height="80dp" android:adjustViewBounds="true" android:scaleType="fitXY" android:contentDescription="Descripción del contenido de la imagen" android:src="@android:drawable/ic_menu_gallery" />

<TextView android:id="@+id/textView_titulo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Large Text" android:textAppearance="?android:attr/textAppearanceLarge" />

</LinearLayout>

Y el del detalle, en layout_fragment_detalle.xml:

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

<TextView android:id="@+id/textView_superior" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Large Text" android:textAppearance="?android:attr/textAppearanceLarge" />

<ImageView android:id="@+id/imageView_imagen" android:layout_width="178dp" android:layout_height="178dp" android:adjustViewBounds="true" android:contentDescription="Descripción del contenido de la imagen" android:scaleType="fitXY" android:src="@android:drawable/ic_menu_gallery" />

<TextView android:id="@+id/textView_inferior"

Page 43: Fragments Android

android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Small Text" android:textAppearance="?android:attr/textAppearanceSmall" />

</LinearLayout>

Ya tenemos las vistas. Antes de entrar con Java, tenemos que hacer una diferenciación más.

Necesitamos diferenciar con qué tipo de pantalla estamos tratando para poder aplicar el

diseño de móvil o de tablet. Esta diferenciación Android la hace muy bien gracias a las

carpetas “values”, que están en la carpeta “res”.

Tendremos 3 carpetas “values” (más información

en http://developer.android.com/guide/practices/screens_support.html#DeclaringTabletLay

outs):

values-large : para pantallas grandes; es decir, para Tablets (Necesario para que

funcione en la versión 3 de Android). Crearemos en esta carpeta un nuevo fichero

XML llamado “layout.xml” (lo explico un poco más adelante).

values-sw600dp : para pantallas con un ancho mayor a 600dp (independientemente de

la rotación de la pantalla); para Tablets de este tamaño o más. Este valor lo podemos

cambiar, pero 600 nos va a funcionar muy bien para nuestros propósitos. Crearemos

en esta carpeta otro nuevo fichero XML también llamado “layout.xml” (lo explico un

poco más adelante).

Page 44: Fragments Android

values : carpeta de valores para el resto de dispositivos que no cumplan las condiciones

anteriores. Lo usaran los Smartphones, o las Tablets pequeñas que no cumplan el

tamaño mínimo que hemos puesto. No tocamos ni añadimos nada en esta carpeta.

Hemos creado dos ficheros llamados “layout.xml”. Ambos contendrán el mismo contenido

que es el siguiente:

<resources> <item name="layout_activity_principal_listado" type="layout">@layout/layout_activity_principal_dospaneles</item></resources>

Esto es un truquito que te cuento para no repetir ficheros: los alias. Podríamos haber creado

varias carpetas “layout” pero tendríamos código duplicado con lo mismo; con el alias

evitamos repetir código innecesariamente. Este alias lo único que hace es sustituir el

contenido de un XML por otro. Así, si la pantalla es pequeña cargará directamente el

XML “layout_activity_principal_listado.xml” en el que solo se muestra un listado; y si es

grande, el contenido de este fichero será sustituido por el contenido del fichero

“layout_acitivity_principal_dospaneles.xml” que dispone un Fragment a la izquierda para

el listado y un FrameLayout a la derecha para el Fragment dinámico del detalle. Para mayor

claridad, echa un vistazo a la imagen siguiente:

Ahora ya podemos meternos de lleno con el código Java. Primero te voy a mostrar la

estructura completa del proyecto, deberás crear las clases Java (que detallo a continuación)

y añadir las imágenes. De la carpeta “res” ya está todo terminado. De la siguiente imagen

solo hemos creado lo que está en rojo para hacer la aplicación.

Page 45: Fragments Android

Empecemos por el código primero en ejecutarse, que es Activity_Principal.java. Esta es

una Activity, pero para tratar con Fragments necesitamos un tipo de Activity especial

llamada “FragmentActivity” de la que extenderemos la clase. Pero antes vamos a explicar

en esencia lo que hace, que son dos cosas y luego muestro el código:

Diferenciar la cantidad de elementos que maneja para cada tipo de dispositivo : En el

“onCreate” le asociaremos el Layout “layout_activity_principal_listado.xml” –

recordemos que su contenido tendrá únicamente un listado, o un listado y un detalle,

dependiendo del tamaño de la pantalla. Para saber si estamos trabajando con dos

Fragments en la misma Acitivity, es tan sencillo como preguntar si existe el detalle,

sino existe estaremos trabajando con un solo Fragment que será sin lugar a dudas el

listado.

Page 46: Fragments Android

Comunicar a los Fragments : Programaremos los Fragments de manera completamente

modular; es decir, un Fragment no se comunica con otro directamente sino a través de

interfaces. Funciona del siguiente modo (para mayor claridad, echar un vistazo a la

imagen inferior que es para el caso del Tablet; para el Smartphone sería igual, pero

pasándole el dato de una Activity a la otra, y ésta ya se lo comunicaría al Fragment

con el detalle):

 1)      Se pulsa sobre un elemento del Fragment que contiene al listado. A través de un

método de Callback “onEntradaSelecionada” (esto es programación por eventos, en este

artículo explicamos su comportamiento, pero resumimos al explicar el código por no ser el

tema principal del artículo) comunica el id de la entrada seleccionada a la Activity que lo

engloba (de ahí que implemente a “Fragment_Lista.Callbacks”).

2)      La Activity recoge el dato. Si:

Es Tablet: crea un nuevo Fragment con el detalle, le envía el id para que cargue el

contenido apropiado y lo coloca en el FrameLayout, remplazando cualquier otro

Fragment con el detalle que pueda existir.

Es Smartphone: Crea una nueva Activity al que le envía el id. La nueva Activity

cargará el Fragment con el detalle sobre sí misma.

Page 47: Fragments Android

3)      Se muestra el Fragment con el detalle al usuario.

Y este es el código correspondiente de esta clase:

public class Activity_Principal extends FragmentActivity implements Fragment_Lista.Callbacks {

private boolean dosFragmentos;

@Overrideprotected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.layout_activity_principal_listado);

if (findViewById(R.id.framelayout_contenedor_detalle) != null) {

dosFragmentos = true;}

}

@Overridepublic void onEntradaSelecionada(String id) {

if (dosFragmentos) {Bundle arguments = new Bundle();

arguments.putString(Fragment_Detalle.ARG_ID_ENTRADA_SELECIONADA, id);Fragment_Detalle fragment = new

Fragment_Detalle();fragment.setArguments(arguments);

getSupportFragmentManager().beginTransaction().replace(R.id.framelayout_contenedor_detalle, fragment).commit();

} else {Intent detailIntent = new Intent(this,

Activity_Detalle.class);

detailIntent.putExtra(Fragment_Detalle.ARG_ID_ENTRADA_SELECIONADA, id);

startActivity(detailIntent);

Page 48: Fragments Android

}}

}

Comencemos con un Fragment normal, que es el del detalle. Para ello escribimos el código

de Fragment_Detalle.java. Por ser un Fragment normal y corriente extenderá de

Fragment. Explico el contenido de los métodos:

Contructor: en los Fragments siempre vació para poder instanciarlo.

onCreate: recoge el contenido de la entrada.

onCreateView: aquí se maneja la vista, es decir crea y se asocia con el Layout (en

Activity se hace en el onCreate con el método “setContentView”; pero esto es un

Fragment, funciona de manera diferente y es así como se hace, con inflate(). Averigua

todo sobre inflate() en este otro artículo).

public class Fragment_Detalle extends Fragment {

public static final String ARG_ID_ENTRADA_SELECIONADA = "item_id";

private Lista_contenido.Lista_entrada mItem;

public Fragment_Detalle() {}

@Overridepublic void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

if (getArguments().containsKey(ARG_ID_ENTRADA_SELECIONADA)) {

//Cargamos el contenido de la entrada con cierto ID seleccionado en la lista. Se recomiendo usar un Loader para cargar el contenido

mItem = Lista_contenido.ENTRADAS_LISTA_HASHMAP.get(getArguments().getString(ARG_ID_ENTRADA_SELECIONADA));

}}

@Override

Page 49: Fragments Android

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

View rootView = inflater.inflate(R.layout.layout_fragment_detalle, container, false);

//Mostramos el contenido al usuarioif (mItem != null) {

((TextView) rootView.findViewById(R.id.textView_superior)).setText(mItem.textoEncima);

((TextView) rootView.findViewById(R.id.textView_inferior)).setText(mItem.textoDebajo);

((ImageView) rootView.findViewById(R.id.imageView_imagen)).setImageResource(mItem.idImagen);

}

return rootView;}

}

¿Exporto android.support.v4.app o android.app?

A la hora de exportar la librería de Fragment (por ejemplo, aunque también podría darse el

caso con FragmentManager, FragmentTransaction, etc). Eclipse te dará a elegir entre las

dos opciones dos: “android.app.Fragment” -solo funciona a partir de Android 3.0 ó nivel

11- o “android.support.v4.app.Fragment” -para cualquier versión de Android.

Nuestra recomendación es que uses “android.support.v4.app.*“.

Nunca mezcles en el mismo proyecto las dos librerías a importar (por ejemplo, que hayas

importado “android.support.v4.app.FragmentManager” y “android.app.Fragment” ), pues lo

más probable es que te encuentres con alguna variante del siguiente error en tiempo de

ejecución:

The method replace(…) in the type FragmentTransaction is not applicable for the

arguments (…)

La solución ya sabes que debes hacer: buscar y cambiar el archivo importado que no

cumple lo anterior descrito y cambiarlo por el otro (en el anterior ejemplo, nos damos

cuenta y lo cambiamos a “android.support.v4.app.FragmentManager”

y “android.support.v4.app.Fragment” ).

Page 50: Fragments Android

Entendido el Fragment normal, ahora uno especial. Implementaremos el del listado

en Fragment_Lista.java. Esta clase es un Fragment, pero es un Fragment especial que

extenderá de ListFragment, que es el listado preparado para ser Fragment (Aquí no

asociamos ningún Layout, pues al extender de ListFragment ya lo trae puesto). Callaback:

Tendrá implementado el Callback que notifique a la Activity de que elemento del listado se

haya pulsado. Explico uno a uno los métodos aquí usados:

Constructor: Seguro que te fijarás en el constructor vacío, es necesario para el correcto

funcionamiento de los Fragments, así que ni se te ocurra quitarlo :D.

onCreate: como ya hicimos en el artículo de listado asignamos el contenido de cada

entrada a cada elemento del listado.

onAttach: Simplemente asegura que el desarrollador haya implementado el Callback,

en la clase que use a este Fragment.

onDetach: Limpia el Callback.

onListItemClick: al extender de ListFragment, es necesario sobrescribir esta clase, que

es la que escucha la pulsación sobre un elemento de la lista. Y así usaremos el

Callback para notificar a la Actividad del id pulsado.

public class Fragment_Lista extends ListFragment {

private Callbacks mCallbacks = CallbacksVacios;

public interface Callbacks {public void onEntradaSelecionada(String id);

}

private static Callbacks CallbacksVacios = new Callbacks() {@Overridepublic void onEntradaSelecionada(String id) {}

};

public Fragment_Lista() {}

@Override

Page 51: Fragments Android

public void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);

setListAdapter(new Lista_adaptador(getActivity(), R.layout.layout_elemento_listado, Lista_contenido.ENTRADAS_LISTA){

@Overridepublic void onEntrada(Object entrada, View view)

{ if (entrada != null) { TextView texto_superior_entrada =

(TextView) view.findViewById(R.id.textView_titulo); if (texto_superior_entrada != null)

texto_superior_entrada.setText(((Lista_contenido.Lista_entrada) entrada).textoEncima);

ImageView imagen_entrada = (ImageView) view.findViewById(R.id.imageView_imagen_miniatura);

if (imagen_entrada != null)

imagen_entrada.setImageResource(((Lista_contenido.Lista_entrada) entrada).idImagen);

}}

});

}

@Overridepublic void onAttach(Activity activity) {

super.onAttach(activity);

if (!(activity instanceof Callbacks)) {throw new IllegalStateException("Error: La

actividad debe implementar el callback del fragmento");}

mCallbacks = (Callbacks) activity;}

@Overridepublic void onDetach() {

super.onDetach();mCallbacks = CallbacksVacios;

Page 52: Fragments Android

}

@Overridepublic void onListItemClick(ListView listView, View view, int

posicion, long id) {super.onListItemClick(listView, view, posicion, id);

mCallbacks.onEntradaSelecionada(Lista_contenido.ENTRADAS_LISTA.get(posicion).id);

}

}

Hasta aquí ya tenemos lo que hace funcionar la estructura de los Tablets.

Continuemos para el caso de Smartphone. Necesitamos un segundo Activity para contener

al Fragment del detalle. Llamaremos Activity_Detalle. ¿Recuerdas que heredaba? A

Activity no, estamos trabajando con Fragments, extiende de FragmentActivity. Solo tiene

un método onCreate que comprobará si ya ha creado al Fragment del detalle para cargarlo,

o no para crearlo. La creación del Fragment del detalle es igual que en el caso de

Activity_Principal.java.

public class Activity_Detalle extends FragmentActivity {

@Overrideprotected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);setContentView(R.layout.layout_activity_detalle);

// Comprobamos que previamente no hayamos entrado en esta actividad (por ejemplo, al rotar el dispositivo). Si es así se añade el fragmento al contenedor

if (savedInstanceState == null) {// Crea el fragmento del detalle de la entrada y

lo añade a la actividadBundle arguments = new Bundle();

arguments.putString(Fragment_Detalle.ARG_ID_ENTRADA_SELECIONADA, getIntent().getStringExtra(Fragment_Detalle.ARG_ID_ENTRADA_SELECIONADA));

Fragment_Detalle fragment = new Fragment_Detalle();

fragment.setArguments(arguments);

Page 53: Fragments Android

getSupportFragmentManager().beginTransaction().add(R.id.framelayout_contenedor_detalle, fragment).commit();

}}

}

Ya solo nos queda comentar Lista_adaptador.java que encontrarás en el artículo del

listado.

Y Lista_contenido.java, que contiene a la clase Lista_entrada del artículo del listado. Lo

hemos hecho así por simplificar el código. Ya que si los datos no van a cambiar, así es muy

fácil hacer una estructura estática que de elementos que siempre esté visible por todas las

clases sea el momento que sea de la ejecución. Simplemente guardamos unos listados con

el contenido de las entradas estáticamente, esto se hace al inicio de la aplicación.

public class Lista_contenido {

/** * Donde se guardan las entradas de la lista. */public static ArrayList<Lista_entrada> ENTRADAS_LISTA = new

ArrayList<Lista_entrada>();

/** * Donde se asigna el identificador a cada entrada de la lista */public static Map<String, Lista_entrada>

ENTRADAS_LISTA_HASHMAP = new HashMap<String, Lista_entrada>();

/** * Creamos estáticamente las entradas */static {

aniadirEntrada(new Lista_entrada("0", R.drawable.im_buho, "BUHO", "Búho es el nombre común..."));

aniadirEntrada(new Lista_entrada("1", R.drawable.im_colibri, "COLIBRÍ", "Los troquilinos (Trochilinae) son..."));

aniadirEntrada(new Lista_entrada("2", R.drawable.im_cuervo, "CUERVO", "El cuervo común (Corvus corax) es ..."));

Page 54: Fragments Android

aniadirEntrada(new Lista_entrada("3", R.drawable.im_flamenco, "FLAMENCO", "Los fenicopteriformes..."));

aniadirEntrada(new Lista_entrada("4", R.drawable.im_kiwi, "KIWI", "Los kiwis (Apterix, gr. 'sin alas') son..."));

aniadirEntrada(new Lista_entrada("5", R.drawable.im_loro, "LORO", "Las Psitácidas (Psittacidae) son..."));

aniadirEntrada(new Lista_entrada("6", R.drawable.im_pavo, "PAVO", "Pavo es un género de aves..."));

}

/** Añade una entrada a la lista * @param entrada Elemento que añadimos a la lista */private static void aniadirEntrada(Lista_entrada entrada) {

ENTRADAS_LISTA.add(entrada);ENTRADAS_LISTA_HASHMAP.put(entrada.id, entrada);

}

/** * Representa una entrada del contenido de la lista */public static class Lista_entrada {

public String id;public int idImagen; public String textoEncima; public String textoDebajo;

public Lista_entrada (String id, int idImagen, String textoEncima, String textoDebajo) {

this.id = id; this.idImagen = idImagen; this.textoEncima = textoEncima; this.textoDebajo = textoDebajo; }

}

}

Y nos falta añadir al AndroidManifest.xml la Actividad que no es la principal, es decir

Activity_Detalle. Este es mi AndroidManifest:

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="jarroba.invarato.fragmentos"

Page 55: Fragments Android

android:versionCode="1" android:versionName="1.0" >

<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="18" />

<application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="jarroba.invarato.fragmentos.Activity_Principal" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="jarroba.invarato.fragmentos.Activity_Detalle" android:label="@string/titulo_detalle_entrada" android:parentActivityName=".Activity_Principal" > <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".Activity_Detalle" /> </activity> </application>

</manifest>

Dejo aquí el código funcional para descargar, bastante comentado para aclarar todavía más

las posibles dudas que queden en el tintero. Descárgalo aquí: Fragmentos

Te recomiendo que lo pruebes tanto con un emulador –o dispositivo físico- de Smartphone,

como de Tablet, para que veas como queda en cada dispositivo.

Nota: si quieres ahorrarte un montón de código aprovechando ahora que entiendes la

arquitectura de los Fragments. Al crear un nuevo proyecto Android puedes seleccionar

Page 56: Fragments Android

“Master/Detail Flow” en la página “Create Activity”. Lo único que, como usa ActionBar,

requiere de mínimo Android 3.0 (API 11 o Honeycomb).

Y con esto acabamos los fragmentos. La estructura mental de Fragment tiene cosas que ver

y cosas que son completamente diferentes a la de Activity. Una vez entendidas te aseguro

que son muy fáciles

Page 57: Fragments Android

Mucho mejor. Al seleccionar un elemento del listado, se mostrará a la derecha el detalle del

mismo. Mejor aprovechamiento de la pantalla y mucho más cómodo (no hay que volver

atrás, se puede seleccionar otro inmediatamente).

Cabe aclarar que si mantenemos este diseño para un Smartphone, la comodidad del Tablet

se pierde, pues no se podría leer bien la descripción del pájaro al aparecer muy pequeña y el

listado sería difícil de pulsar al ser muy estrecho. Por esto tiene sentido que en el móvil

tenga un diseño y funcionalidades diferentes al del Tablet.

Por cuestiones de espacio nos limitaremos al ejemplo de Smartphone y Tablet. Pero

imagina que queremos hacer esta misma aplicación para un reloj de unas 2 pulgadas, puede

que en vez de poner un listado pusiéramos un grid con las imágenes de los pájaros para que

el dedo pudiera pulsar mejor los elementos, y la descripción aparecería aparte sin foto. O en

una televisión de 40 pulgadas imagina la cantidad de cosas que entrarían; podríamos, por

ejemplo, poner al lado de la descripción un vídeo del pájaro que vuela y debajo la ficha

técnica, todo en la misma pantalla. Esto quiere decir que nos ajustamos al tamaño para

realizar el diseño y la funcionalidad.

Por suerte para nosotros, no tenemos que hacer un diseño para cada pulgada de cada

pantalla y para cada forma. Nos atendemos a la limitación por las modas. Pongo todos los

dispositivos existentes con Android que pudiera ser bueno prestar atención al diseño y a la

funcionalidad. Siempre claro, dependiendo del alcance de nuestra aplicación (las pulgadas

y formas son orientativas):

Page 58: Fragments Android

Reloj (1 a 2 pulgadas, cuadrado)

Smarphone o dispositivo GPS (3 a 5 pulgadas, rectangular alargado)

Phablet (5 a 7 pulgadas, rectangular alargado)

Tablet (7 a 12 pulgadas, proporción aurea)

Pantalla de un ordenador (14 a 30 pulgadas, rectangular alargado o proporción aurea)

Televisión (30 a 80 pulgadas, proporción aurea)

Proyector (80 a 300 pulgadas, proporción aurea)

Dependerá del desarrollador saber a qué diseños y que funcionalidades atender en cada

momento.

¿Y se puede hacer esto en la misma aplicación? o ¿Se desarrolla una App para cada tamaño

de pantalla? La respuesta es, siempre desde la perspectiva del “buen programador”, se debe

y se puede hacer en la misma aplicación. Los motivos son muy simples, que seguro estás

harto de escuchar: modularidad y reusabilidad. Esto se logra con los Fragments o

Fragmentos.

Si conoces el comportamiento de las Activities (ver en http://jarroba.com/activity-entender-

y-usar-una-actividad/), advertirás que tienen un ciclo de vida y están asociadas a una vista

(Layout en XML).

Un Fragment es un trozo (o un fragmento, si apelamos al mismo nombre que lo viste) de

una actividad. Un Fragment tiene su propio Layout y su propio ciclo de vida.

Puedes apreciar, que programándolo una vez, puedes rehusar el código para que se adapte a

diferentes tamaños de pantalla con la disposición de los elementos que quieras.

Por lo que adivinarás que un Fragment siempre ha de estar en una Activity, no puede existir

independiente. Y el ciclo de vida de la Activity contenedora afectará al del Fragment; por

ejemplo, si se pausa la Activity el o los Fragments, que estén en esta actividad, serán

pausados.

Page 59: Fragments Android

Hablando del ciclo de vida de un Fragment, no podemos continuar sin dedicarle la parte

que se merece. Es el siguiente, y no te asustes de lo largo que es, si entendiste el de Activity

este es parecido (Gráficos extraídos

de http://developer.android.com/guide/components/fragments.html, modificados y

traducidos por www.Jarroba.com):

Page 61: Fragments Android

Para que conozcas a grandes rasgos que es lo que hace cada una de los métodos, te los

detallo en la siguiente imagen (Referencia

en http://developer.android.com/reference/android/app/Fragment.html):

Page 63: Fragments Android

Nota: Como ya hicieramos con Activity en este otro artículo, dejo un ejemplo simple -sirve

como plantilla o snippet- de un Fragment, con todos sus métodos (decir tiene que no todos

son obligatorios; pero el constructor vacío sí que lo es, lo explico en el siguiente artículo) y

el log puesto para ver por donde va entrando:

public class Fragment_de_ejemplo extends Fragment {

private final String LOG_TAG = "test";

public Fragment_de_ejemplo() {}

@Overridepublic void onAttach (Activity activity) {

super.onAttach(activity);Log.v(LOG_TAG, "onAttach");

}

@Overridepublic void onCreate (Bundle savedInstanceState) {

super.onCreate(savedInstanceState);Log.v(LOG_TAG, "onCreate");

}

@Overridepublic View onCreateView (LayoutInflater inflater, ViewGroup

container, Bundle savedInstanceState) {View rootView = inflater.inflate(R.layout.miLayout,

container, false);Log.v(LOG_TAG, "onCreateView");/* Aquí podemos seleccionar las Views contenidas en el

Layout para trabajar con ellas, por ejemplo con: * TipoView miView = (TipoView)

rootView.findViewById(R.id.miViewXML); */return rootView;

}

@Overridepublic void onActivityCreated (Bundle savedInstanceState) {

super.onActivityCreated(savedInstanceState);Log.v(LOG_TAG, "onActivityCreated");

}

@Override

Page 64: Fragments Android

public void onViewStateRestored (Bundle savedInstanceState) {super.onViewStateRestored(savedInstanceState);Log.v(LOG_TAG, "onViewStateRestored");

}

@Overridepublic void onStart () {

super.onStart();Log.v(LOG_TAG, "onStart");

}

@Overridepublic void onResume () {

super.onResume();Log.v(LOG_TAG, "onResume");

}

@Overridepublic void onPause () {

super.onPause();Log.v(LOG_TAG, "onPause");

}

@Overridepublic void onStop () {

super.onStop();Log.v(LOG_TAG, "onStop");

}

@Overridepublic void onDestroyView () {

super.onDestroyView();Log.v(LOG_TAG, "onDestroyView");

}

@Overridepublic void onDestroy () {

super.onDestroy();Log.v(LOG_TAG, "onDestroy");

}

@Overridepublic void onDetach () {

super.onDetach();Log.v(LOG_TAG, "onDetach");

}

Page 65: Fragments Android

}

 

Hemos creído conveniente dividir este artículo en dos partes por comodidad y

optimización. Este primero más teórico, y una segunda parte que dispone de un ejemplo

completo de como programar con Fragments para Android completamente explicado: en

la siguiente página, a la que puedes ir pinchando aquí mismo.

 

Programar Fragments (fragmentos) en AndroidDespués del primer artículo (ver primera parte aquí), continuamos donde lo dejamos y

preparados para programar. Comenzaré con una descripción ilustrada para que no quepan

dudas, o al menos sean mínimas. Continuemos donde lo dejamos.

Retomamos el ejemplo para diseñar el Smartphone. Veamos que tendremos una Activity

con un listado que representaremos el diseño como:

Page 66: Fragments Android

Distinguiré lo verde como Fragment y lo rojo como Activity. El resto de contenido

pertenece al Layout (texto, imágenes, etc).

Y un al pulsar sobre un elemento de la lista, nos abrirá una segunda Activity que será el

detalle de la entrada:

Ahora un par de preguntas sobre la representación del detalle de la entrada (la imagen

anterior), para ver si has entendido la teoría ¿Cuántos Layouts, es decir, ficheros XML de la

carpeta Layout, componen esta Activity? ¿Y dónde estarían ubicados? La solución es 2

Layouts, y están ubicados donde se muestra en la imagen a continuación (la letra más

pequeña en negrita con rectángulo transparente):

Page 67: Fragments Android

Si no has acertado, no te preocupes, a continuación lo explico. Si has dado con la solución a

la primera, me quito el sombrero, ya que has entendido la complejidad de las vistas de los

Fragments.

La Activity tiene asociado un Layout como siempre, que llamaremos

“Layout_activity_detalle” (engloba todo el rectángulo rojo) –es la asociación Layout con

Activity típica. Y hemos dicho que el Fragment es como si fuera una Activity pero

pequeña, con lo que también tiene asociado un Layout que llamamos

“Layout_fragment_detalle”. Espero que los nombres sean acertados y aclaratorios 

Hemos dejado para el final el primero, porque tiene una particularidad. Si te hago las dos

preguntas anteriores ¿Dónde y cuántos Layouts componen el listado? Podrías decirme que

3, pero no son 3 sino 2. Fíjate en la siguiente imagen un segundo y te lo explico.

Page 68: Fragments Android

La actividad tiene, como acostumbra, su Layout asociado llamado

“Layout_activity_principal_listado”. El Fragment no tiene fichero de Layout asociado ¿No

me acabas de decir que sí tenía Layout, además, es el fichero XML tiene un ListView

claramente? En Fragments el listado es un caso especial. Si has tocado listas un poco más

en profundidad sabrás que si extendemos la clase, en vez de con Activity con ListActivity,

no hace falta declarar un fichero con el ListView. Con Fragments es igual si extendemos de

ListFragment. Usaremos este modo por simplificar código. Y aclarar que en este Fragment

existir existe el Layout con el ListView, pero no lo haremos nosotros directamente. Ya

terminamos con el último Layout, el de cada elemento de la lista llamado

“Layout_elemento_listado”.

Aquí acaba el diseño para dispositivos móviles.

Ahora el de la Tablet en el que me explayaré menos en detalles, pasando a lo importante.

Recordamos que queremos que todo se muestra a la vez (listado y detalle).

Page 69: Fragments Android

Notarás cómo están separados los dos Fragments y que solo necesitamos una Activity.

También te hago las mismas preguntas anteriores sobre los Layouts ¿Cuántos y dónde están

ubicados? Esta es fácil habiéndolo visto anteriormente.

Bueno, existía una trampa al tratarse de una sola Activity. En el diseño de móvil cada

actividad tenía un Layout que contenía un Fragment. En el diseño para Tablets, solo existe

una Activity que tiene asociado un Layout que contiene a los dos Fragments. Es razonable

que sea así; quería dejar clara esta diferencia.

Page 70: Fragments Android

Por cierto, estos van a ser todos los Layouts que vamos a utilizar y ninguno más (todos los

has visto en las imágenes anteriores):

Ya llega el código, tranquilo. Es importante no perderse con tanto archivo y tener las ideas

muy claras.

Previo a revelarte el código XML, hay un simplismo que debes conocer. Se suele pasar por

alto y no debería, es fuente de muchos errores, por consiguiente se obtiene desesperación al

tratar con Fragments (ya nunca más   ). El tema de Fragments estáticos o finales y

dinámicos:

Un Fragment estático o final es aquel que se declara en el fichero XML de la carpeta

Layout directamente. Este Fragment tendrá la cualidad de no ser eliminado o

sustituido por nada -De lo contrario tendremos errores.

Un Fragment dinámico es el que se crea desde código Java y se asocia a un

ViewGroup (Se recomienda el uso de FrameLayout). Éste sí que se podrá eliminar o

sustituir por otro Fragment u otro contenido.

Para entenderlo bien, con el ejemplo del Tablet nos bastará.

Page 71: Fragments Android

Tenemos el listado, que es algo que siempre va a ser igual (podrán aumentar o disminuir los

elementos contenidos en el listado, pero siempre será un listado), por lo que podemos

declarar su fragment en el XML, es estático. Y luego tenemos la parte derecha del detalle,

dependiendo del seleccionado en el listado mostrará el detalle de un pájaro (descripción,

nombre y foto) diferente. Con lo que cada detalle de cada pájaro tendrá su propio Fragment

-si los equiparamos a las hojas de un libro, pasar cada Fragment equivaldría a pasar las

hojas del libro de pájaros- por lo que nos interesa que estos Fragments sean dinámicos. Lo

que nos dibujará el siguiente Layout con un Fragment estático en XML y un FrameLayout

que contendrá a los Fragments dinámicos.

Page 72: Fragments Android

Por lo que el código de layout_activity_principal_dospaneles.xml será:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" android:baselineAligned="false" android:divider="?android:attr/dividerHorizontal" android:orientation="horizontal" android:showDividers="middle" >

<!-- Este fragmento estático contiene al listado --> <fragment android:id="@+id/fragment_listado" android:name="jarroba.invarato.fragmentos.Fragment_Lista" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1"/>

<!-- Este FrameLayout contendrá un fragmento dinámico con el contenido del elemento pulsado del listado --> <FrameLayout android:id="@+id/framelayout_contenedor_detalle" android:layout_width="0dp" android:layout_height="match_parent"

Page 73: Fragments Android

android:layout_weight="3" />

</LinearLayout>

Del mismo modo, para móvil, tenemos por separado el listado con el Fragment estático.

Cuyo código de layout_activity_principal_listado.xml es:

<!--Este fragmento estático contiene al listado--><fragment xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/fragment_listado"    android:name="jarroba.invarato.fragmentos.Fragment_Lista"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:layout_marginLeft="16dp"    android:layout_marginRight="16dp" />

Y por otra parte, de manera separada pero con la misma lógica al que hemos explicado de

la Tablet, tenemos el Fragment dinámico para el detalle. Paralayout_activity_detalle.xml:

<!--Este FrameLayout contendrá un fragmento dinámico con el contenido del elemento pulsado del listado--><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/framelayout_contenedor_detalle"    android:layout_width="match_parent"    android:layout_height="match_parent" />

También tenemos la estructura simple de la vista de cada elemento del listado

en layout_elemento_listado.xml:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center|left" android:orientation="horizontal" >

<ImageView android:id="@+id/imageView_imagen_miniatura"

Page 74: Fragments Android

android:layout_width="80dp" android:layout_height="80dp" android:adjustViewBounds="true" android:scaleType="fitXY" android:contentDescription="Descripción del contenido de la imagen" android:src="@android:drawable/ic_menu_gallery" />

<TextView android:id="@+id/textView_titulo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Large Text" android:textAppearance="?android:attr/textAppearanceLarge" />

</LinearLayout>

Y el del detalle, en layout_fragment_detalle.xml:

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

<TextView android:id="@+id/textView_superior" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Large Text" android:textAppearance="?android:attr/textAppearanceLarge" />

<ImageView android:id="@+id/imageView_imagen" android:layout_width="178dp" android:layout_height="178dp" android:adjustViewBounds="true" android:contentDescription="Descripción del contenido de la imagen" android:scaleType="fitXY" android:src="@android:drawable/ic_menu_gallery" />

<TextView android:id="@+id/textView_inferior"

Page 75: Fragments Android

android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Small Text" android:textAppearance="?android:attr/textAppearanceSmall" />

</LinearLayout>

Ya tenemos las vistas. Antes de entrar con Java, tenemos que hacer una diferenciación más.

Necesitamos diferenciar con qué tipo de pantalla estamos tratando para poder aplicar el

diseño de móvil o de tablet. Esta diferenciación Android la hace muy bien gracias a las

carpetas “values”, que están en la carpeta “res”.

Tendremos 3 carpetas “values” (más información

en http://developer.android.com/guide/practices/screens_support.html#DeclaringTabletLay

outs):

values-large : para pantallas grandes; es decir, para Tablets (Necesario para que

funcione en la versión 3 de Android). Crearemos en esta carpeta un nuevo fichero

XML llamado “layout.xml” (lo explico un poco más adelante).

values-sw600dp : para pantallas con un ancho mayor a 600dp (independientemente de

la rotación de la pantalla); para Tablets de este tamaño o más. Este valor lo podemos

cambiar, pero 600 nos va a funcionar muy bien para nuestros propósitos. Crearemos

en esta carpeta otro nuevo fichero XML también llamado “layout.xml” (lo explico un

poco más adelante).

Page 76: Fragments Android

values : carpeta de valores para el resto de dispositivos que no cumplan las condiciones

anteriores. Lo usaran los Smartphones, o las Tablets pequeñas que no cumplan el

tamaño mínimo que hemos puesto. No tocamos ni añadimos nada en esta carpeta.

Hemos creado dos ficheros llamados “layout.xml”. Ambos contendrán el mismo contenido

que es el siguiente:

<resources> <item name="layout_activity_principal_listado" type="layout">@layout/layout_activity_principal_dospaneles</item></resources>

Esto es un truquito que te cuento para no repetir ficheros: los alias. Podríamos haber creado

varias carpetas “layout” pero tendríamos código duplicado con lo mismo; con el alias

evitamos repetir código innecesariamente. Este alias lo único que hace es sustituir el

contenido de un XML por otro. Así, si la pantalla es pequeña cargará directamente el

XML “layout_activity_principal_listado.xml” en el que solo se muestra un listado; y si es

grande, el contenido de este fichero será sustituido por el contenido del fichero

“layout_acitivity_principal_dospaneles.xml” que dispone un Fragment a la izquierda para

el listado y un FrameLayout a la derecha para el Fragment dinámico del detalle. Para mayor

claridad, echa un vistazo a la imagen siguiente:

Ahora ya podemos meternos de lleno con el código Java. Primero te voy a mostrar la

estructura completa del proyecto, deberás crear las clases Java (que detallo a continuación)

y añadir las imágenes. De la carpeta “res” ya está todo terminado. De la siguiente imagen

solo hemos creado lo que está en rojo para hacer la aplicación.

Page 77: Fragments Android

Empecemos por el código primero en ejecutarse, que es Activity_Principal.java. Esta es

una Activity, pero para tratar con Fragments necesitamos un tipo de Activity especial

llamada “FragmentActivity” de la que extenderemos la clase. Pero antes vamos a explicar

en esencia lo que hace, que son dos cosas y luego muestro el código:

Diferenciar la cantidad de elementos que maneja para cada tipo de dispositivo : En el

“onCreate” le asociaremos el Layout “layout_activity_principal_listado.xml” –

recordemos que su contenido tendrá únicamente un listado, o un listado y un detalle,

dependiendo del tamaño de la pantalla. Para saber si estamos trabajando con dos

Fragments en la misma Acitivity, es tan sencillo como preguntar si existe el detalle,

sino existe estaremos trabajando con un solo Fragment que será sin lugar a dudas el

listado.

Page 78: Fragments Android

Comunicar a los Fragments : Programaremos los Fragments de manera completamente

modular; es decir, un Fragment no se comunica con otro directamente sino a través de

interfaces. Funciona del siguiente modo (para mayor claridad, echar un vistazo a la

imagen inferior que es para el caso del Tablet; para el Smartphone sería igual, pero

pasándole el dato de una Activity a la otra, y ésta ya se lo comunicaría al Fragment

con el detalle):

 1)      Se pulsa sobre un elemento del Fragment que contiene al listado. A través de un

método de Callback “onEntradaSelecionada” (esto es programación por eventos, en este

artículo explicamos su comportamiento, pero resumimos al explicar el código por no ser el

tema principal del artículo) comunica el id de la entrada seleccionada a la Activity que lo

engloba (de ahí que implemente a “Fragment_Lista.Callbacks”).

2)      La Activity recoge el dato. Si:

Es Tablet: crea un nuevo Fragment con el detalle, le envía el id para que cargue el

contenido apropiado y lo coloca en el FrameLayout, remplazando cualquier otro

Fragment con el detalle que pueda existir.

Es Smartphone: Crea una nueva Activity al que le envía el id. La nueva Activity

cargará el Fragment con el detalle sobre sí misma.

Page 79: Fragments Android

3)      Se muestra el Fragment con el detalle al usuario.

Y este es el código correspondiente de esta clase:

public class Activity_Principal extends FragmentActivity implements Fragment_Lista.Callbacks {

private boolean dosFragmentos;

@Overrideprotected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.layout_activity_principal_listado);

if (findViewById(R.id.framelayout_contenedor_detalle) != null) {

dosFragmentos = true;}

}

@Overridepublic void onEntradaSelecionada(String id) {

if (dosFragmentos) {Bundle arguments = new Bundle();

arguments.putString(Fragment_Detalle.ARG_ID_ENTRADA_SELECIONADA, id);Fragment_Detalle fragment = new

Fragment_Detalle();fragment.setArguments(arguments);

getSupportFragmentManager().beginTransaction().replace(R.id.framelayout_contenedor_detalle, fragment).commit();

} else {Intent detailIntent = new Intent(this,

Activity_Detalle.class);

detailIntent.putExtra(Fragment_Detalle.ARG_ID_ENTRADA_SELECIONADA, id);

startActivity(detailIntent);

Page 80: Fragments Android

}}

}

Comencemos con un Fragment normal, que es el del detalle. Para ello escribimos el código

de Fragment_Detalle.java. Por ser un Fragment normal y corriente extenderá de

Fragment. Explico el contenido de los métodos:

Contructor: en los Fragments siempre vació para poder instanciarlo.

onCreate: recoge el contenido de la entrada.

onCreateView: aquí se maneja la vista, es decir crea y se asocia con el Layout (en

Activity se hace en el onCreate con el método “setContentView”; pero esto es un

Fragment, funciona de manera diferente y es así como se hace, con inflate(). Averigua

todo sobre inflate() en este otro artículo).

public class Fragment_Detalle extends Fragment {

public static final String ARG_ID_ENTRADA_SELECIONADA = "item_id";

private Lista_contenido.Lista_entrada mItem;

public Fragment_Detalle() {}

@Overridepublic void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

if (getArguments().containsKey(ARG_ID_ENTRADA_SELECIONADA)) {

//Cargamos el contenido de la entrada con cierto ID seleccionado en la lista. Se recomiendo usar un Loader para cargar el contenido

mItem = Lista_contenido.ENTRADAS_LISTA_HASHMAP.get(getArguments().getString(ARG_ID_ENTRADA_SELECIONADA));

}}

@Override

Page 81: Fragments Android

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

View rootView = inflater.inflate(R.layout.layout_fragment_detalle, container, false);

//Mostramos el contenido al usuarioif (mItem != null) {

((TextView) rootView.findViewById(R.id.textView_superior)).setText(mItem.textoEncima);

((TextView) rootView.findViewById(R.id.textView_inferior)).setText(mItem.textoDebajo);

((ImageView) rootView.findViewById(R.id.imageView_imagen)).setImageResource(mItem.idImagen);

}

return rootView;}

}

¿Exporto android.support.v4.app o android.app?

A la hora de exportar la librería de Fragment (por ejemplo, aunque también podría darse el

caso con FragmentManager, FragmentTransaction, etc). Eclipse te dará a elegir entre las

dos opciones dos: “android.app.Fragment” -solo funciona a partir de Android 3.0 ó nivel

11- o “android.support.v4.app.Fragment” -para cualquier versión de Android.

Nuestra recomendación es que uses “android.support.v4.app.*“.

Nunca mezcles en el mismo proyecto las dos librerías a importar (por ejemplo, que hayas

importado “android.support.v4.app.FragmentManager” y “android.app.Fragment” ), pues lo

más probable es que te encuentres con alguna variante del siguiente error en tiempo de

ejecución:

The method replace(…) in the type FragmentTransaction is not applicable for the

arguments (…)

La solución ya sabes que debes hacer: buscar y cambiar el archivo importado que no

cumple lo anterior descrito y cambiarlo por el otro (en el anterior ejemplo, nos damos

cuenta y lo cambiamos a “android.support.v4.app.FragmentManager”

y “android.support.v4.app.Fragment” ).

Page 82: Fragments Android

Entendido el Fragment normal, ahora uno especial. Implementaremos el del listado

en Fragment_Lista.java. Esta clase es un Fragment, pero es un Fragment especial que

extenderá de ListFragment, que es el listado preparado para ser Fragment (Aquí no

asociamos ningún Layout, pues al extender de ListFragment ya lo trae puesto). Callaback:

Tendrá implementado el Callback que notifique a la Activity de que elemento del listado se

haya pulsado. Explico uno a uno los métodos aquí usados:

Constructor: Seguro que te fijarás en el constructor vacío, es necesario para el correcto

funcionamiento de los Fragments, así que ni se te ocurra quitarlo :D.

onCreate: como ya hicimos en el artículo de listado asignamos el contenido de cada

entrada a cada elemento del listado.

onAttach: Simplemente asegura que el desarrollador haya implementado el Callback,

en la clase que use a este Fragment.

onDetach: Limpia el Callback.

onListItemClick: al extender de ListFragment, es necesario sobrescribir esta clase, que

es la que escucha la pulsación sobre un elemento de la lista. Y así usaremos el

Callback para notificar a la Actividad del id pulsado.

public class Fragment_Lista extends ListFragment {

private Callbacks mCallbacks = CallbacksVacios;

public interface Callbacks {public void onEntradaSelecionada(String id);

}

private static Callbacks CallbacksVacios = new Callbacks() {@Overridepublic void onEntradaSelecionada(String id) {}

};

public Fragment_Lista() {}

@Override

Page 83: Fragments Android

public void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);

setListAdapter(new Lista_adaptador(getActivity(), R.layout.layout_elemento_listado, Lista_contenido.ENTRADAS_LISTA){

@Overridepublic void onEntrada(Object entrada, View view)

{ if (entrada != null) { TextView texto_superior_entrada =

(TextView) view.findViewById(R.id.textView_titulo); if (texto_superior_entrada != null)

texto_superior_entrada.setText(((Lista_contenido.Lista_entrada) entrada).textoEncima);

ImageView imagen_entrada = (ImageView) view.findViewById(R.id.imageView_imagen_miniatura);

if (imagen_entrada != null)

imagen_entrada.setImageResource(((Lista_contenido.Lista_entrada) entrada).idImagen);

}}

});

}

@Overridepublic void onAttach(Activity activity) {

super.onAttach(activity);

if (!(activity instanceof Callbacks)) {throw new IllegalStateException("Error: La

actividad debe implementar el callback del fragmento");}

mCallbacks = (Callbacks) activity;}

@Overridepublic void onDetach() {

super.onDetach();mCallbacks = CallbacksVacios;

Page 84: Fragments Android

}

@Overridepublic void onListItemClick(ListView listView, View view, int

posicion, long id) {super.onListItemClick(listView, view, posicion, id);

mCallbacks.onEntradaSelecionada(Lista_contenido.ENTRADAS_LISTA.get(posicion).id);

}

}

Hasta aquí ya tenemos lo que hace funcionar la estructura de los Tablets.

Continuemos para el caso de Smartphone. Necesitamos un segundo Activity para contener

al Fragment del detalle. Llamaremos Activity_Detalle. ¿Recuerdas que heredaba? A

Activity no, estamos trabajando con Fragments, extiende de FragmentActivity. Solo tiene

un método onCreate que comprobará si ya ha creado al Fragment del detalle para cargarlo,

o no para crearlo. La creación del Fragment del detalle es igual que en el caso de

Activity_Principal.java.

public class Activity_Detalle extends FragmentActivity {

@Overrideprotected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);setContentView(R.layout.layout_activity_detalle);

// Comprobamos que previamente no hayamos entrado en esta actividad (por ejemplo, al rotar el dispositivo). Si es así se añade el fragmento al contenedor

if (savedInstanceState == null) {// Crea el fragmento del detalle de la entrada y

lo añade a la actividadBundle arguments = new Bundle();

arguments.putString(Fragment_Detalle.ARG_ID_ENTRADA_SELECIONADA, getIntent().getStringExtra(Fragment_Detalle.ARG_ID_ENTRADA_SELECIONADA));

Fragment_Detalle fragment = new Fragment_Detalle();

fragment.setArguments(arguments);

Page 85: Fragments Android

getSupportFragmentManager().beginTransaction().add(R.id.framelayout_contenedor_detalle, fragment).commit();

}}

}

Ya solo nos queda comentar Lista_adaptador.java que encontrarás en el artículo del

listado.

Y Lista_contenido.java, que contiene a la clase Lista_entrada del artículo del listado. Lo

hemos hecho así por simplificar el código. Ya que si los datos no van a cambiar, así es muy

fácil hacer una estructura estática que de elementos que siempre esté visible por todas las

clases sea el momento que sea de la ejecución. Simplemente guardamos unos listados con

el contenido de las entradas estáticamente, esto se hace al inicio de la aplicación.

public class Lista_contenido {

/** * Donde se guardan las entradas de la lista. */public static ArrayList<Lista_entrada> ENTRADAS_LISTA = new

ArrayList<Lista_entrada>();

/** * Donde se asigna el identificador a cada entrada de la lista */public static Map<String, Lista_entrada>

ENTRADAS_LISTA_HASHMAP = new HashMap<String, Lista_entrada>();

/** * Creamos estáticamente las entradas */static {

aniadirEntrada(new Lista_entrada("0", R.drawable.im_buho, "BUHO", "Búho es el nombre común..."));

aniadirEntrada(new Lista_entrada("1", R.drawable.im_colibri, "COLIBRÍ", "Los troquilinos (Trochilinae) son..."));

aniadirEntrada(new Lista_entrada("2", R.drawable.im_cuervo, "CUERVO", "El cuervo común (Corvus corax) es ..."));

Page 86: Fragments Android

aniadirEntrada(new Lista_entrada("3", R.drawable.im_flamenco, "FLAMENCO", "Los fenicopteriformes..."));

aniadirEntrada(new Lista_entrada("4", R.drawable.im_kiwi, "KIWI", "Los kiwis (Apterix, gr. 'sin alas') son..."));

aniadirEntrada(new Lista_entrada("5", R.drawable.im_loro, "LORO", "Las Psitácidas (Psittacidae) son..."));

aniadirEntrada(new Lista_entrada("6", R.drawable.im_pavo, "PAVO", "Pavo es un género de aves..."));

}

/** Añade una entrada a la lista * @param entrada Elemento que añadimos a la lista */private static void aniadirEntrada(Lista_entrada entrada) {

ENTRADAS_LISTA.add(entrada);ENTRADAS_LISTA_HASHMAP.put(entrada.id, entrada);

}

/** * Representa una entrada del contenido de la lista */public static class Lista_entrada {

public String id;public int idImagen; public String textoEncima; public String textoDebajo;

public Lista_entrada (String id, int idImagen, String textoEncima, String textoDebajo) {

this.id = id; this.idImagen = idImagen; this.textoEncima = textoEncima; this.textoDebajo = textoDebajo; }

}

}

Y nos falta añadir al AndroidManifest.xml la Actividad que no es la principal, es decir

Activity_Detalle. Este es mi AndroidManifest:

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="jarroba.invarato.fragmentos"

Page 87: Fragments Android

android:versionCode="1" android:versionName="1.0" >

<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="18" />

<application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="jarroba.invarato.fragmentos.Activity_Principal" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="jarroba.invarato.fragmentos.Activity_Detalle" android:label="@string/titulo_detalle_entrada" android:parentActivityName=".Activity_Principal" > <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".Activity_Detalle" /> </activity> </application>

</manifest>

Dejo aquí el código funcional para descargar, bastante comentado para aclarar todavía más

las posibles dudas que queden en el tintero. Descárgalo aquí: Fragmentos

Te recomiendo que lo pruebes tanto con un emulador –o dispositivo físico- de Smartphone,

como de Tablet, para que veas como queda en cada dispositivo.

Nota: si quieres ahorrarte un montón de código aprovechando ahora que entiendes la

arquitectura de los Fragments. Al crear un nuevo proyecto Android puedes seleccionar

Page 88: Fragments Android

“Master/Detail Flow” en la página “Create Activity”. Lo único que, como usa ActionBar,

requiere de mínimo Android 3.0 (API 11 o Honeycomb).

Y con esto acabamos los fragmentos. La estructura mental de Fragment tiene cosas que ver

y cosas que son completamente diferentes a la de Activity. Una vez entendidas te aseguro

que son muy fáciles

Page 89: Fragments Android

Context de AndroidEn Jarroba hemos querido recopilar y explicar con máximo detalle el Context o contexto de

Android, a razón de que el Contex es una parte fundamental a la hora de desarrollar

aplicaciones Android (Antes de continuar puede que te interese comprender algunos

conceptos básicos como Activity en este artículo o Intent en este otro). Si eres programador

Android seguro que notarás que entenderlo resulte fundamental, pero la poca información

respecto al tema resulte escasa por la breve documentación, o a dar por dadas cosas que no

están lo suficientemente explicadas. Puede que tengamos una breve idea sobre Context. Por

eso, le hemos querido dedicar un artículo a esta parte apasionante de Android. Así, si nos

dirigimos a la documentación oficial podemos leer:Es el interfaz global de información acerca del entorno de la aplicación. Es una clase abstracta que implementa Android. Permite acceder a los recursos específicos de la aplicación y a sus clases, así como llamar al padre para realizar operaciones a nivel de la aplicación, como lanzar Activities, difundir mensajes por el sistema, recibir Intents, etc.

(Original en http://developer.android.com/reference/android/content/Context.htm,

traducción realizada por www.Jarroba.com)

Antes de explicar con detalle esta idea oficial -a la que te recomiendo que acudas como

resumen cuando tengas claras las ideas- seguro que te ha ocurrido el mal de muchos

programadores Android. Dicho mal ha sido creernos lo del Context y aplicarlo cada vez

que se nos pide sin saber muy bien por qué, para qué sirve realmente, cuántos ContextPero

no esta la  hay, o de dónde sale. Por norma general sabemos que el contexto es el this, que

es el objeto propiamente:

Context contexto = this;

Pero si es el objeto ¿Cómo puede ser a la vez un contexto? ¿Todos los objetos tienen

contexto? Responderemos las preguntas a continuación.

Si te dijera que el Context de Android es solo eso un contexto, puede que te quedes igual.

Si tampoco está muy clara la idea de la palabra “contexto” y te digo que cumple a la

perfección su segundo significado que figura en la RAE (Definición de contexto en la

RAE):

Page 90: Fragments Android

2. m. Entorno físico o de situación, ya sea político, histórico, cultural o de cualquier otra índole, en el cual se considera un hecho.

Puedes que me llames hasta loco   Y aunque no te puedo demostrar que esté loco o no, lo

que te comento no es ninguna locura y te lo voy a aclarar.

 

SOBRE EL CONTEXT

14-YEAR-OLD GIRL STEALS HER MOTHER'S HUSBAND (BWNTODAY)

Para entenderlo tenemos que volver a un pasado lejano, unos cuantos siglos atrás,

concretamente a 1492 (Esto no demuestra mi locura, es solo un ejemplo   ). El año

1492 lo englobaba todo en esa época: casas, barcos, personas, cultura, libros, etc. Si lo

extrapolamos a las Apps de Android, diríamos que 1492 es nuestra aplicación –la que

engloba todo- y que los barcos, la gente y las casas son las Activities. Todo tiene un

contexto propio y estará dentro de otro. Podemos hablar del contexto histórico de 1492 (ojo

con la palabra contexto); así, si le pidiera al contexto de 1492 que me dé un barco, no me

devolvería un portaviones de motor nuclear, probablemente me devolvería una carabela,

una nao, o algún otro barco de la época. He escogido el año de 1492 porque me parecía

muy global, al ser el año del descubrimiento de América por Cristobal Colón; y porque es

algo histórico, que es donde más se escucha la palabra contexto, más concretamente

“contexto histórico”. Como antes mencioné todo tiene un contexto propio, por ejemplo un

barco tiene su propio contexto, que será diferente al contexto de otro barco, y muy diferente

al de un libro, pero ambos están dentro del mismo contexto del año 1492 .

Vamos a concretar un poco más el ejemplo. Hemos creado una aplicación que hemos

llamado 1492 (en la imagen siguiente verás que hemos puesto 1492.apk, el apk es la

extensión en todas las aplicaciones de Android). De momento solo nos ha dado tiempo a

crear 4 Activities. 3 Activities van a ser los barcos que Cristobal Colón usó para viajar,

estos fueron: La Niña, La Pinta y La Santa María. Y añadimos una última Activity que

hemos llamado “Cortes”.

Page 91: Fragments Android

Si estando en la Activity La_Pinta por ejemplo, le pedimos que nos muestre los cuadros

famosos (imagina que lo que aquí llamamos “cuadro famoso” es un recurso drawable de

Android, es decir, un imagen) que están en su contexto, nos devolverá pinturas de la época

estén o no estén dentro del barco (en Android serían todos las imágenes que estén en la

carpeta res). Pero algún cuadro, lo podremos comprar y colgar dentro del camarote del

capitán (para Android, “comprar” equivaldría a obtener la imagen por

getResources().getDrawable(), y “colgar en el camarote”  por ejemplo al método de una

View: setBackground () ), de este modo sí estaría el cuadro dentro del barco aunque en su

contexto existan otros.

Para ver este ejemplo en código vamos a tener la siguiente estructura de carpetas (solo

usaremos la clase que hereda de Activity llamada “Barco_La_Pinta.java”, el resto están

puestas para ejemplificar el mundo de 1492 del ejemplo):

Page 92: Fragments Android

Tenemos un layout que hemos llamado “pared_del_camarote_del_barco_la_pinta.xml”,

que contiene una ImageView (que es el hueco de la pared donde colgar el cuadro) en el

siguiente código:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context=".Barco_La_Pinta" >

<ImageViewandroid:id="@+id/imageView_Barco_La_Pinta_lugarParaCuadro"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentTop="true"

Page 93: Fragments Android

android:layout_centerHorizontal="true"android:layout_marginTop="188dp"android:src="@drawable/ic_launcher" />

</RelativeLayout>

Y el código java de “Barco_La_Pinta.java” es:

public class Barco_La_Pinta extends Activity {

@Overrideprotected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.pared_del_camarote_del_barco_la_pinta);

ImageView iv = (ImageView) findViewById(R.id.imageView_Barco_La_Pinta_lugarParaCuadro);

Context contexto = this;

Drawable cuadroElegido = contexto.getResources().getDrawable(R.drawable.cuadro_torre);

iv.setBackground(cuadroElegido); //Nota: Si se usa un Api Level anterior a 16 usar setBackgroundDrawable()

}

}

En Android, cuando la documentación oficial se refiere a “interfaz global de información

acerca del entorno de la aplicación”, quiere decir que nos proporciona a los

programadores acceso a métodos que nos facilitan la vida; como lanzar nuevas

Activities con startActivity() (¿A qué no programas cada línea de código al lanzar una

aplicación? Simplemente llamas a este método y se acabó). U obtener recursos de la carpeta

res (como a las imágenes, colores, textos, etc) con getResources() o los Strings con

getString(), obtener los datos almacenados en local con getSharedPreferences(), entre otros

muchos.

Volvamos al ejemplo, ¿Pero si le pregunto al contexto de 1492 por los cuadros famosos, no

nos devolvería los mismos? Sí, los mismos. Pero no es esta la causa por lo que se

Page 94: Fragments Android

diferencian los contextos, existen cosas comunes.

Al contexto de cualquier barco le puedo preguntar ¿Cómo de ajetreado está el mar? y nos

responderá, pero el de la “corte” nunca le voy a poder hacer esa pregunta ¡no está en el

mar! (En Android no es lo mismo el contexto de una Activity al que le puedo preguntar por

el Layout asociado, que de un Servicio que no puedo preguntarle por el Layout porque no

tiene ninguno).

Entonces, en Android nos encontramos con los siguientes tres diferentes tipos de

contextos:

Aplicación (Como es lógico, al cubrir todo el ciclo de vida de la aplicación desde que la

arrancamos hasta que muere, cada aplicación tiene un único contexto de aplicación; además,

este contexto engloba a todos los demás): Crea una instancia Singleton que se crea al iniciar

el proceso de la aplicación. Se puede acceder desde una Activity o un Service con

getApplication(), o desde cualquiera que herede de Context con getApplicationContext().

Activity o Service: Heredan de ContextWrapper que a su vez hereda de Context.

ContextWrapper hace que se apodere de todas las llamadas y las envíe a una instancia oculta

de Context (conocido como “Base Context”)

Los siguientes componentes fundamentales de Android no heredan de Context, pero lo

necesitan, por lo que el sistema se los pasa de algún modo:

BroadcastReceiver : Pero se le pasa un Context en el onReceive() cada vez que un nuevo

evento difundido por el sistema entra.

ContentProvider : Puede acceder al Context con getContext() y se le devolverá el Context de

la aplicación que lo esté ejecutando (puede ser la misma aplicación u otra diferente).

En el ejemplo, si un barco es destruido, su contexto muere con él. Así, en Android el

Context está ligado al ciclo de vida del elemento que cubre, desde el momento que está en

ejecución hasta que termina.

Siendo un poco más técnicos. Context es una clase abstracta que implementa

Android. Los elementos que tienen Context heredan de la clase Context. Por lo que –y

respondiendo a la preguntas del principio- ni todos los objetos tienen Context, y ni el objeto

es el propio Context, depende de si heredan de Context o no.

Nota sobre la herencia: Si no entiendes muy bien la documentación, te recomiendo que

eches un vistazo a las diferentes documentaciones (Por facilitar, hemos puesto los links de

las documentaciones en cada uno de los elementos anteriores). Por ejemplo, a la

documentación de Activity (ver en la documentación oficial de Activity). Vemos en la

cabecera como efectivamente hereda de Context:

Page 95: Fragments Android

 

 

SUELEN PEDIR EL CONTEXT

Normalmente piden el Context:

Al acceder a los recursos como imágenes, Strings, etc

contexto.getResources().getDrawable(R.drawable.imagen_en_los_recursos);

Al crear nuevos Views, Listeners, Adapters, Intents, etc. Por ejemplo:

TextView miTexto = new TextView(contexto);

Intent intento = new Intent(contexto, claseActivityArrancar.class);

Al acceder directamente a componentes como ActionBar, Intents, etc

contexto.getIntent();

 

OBTENER EL CONTEXT

Hemos explicado cómo obtener el Context dependiendo del tipo. Aquí dejo una lista de las

diferentes maneras de obtener el Context con ejemplos completos:

Una clase que hereda de Context (Como desde una Activity o Service): this

Page 96: Fragments Android

public class ObtenerContexto extends Activity  {

@Overrideprotected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);Context contexto = this;

}

}

Una clase que hereda de Context desde una clase anónima: ClaseActivity.this

public class ObtenerContexto extends Activity  {

@Overrideprotected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

Button boton = (Button) findViewById(R.id.button_obtenerContext_boton);

boton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {

Context contexto = ObtenerContexto.this;}

});

}

}

Una clase que NO hereda de Context (como desde View): mi_view.getContext()

public class ObtenerContexto extends Activity  {

@Overrideprotected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

Button boton = (Button) findViewById(R.id.button_obtenerContext_boton);

Context contexto = boton.getContext();}

Page 97: Fragments Android

}

La aplicación (desde cualquier clase que hereda de Context): getApplicationContext()

public class ObtenerContexto extends Activity  {

@Overrideprotected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

Context contexto = getApplicationContext();}

}

Otro Context (desde una clase que hereda de ContextWrapper, como Activity):

getBaseContext()

public class ObtenerContexto extends Activity  {

@Overrideprotected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

Context contexto = getBaseContext();}

}

La Activity del Fragment que lo contiene (desde un Fragment): getActivity()

public class obtenerContextoFragment extends Fragment {

@Overridepublic void onAttach (Activity activity) {

super.onAttach(activity);

Context contexto = getActivity();}

}

 

Page 98: Fragments Android

EVITAR PROBLEMAS CON EL CONTEXT

Ahora solo queda evitar las fugas de memoria que el Context nos puede provocar. Lo

primero que tenemos que repasar es que un Context vive tanto como el elemento al que

pertenece, por lo que depende del ciclo de vida. Así, un Context de una Activity vivirá el

mismo tiempo que esta, y el Context de la aplicación vive hasta que se termina por

completo la aplicación (hasta que muere, no hasta que se pausa).

Aunque suene un poco contradictorio, por norma general siempre hay que usar el Context

más pequeño que suele ser el del elemento al que pertenece; pero en la mayoría de casos se

obtiene pérdida de memoria, siendo altamente recomendable usar el Context de la

aplicación. Esto sucede por el tema de la doble referencia, es decir un objeto apunta a otro

objeto y el Garbage Collection (recolector de basura de Java, para la liberación automática

de memoria) no puede eliminar dichos objetos, lo que implica no poder liberar esa memoria

aunque no haya nada más apuntando a dichos objetos más que el uno al otro.

Imaginemos que queremos guardar en una variable estática una imagen de fondo para no

tener que estar accediendo todo el rato a la memoria cada vez que giramos el dispositivo

(recuerdo rápidamente que al girar el dispositivo se destruye la actividad y se vuelve a

crear, si la variable es estática siempre estará en memoria aunque sea destruida), con esto el

código iría mucho más rápido:

public class miActivity extends Activity  {

private static Drawable sFondoDelTexto;

@Overrideprotected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

Context contexto = this;

TextView label = new TextView(contexto);label.setText("Texto con fondo");

if (sFondoDelTexto == null) {sFondoDelTexto =

getDrawable(R.drawable.mi_imagen);}

Page 99: Fragments Android

label.setBackgroundDrawable(sFondoDelTexto);

setContentView(label);}

}

El problema es que le ponemos el Context como this al TextView, y al TextView le

asignamos una imagen que no va a ser destruida al rotar el dispositivo. Como esta imagen

apunta al objeto TextView, y la imagen no va a ser destruida, no se destruirá el TextView,

así no se destruirá el Context, por lo que no se destruirá la Activity que está ligado al

Context. Aquí tenemos el problema de memoria, pues al volverse a crear, la imagen

apuntará a otro Context de otra Activity creada; y así en cada giro del móvil, hasta que nos

quedemos sin memoria y de error.

Para solucionar este caso lo mejor es usar el contexto de la aplicación (Usando

Context.getApplicationContext() o con Activity.getApplication() ) ya que no está asociado

al ciclo de vida de la Activity, por lo que al destruirse el Context de la Activity quedará

libre cuando tenga que ser destruida y podrá ser recolectada:

public class miActivity extends Activity  {

private static Drawable sFondoDelTexto;

@Overrideprotected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

Context contexto = getApplicationContext();

TextView label = new TextView(contexto);label.setText("Texto con fondo");

if (sFondoDelTexto == null) {sFondoDelTexto =

getDrawable(R.drawable.mi_imagen);}

label.setBackgroundDrawable(sFondoDelTexto);

Page 100: Fragments Android

setContentView(label);}

}

También hay que tener cuidado al crear una clase Singleton que guarde nuestro Context

(desde Jarroba no recomendamos guardar un Context ni con Singleton ni con variables

estáticas, el Context se ha de pasar siempre para no perder su control). Pues si la Activity es

destruida nunca se borrará de memoria, ya que como el ejemplo anterior, existirá una doble

referencia que no permita al Garbage Collector liberar dicha memoria, por lo que la

Activity seguirá ocupando toda la memoria pese a estar destruida:

public class miSingleton {

private static miSingleton sInstancia;

private Context mContexto;

public static miSingleton getInstance(Context contexto) {if (sInstancia == null) {

sInstancia = new miSingleton(contexto);}

return sInstancia;}

private miSingleton(Context contexto) {mContexto = contexto;

}

}

Para corregir esto, se puede usar el Context de la aplicación otra vez (aunque recordemo

que el Context de una aplicación está guardado en una clase Singleton a su vez, con lo qué

¿Realmente necesitamos guardarlo de esta manera o con obtenerlo nos basta?):

public class miSingleton {

private static miSingleton sInstance;

Page 101: Fragments Android

private Context mContexto;

public static miSingleton getInstance(Context contexto) {if (sInstance == null) {

sInstancia = new miSingleton(contexto.getApplicationContext());

}

return sInstancia;}

private miSingleton(Context contexto) {mContexto = contexto;

}

}

Con esto terminamos con un resumen del Context: sirve para abstraer de muchas cosas que

las hace el sistema operativo por nosotros, así se hace la labor del desarrollador mucho más

simple. Siempre que entendamos el Context y sus implicaciones.

Esperamos que el artículo haya servido para aclarar muchos conceptos. No ha sido un

artículo fácil por la escasa información que existe sobre el tema. Por esta razón lo

modificaremos con el tiempo, para que sea todavía más aclaratorio tanto por el feedback

que nos vayáis aportando, como por las novedades que vayan saliendo del mundo Android.

Si quieres ver algunos ejemplos más en donde se aplica Context puedes ver el artículo de

listado o en el artículo de fragments.

Referencias:

Doubleencore: http://www.doubleencore.com/2013/06/context/

Android Developer: http://developer.android.com/reference/android/content/Context.html

Android Deveolper Blog: http://android-developers.blogspot.com.es/2009/01/avoiding-

memory-leaks.html

Page 102: Fragments Android

Intent – Pasar datos entre Activities – App Android (Video)En este tutorial se va a mostrar el IDE de eclipse para realizar una App en Android y como

pasar datos de un activity a otro tras un evento o dicho de otra forma, como pasar datos de

una pantalla a otra tras pulsar un botón. Pare realizar este tutorial se necesitará tener y saber

lo siguiente:

Se ha de tener instalado el IDE “Eclipse” y tenerlo vinculado con el SDK de Android. Para los

que no tengáis hecho esto lo podeis hacer siguiendo el siguiente tutorial:( Instalar el SDK de

Android y vincularlo con Eclipse)

Se ha de tener el Eclipse configurado correctamente. En este caso solo se necesitara tener

configurado el emulador del móvil aunque también es conveniente tener configurada la

consola de salida de datos llamada “LogCat” para poder realizar trazas en el desarrollo de la

App. Para los que no tengáis configurado el Eclipse, podeis ver como se hace en el siguiente

tutorial:(Preparar las herramientas necesarias para programar en Android)

Por último, aunque no es necesario pero si recomendable, debéis de mirar el siguiente

tutorial (Activity – entender y usar una Actividad) para entender el ciclo de vida de un

activity. No es necesario que lo mireis para realizar este tutorial y quizas si sois “nóveles” en

el desarrollo de Apps os cueste entenderlo a la primera , pero si que es muy recomendable

para que posteriormente entendáis muchas cosas en el desarrollo de Apps.

Es objectivo de este tutorial (a parte de mostrar el IDE y los archivos que hay en un

proyecto) se pretende hacer la siguiente app, que consiste en recoger unos datos del primer

activity y mostrarlos en el segundo, tal y como se muestra en la siguiente imagen:

Page 103: Fragments Android

Una vez vista la funcionalidad que ha de tener esta app, mostramos el video de como

realizar esta App desde Cero:

Page 104: Fragments Android

El proyecto realizado en este Video-Tutorial, lo podeis descargar pulsando AQUI.

Como se ha visto en este tutorial (A parte de crear las interfaces que lleva bastante tiempo),

la clave para pasar los datos de un activity a otro, es crearse un “Intent” para pasar los datos

de un activity a otro. Este Intent se ha declarado dentro del código que define el evento que

se ha de realizar tras pulsar el botón; es decir en el “OnClickListener”. La parte del código

donde se describe esta acción y se declara el Intent es el siguiente:

miBoton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {

Intent i = new Intent (PrimerActivity.this, SegundoActivity.class);

i.putExtra("nombre", campoNombre.getText()+"");

i.putExtra("apellido", campoApellidos.getText()+"");

startActivity(i);}

Page 105: Fragments Android

});

Como se observa, en el intent “se declaran” dos “variable”, ‘nombre’ y ‘apellido’ (lo pongo

entre comillas dobles porque en realidad no es así, pero lo digo de esta forma para que se

entienda) que se pasan al segundo activity (Intent i = new Intent

(PrimerActivity.this, SegundoActivity.class)) tras ejecutarse el método “startActivity(i)”.

Cuando el “Intent” ‘i’ llega al segundo activity, hay que asignar los datos que hay en el

activity a alguna variable o elemento del segundo activity. Es recomendable, aunque no

necesario que se asigne lo que se obtiene de un Intent a variables para que sea el código

mas “legible y entendible”. Recogemos los datos en el segundo activity de la siguiente

forma:

String nombre = getIntent().getStringExtra("nombre");String apellidos =

getIntent().getStringExtra("apellido");

Después de recoger los datos los mostramos en la pantalla en los TextView

correspondientes.

Esto ha sido todo, esperamos que este tutorial os sirva de ayuda 

Entradas Relacionadas Introducción a Android Studio (Video)

Preparar las herramientas necesarias para programar en Android

Page 106: Fragments Android

Error Android – No se encuentra el SDK de android

UITableView en Objective-C -Ejemplo- (Video)

ListView o listado en AndroidEste tutorial está indicado para todos lo que ya hayan buceado un poco en Android. Se dará

por sentado ciertos conocimientos muy básicos, cuyo apoyo pueden servirse de tutoriales

anteriores como el de saber qué es una Actividad. Si no tienes mucho tiempo o quieres un

Snippet de código para hacer un listado rápidamente, entonces te recomiendo que vayas

directamente al artículo de listado en pocas líneas; si por el contrario quieres aprenderlo

todo sobre listados de Android continuar leyendo es la opción correcta.

“Aunque todo en el universo tienda al caos, siempre es bueno ponerle un orden para saber

interpretarlo”. No es la mejor definición de listado, pero si una bonita alegoría al mismo.

Un listado es eso, un conjunto de datos ordenados, para que sea sencillo indagar en ellos.

Este tutorial se basará en describir como hacer un listado en Android: en el que tenemos un

número arbitrario de datos -número exacto de entradas que no conocemos, bien porque

obtengamos los datos de Internet, o estén en una base de datos que va creciendo, etc- de

aves que queremos ordenar en un listado. Cuyas entradas queremos hacer a nuestro antojo

con el nombre del ave como titular, una pequeña descripción y una imagen asociada a cada

uno (Véase la imagen siguiente, para entender de un vistazo lo que se quiere conseguir).

Page 107: Fragments Android

Y no contentos con esto, queremos que además sea seleccionable cada elemento del listado.

Para mostrar más información en un Toast (también podríamos abrir otra Activity con

información extra, o lo que queramos) del elemento seleccionado.

Page 108: Fragments Android

Vamos, que construiremos un ListView de generación dinámica de contenidos (Un ejemplo

estático lo tienes en la página oficial de desarrolladores de Android), deseable el 90% de la

veces por un programador Android. Y sí, las imágenes y textos del ejemplo están obtenidos

de la Wikipedia.

Como haremos esto de una manera sencilla y óptima. Primero lo entenderemos y luego lo

haremos.

¿Qué es lo que queremos hacer? Queremos un listado de todos los elementos/entradas

individuales que me den la gana (entre cero e infinito   ). De lo real al concepto mental

sería la siguiente imagen.

Page 109: Fragments Android

Todos las entradas son iguales, ¿Sería maravilloso que se pudiera diseñar una única entrada

y luego que el resto sean “un copiar y pegar”? Aunque sea por vaguería, la respuesta es:

debemos como buenos programadores.

Page 110: Fragments Android

Ya tenemos dividido lo que queremos hacer. Ahora, la lógica nos dice que en el diseño

hacia delante, tendremos dos Layouts uno principal que contendrá varias veces la copia del

segundo:

Uno con la entrada suelta, con el diseño que queramos. Será la unidad que se copiará en el

interior del listado. Lo llamaremos en este ejemplo “entrada.xml”.

Otro con el listado, que solo dispondrá de tal. Con los “huecos” esperando para contener la

copia del diseño de la entrada; y cada entrada con unos datos diferentes. A este contenedor

de entradas lo llamaremos “listado.xml”.

Haremos el diseño de “entrada.xml”. Para el ejemplo haremos el siguiente Layout, que

evidentemente podremos hacer a nuestro antojo, con los campos que queramos, imágenes,

botones, checkbox, spinner, etc. Al diseñar éste, pensemos, no en el contenido de cada

entrada, sino como haremos todas las entradas para que salgan iguales. Pues este será

replicado para todas la entradas.

Para el ejemplo, la idea de una entrada es la siguiente:

Page 111: Fragments Android

Y su código asociado para generar la vista de una entrada sería este (Nota: por reducir

código y que se entienda mejor, hemos optado por poner los Strings directamente; esto está

mal hecho y deberían ir al fichero “strings.xml”). Evidentemente ponemos los “id” para

luego referenciarlos y llenar cada una de las entradas con sus datos correspondientes.

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" >

<ImageView android:id="@+id/imageView_imagen" android:layout_width="80dp" android:layout_height="80dp" android:adjustViewBounds="true" android:scaleType="fitXY" android:contentDescription="Descripción del contenido de la imagen" android:src="@android:drawable/ic_menu_gallery" />

<LinearLayout android:layout_width="fill_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" >

<TextView android:id="@+id/textView_superior" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Large Text" android:textAppearance="?android:attr/textAppearanceLarge" />

Page 112: Fragments Android

<TextView android:id="@+id/textView_inferior" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Small Text" android:textAppearance="?android:attr/textAppearanceSmall" />

</LinearLayout>

</LinearLayout>

¿Fácil? No. Más fácil es “listado.xml”. En este ejemplo nos bastará con que usemos

ListView, que será el contenedor de las entradas.

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

Falta que hagamos la lógica del programa. La lógica que nos cargará los datos y nos

construirá el listado. Para esto tendremos una Actividad que será la que muestre el listado.

Para no liar las cosas lo haremos en el ejemplo en la clase “MainActivity.java”. Esta hará

uso de otras dos. Una que encapsule los datos de la entrada, cuyo nombre pondremos como

“Lista_entrada.java”. Y otra que será la que adapte las entradas a la lista, y será nombrada

como “Lista_adaptador.java”.

Terminaremos teniendo los siguientes archivos en el explorador de paquetes. Donde los

marcados serán los que usaremos en este ejemplo. Cabe indicar que las imágenes las

estamos insertando directamente para no complicar más el ejemplo, pero esta forma de

construir el listado dinámicamente también funcionaría si las imágenes se descargaran de

internet, se obtuvieran de la memoria del teléfono, etc.

Page 113: Fragments Android

Empezaremos a definir “Lista_entrada.java”. ¿Para qué sirve? Sabemos que nos llegarán

unos datos, y que nos interesan los siguientes para cada entrada: imagen, título y

descripción. Por lo que haremos unos paquetes de datos para cada uno de los conjuntos de

la entrada. Estos paquetes serán objetos de la clase “Lista_entrada.java”.

Para hacernos una idea, imaginemos que tenemos tres pájaros. Cada uno con: título,

descripción e imagen. Tendremos tres objetos de la clase “Lista_entrada.java”, cada uno

almacenará sus datos. Pero no confundamos, esto no está asignado todavía a la entrada de

Page 114: Fragments Android

la vista -de la interfaz- sino que son grupos para separar la información. Estos grupos se

suelen llamar manejadores o en inglés “handler”.

El código siguiente contiene lo que es la encapsulación de los datos, que coinciden con los

explicados:

Título, con nombre de variable como “textoEncima” para especificar en cuál de los dos

TextViews de la vista tendrá que ser colocado.

Descripción, la variable la llamaremos como “textoDebajo”.

Imagen, y almacenaremos la variable “idImagen”, para referenciara a la imagen. Solo

guardaremos el id, ya que solo guardaremos el identificador de la imagen y no la imagen en

sí (para este ejemplo no lo complicamos y guardamos el identificador, pero si queremos

podremos guardar un Drawable, un Bitmap, etc).

Este manejador tendrá las variables de los View que hayamos declarado en el Layout

“entrada.xml”. Repito, aquí no lo referenciaremos, es solo un contenedor que separa unos

datos de otros.

Si nos fijamos en el su código, solo son variables, con sus getters y de setter el mismo

constructor. No tiene más la clase, es así de simple (aquí, para que se entienda mejor,

ponemos los getters y setters; Android recomienda por optimizar hacer variables globales

publicas y olvidarnos de getters y setters, con lo que sería todavía mucho más simple este

código).

package jarroba.ramon.listado;

Page 115: Fragments Android

public class Lista_entrada {private int idImagen; private String textoEncima; private String textoDebajo;

public Lista_entrada (int idImagen, String textoEncima, String textoDebajo) {

this.idImagen = idImagen; this.textoEncima = textoEncima; this.textoDebajo = textoDebajo; }

public String get_textoEncima() { return textoEncima; }

public String get_textoDebajo() { return textoDebajo; }

public int get_idImagen() { return idImagen; }

}

¿Cómo encapsulamos los datos que queramos en este Handler? Muy sencillo, ya verás.

Si creamos el objeto “Lista_entrada” tal cual, lo haríamos de la siguiente manera:

new Lista_entrada(R.drawable. imagen, "TÍTULO", "Descripción");

¿Dónde asignamos el objeto? ¿Cuántos objetos vamos a tener de estos? La respuesta es en

una lista de Java, más concretamente en un ArrayList.

En este ejemplo lo haremos directamente sobre la clase “MainActivity.java” como ejemplo

la siguiente (Nota: lo que se muestra a continuación es un trozo de la clase

“MainActivity.java”, se muestra completa al final del artículo). Aquí un ejemplo de

como añadir la información de las tres aves al ArrayList:

package jarroba.ramon.listado;

Page 116: Fragments Android

import java.util.ArrayList;

import android.app.Activity;import android.os.Bundle;import android.widget.ListView;

public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.listado);

ArrayList<Lista_entrada> datos = new ArrayList<Lista_entrada>();

datos.add(new Lista_entrada(R.drawable.im_buho, "BUHO", "Búho es el nombre común de aves de la familia Strigidae, del orden de las estrigiformes o aves rapaces nocturnas. Habitualmente designa especies que, a diferencia de las lechuzas, tienen plumas alzadas que parecen orejas.")); datos.add(new Lista_entrada(R.drawable.im_colibri, "COLIBRÍ", "Los troquilinos (Trochilinae) son una subfamilia de aves apodiformes de la familia Trochilidae, conocidas vulgarmente como colibríes, quindes, tucusitos, picaflores, chupamirtos, chuparrosas, huichichiquis (idioma nahuatl), mainumby (idioma guaraní) o guanumby. Conjuntamente con las ermitas, que pertenecen a la subfamilia Phaethornithinae, conforman la familia Trochilidae que, en la sistemática de Charles Sibley, se clasifica en un orden propio: Trochiliformes, independiente de los vencejos del orden Apodiformes. La subfamilia Trochilinae incluye más de 100 géneros que comprenden un total de 330 a 340 especies.")); datos.add(new Lista_entrada(R.drawable.im_cuervo, "CUERVO", "El cuervo común (Corvus corax) es una especie de ave paseriforme de la familia de los córvidos (Corvidae). Presente en todo el hemisferio septentrional, es la especie de córvido con la mayor superficie de distribución. Con el cuervo de pico grueso, es el mayor de los córvidos y probablemente la paseriforme más pesada; en su madurez, el cuervo común mide entre 52 y 69 centímetros de longitud y su peso varía de 0,69 a 1,7 kilogramos. Los cuervos comunes viven generalmente de 10 a 15 años pero algunos individuos han vivido 40 años. Los juveniles pueden desplazarse en grupos pero las parejas ya formadas permanecen juntas toda su vida, cada pareja defendiendo un territorio. Existen 8 subespecies conocidas que se diferencian muy poco aparentemente, aunque estudios recientes hayan demostrado diferencias genéticas significativas entre las poblaciones de distintas regiones."));

Page 117: Fragments Android

}

}

Todavía no hemos acabado con la clase “MainActivity.java”. Faltaría decirle que queremos

hacer con el ArrayList, que será enviárselo a la lista para que lo reconstruya y la de forma.

A toda ListView hay que pasarle un adaptador con los datos y que herede de

“BaseAdapter” (Son cosas de Android…).

Antes un inciso. Hemos trabajado y conocemos la pesadez de hacer el adaptador de un

listado de manera dinámica. Por eso www.jarroba.com quiere haceros un regalo, con el

siguiente ahorro de tiempo y de comerse la cabeza. En todos los ejemplos que he visto por

ahí, construyen un adaptador independiente para cada handler para este tipo de listas

simples. Y yo he dicho: no, nunca más, no es necesario, un solo adaptador vale para todas

las listas con entradas personalizadas que queramos (o al menos todas con las que he

trabajado). A continuación les regalo el código, que en apariencia es sencillo; lo que sí es

muy fácil de utilizar. Es el trabajo de mucho tiempo, errores y experiencia. Puede que sea

mejorable, por lo que si descubres y nos lo quieres contar, o descubro una manera mejor

actualizaré este tutorial. Pero por ahora no he visto nada más cómodo para trabajar con

listas. Por supuesto, eres libre de modificarlo, proponer mejoras y de usarlo.  Nota: este

adaptador “universal” es solo para listas simples con vistas complejas, lo que es que van a

cargarse un vez los elementos de la lista y ya no se van a modificar más mientras el

usuario interaccione con la lista; si queremos añadir o eliminar elementos mientras el

usuario toca la lista, requerimos de otro tipo de adaptador (Puedes aprender como utiliza

inflate() el método getView() del Adapter en este otro tutorial).

Código de la clase “Lista_adaptador.java”:

package jarroba.ramon.listado;

import java.util.ArrayList;

import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;

Page 118: Fragments Android

/** Adaptador de ListView universal, para www.jarroba.com * @author Ramon Invarato Menéndez */public abstract class Lista_adaptador extends BaseAdapter {

private ArrayList<?> entradas; private int R_layout_IdView; private Context contexto;

public Lista_adaptador(Context contexto, int R_layout_IdView, ArrayList<?> entradas) { super(); this.contexto = contexto; this.entradas = entradas; this.R_layout_IdView = R_layout_IdView; }

@Override public View getView(int posicion, View view, ViewGroup pariente) { if (view == null) {

LayoutInflater vi = (LayoutInflater) contexto.getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = vi.inflate(R_layout_IdView, null); } onEntrada (entradas.get(posicion), view); return view; }

@Override public int getCount() { return entradas.size(); }

@Override public Object getItem(int posicion) { return entradas.get(posicion); }

@Override public long getItemId(int posicion) { return posicion; }

/** Devuelve cada una de las entradas con cada una de las vistas a la que debe de ser asociada

Page 119: Fragments Android

* @param entrada La entrada que será la asociada a la view. La entrada es del tipo del paquete/handler * @param view View particular que contendrá los datos del paquete/handler */ public abstract void onEntrada (Object entrada, View view);

}

Este código lo que hace es crear un adaptador para ListView. En el constructor hay que

pasarle el contexto de la aplicación, el id del layout de la entrada (en este ejemplo

“R.layout.entrada”), y el ArrayList que construimos anteriormente con los handler con los

datos. Como es obvio, ya que pide los datos del ArrayList, este código irá justo después de

la creación del ArrayList. Al crear el objeto se nos pedirá sobrescribir el método

“onEntrada”, que será donde asignaremos cada parte del handler de la clase

“Lista_entrada.java” a cada una de las copias de las vistas “entrada.xml”. “onEntrada” da

de información: el objeto con solo los datos de la entrada que va a construir y la vista donde

lo va a meter. Te sorprenderá el escaso código que hay que usar y lo simple que es su uso, a

continuación lo veremos.

Una vista rápida de donde se usa este adaptador.

Page 120: Fragments Android

En el ejemplo lo situaremos dentro del “MainActivity.java” en el “onCreate”, después del

código que ya había (Nota: como comentamos anteriormente, al final publicaremos el

código completo). Código para usar el adaptador:

ListView lista = (ListView) findViewById(R.id.ListView_listado); lista.setAdapter(new Lista_adaptador(this, R.layout.entrada, datos){

@Overridepublic void onEntrada(Object entrada, View view)

{ TextView texto_superior_entrada =

(TextView) view.findViewById(R.id.textView_superior);

texto_superior_entrada.setText(((Lista_entrada) entrada).get_textoEncima());

Page 121: Fragments Android

TextView texto_inferior_entrada = (TextView) view.findViewById(R.id.textView_inferior);

texto_inferior_entrada.setText(((Lista_entrada) entrada).get_textoDebajo());

ImageView imagen_entrada = (ImageView) view.findViewById(R.id.imageView_imagen);

imagen_entrada.setImageResource(((Lista_entrada) entrada).get_idImagen());

}});

Este poco de código usa el adaptador. Se asigna cada View con su dato en el momento justo

en el que se construye cada entrada ¿No dije que era fácil?.

Ya solo queda capturar el evento de Click para cada una de las entradas y hacer lo que

sea con esta. En este ejemplo recogemos la descripción y la mostramos en un Toast. Esto

lo hacemos al devolver el adaptador y conociendo la posición nos habrá devuelto la entrada

pulsada. Después del anterior código escribimos el escuchador del evento click como se

hace normalmente:

lista.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> pariente, View view, int posicion, long id) { Lista_entrada elegido = (Lista_entrada) pariente.getItemAtPosition(posicion);

CharSequence texto = "Seleccionado: " + elegido.get_textoDebajo(); Toast toast = Toast.makeText(MainActivity.this, texto, Toast.LENGTH_LONG); toast.show(); } });

Y ya al fin, el código completo del “MainActivity.java”, es el siguiente:

package jarroba.ramon.listado;

import java.util.ArrayList;

Page 122: Fragments Android

import android.app.Activity;import android.os.Bundle;import android.view.View;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.ImageView;import android.widget.ListView;import android.widget.TextView;import android.widget.Toast;

public class MainActivity extends Activity {

private ListView lista;

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.listado);

ArrayList<Lista_entrada> datos = new ArrayList<Lista_entrada>();

datos.add(new Lista_entrada(R.drawable.im_buho, "BUHO", "Búho es el nombre común de aves de la familia Strigidae, del orden de las estrigiformes o aves rapaces nocturnas. Habitualmente designa especies que, a diferencia de las lechuzas, tienen plumas alzadas que parecen orejas.")); datos.add(new Lista_entrada(R.drawable.im_colibri, "COLIBRÍ", "Los troquilinos (Trochilinae) son una subfamilia de aves apodiformes de la familia Trochilidae, conocidas vulgarmente como colibríes, quindes, tucusitos, picaflores, chupamirtos, chuparrosas, huichichiquis (idioma nahuatl), mainumby (idioma guaraní) o guanumby. Conjuntamente con las ermitas, que pertenecen a la subfamilia Phaethornithinae, conforman la familia Trochilidae que, en la sistemática de Charles Sibley, se clasifica en un orden propio: Trochiliformes, independiente de los vencejos del orden Apodiformes. La subfamilia Trochilinae incluye más de 100 géneros que comprenden un total de 330 a 340 especies.")); datos.add(new Lista_entrada(R.drawable.im_cuervo, "CUERVO", "El cuervo común (Corvus corax) es una especie de ave paseriforme de la familia de los córvidos (Corvidae). Presente en todo el hemisferio septentrional, es la especie de córvido con la mayor superficie de distribución. Con el cuervo de pico grueso, es el mayor de los córvidos y probablemente la paseriforme más pesada; en su madurez, el cuervo común mide entre 52 y 69 centímetros de longitud y su peso

Page 123: Fragments Android

varía de 0,69 a 1,7 kilogramos. Los cuervos comunes viven generalmente de 10 a 15 años pero algunos individuos han vivido 40 años. Los juveniles pueden desplazarse en grupos pero las parejas ya formadas permanecen juntas toda su vida, cada pareja defendiendo un territorio. Existen 8 subespecies conocidas que se diferencian muy poco aparentemente, aunque estudios recientes hayan demostrado diferencias genéticas significativas entre las poblaciones de distintas regiones.")); datos.add(new Lista_entrada(R.drawable.im_flamenco, "FLAMENCO", "Los fenicopteriformes (Phoenicopteriformes), los cuales reciben el nombre vulgar de flamencos, son un orden de aves neognatas, con un único género viviente: Phoenicopterus. Son aves que se distribuyen tanto por el hemisferio occidental como por el hemisferio oriental: existen cuatro especies en América y dos en el Viejo Mundo. Tienen cráneo desmognato holorrino, con 16 a 20 vértebras cervicales y pies anisodáctilos.")); datos.add(new Lista_entrada(R.drawable.im_kiwi, "KIWI", "Los kiwis (Apterix, gr. 'sin alas') son un género de aves paleognatas compuesto por cinco especies endémicas de Nueva Zelanda.1 2 Son aves no voladoras pequeñas, aproximadamente del tamaño de una gallina. Antes de la llegada de los humanos alrededor del año 1300, en Nueva Zelanda los únicos mamíferos que había eran murciélagos, y los nichos ecológicos que en otras partes del mundo eran ocupados por animales tan diversos como caballos, lobos y ratones fueron utilizados en Nueva Zelanda por pájaros (y en menor proporción por ciertas especies de reptiles). La denominación kiwi es maorí, idioma del pueblo homónimo de linaje malayopolinesio que colonizó Nueva Zelanda antes de la llegada de los europeos.")); datos.add(new Lista_entrada(R.drawable.im_loro, "LORO", "Las Psitácidas (Psittacidae) son una familia de aves psitaciformes llamadas comúnmente loros o papagayos, e incluye a los guacamayos, las cotorras, los periquitos, los agapornis y formas afines.")); datos.add(new Lista_entrada(R.drawable.im_pavo, "PAVO", "Pavo es un género de aves galliformes de la familia Phasianidae, que incluye dos especies, el pavo real común (Pavo cristatus) y el pavo real cuelliverde (Pavo muticus).1")); datos.add(new Lista_entrada(R.drawable.im_pinguino, "PINGÜINO", "Los pingüinos (familia Spheniscidae, orden Sphenisciformes) son un grupo de aves marinas, no voladoras, que se distribuyen únicamente en el Hemisferio Sur, sobre todo en sus altas latitudes."));

lista = (ListView) findViewById(R.id.ListView_listado); lista.setAdapter(new Lista_adaptador(this, R.layout.entrada, datos){

@Override

Page 124: Fragments Android

public void onEntrada(Object entrada, View view) {

if (entrada != null) { TextView texto_superior_entrada =

(TextView) view.findViewById(R.id.textView_superior); if (texto_superior_entrada != null)

texto_superior_entrada.setText(((Lista_entrada) entrada).get_textoEncima());

TextView texto_inferior_entrada = (TextView) view.findViewById(R.id.textView_inferior);

if (texto_inferior_entrada != null)

texto_inferior_entrada.setText(((Lista_entrada) entrada).get_textoDebajo());

ImageView imagen_entrada = (ImageView) view.findViewById(R.id.imageView_imagen);

if (imagen_entrada != null)

imagen_entrada.setImageResource(((Lista_entrada) entrada).get_idImagen());

}}

});

lista.setOnItemClickListener(new OnItemClickListener() { @Overridepublic void onItemClick(AdapterView<?> pariente,

View view, int posicion, long id) {Lista_entrada elegido = (Lista_entrada)

pariente.getItemAtPosition(posicion);

CharSequence texto = "Seleccionado: " + elegido.get_textoDebajo(); Toast toast = Toast.makeText(MainActivity.this, texto, Toast.LENGTH_LONG); toast.show();

} });

}

}

Page 125: Fragments Android

Esperamos que haya sido aclaratorio y para entender muchas cosas sobre cómo se

construye una lista.

Aquí dejo para descargar el proyecto entero: Listado

En respuesta a las preguntas más comunes de nuestros lectores. Si quieres hacer otra cosa

con los listados, así como si has visto cosas que no has sabido como solucionar; e incluso,

con lo aquí aprendido, como hacer un GridView o un Spinner completamente

personalizados. Te recomiendo que eches una ojeada al artículo que expande los

conocimientos de ListView, se solventan muchas dudas y enseña mucho más,

en: http://jarroba.com/expansion-listview/

También puede ser muy interesante el artículo sobre programar para diferentes dispositivos

Android con la arquitectura de Fragments, en el que extiendo un poco más el ejemplo de

listado y añado conceptos avanzados.Entradas Relacionadas

Activity – entender y usar una Actividad

Preparar las herramientas necesarias para programar en Android

Cambiar la clave por defecto de acceso al Menú de Configuración del Router ADSL

(Seguridad Mínima)

Page 127: Fragments Android

Bibliografía

http://jarroba.com/activity-entender-y-usar-una-actividad/

http://jarroba.com/programar-fragments-fragmentos-en-android/

http://jarroba.com/fragments-fragmentos-en-android/

http://jarroba.com/context-de-android/

http://jarroba.com/intent-pasar-datos-entre-activity-app-android-video/

http://jarroba.com/listview-o-listado-en-android/

http://jarroba.com/fragments-fragmentos-en-android/