Fragments (fragmentos) en 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 https://jarroba.com/activity-entender-y-usar-una-actividad/. Para los ejemplo usaremos listados, que detallamos en https://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 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.
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:
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.
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):
- 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 https://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.
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):
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):
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() { } @Override public void onAttach (Activity activity) { super.onAttach(activity); Log.v(LOG_TAG, "onAttach"); } @Override public void onCreate (Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.v(LOG_TAG, "onCreate"); } @Override public 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; } @Override public void onActivityCreated (Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); Log.v(LOG_TAG, "onActivityCreated"); } @Override public void onViewStateRestored (Bundle savedInstanceState) { super.onViewStateRestored(savedInstanceState); Log.v(LOG_TAG, "onViewStateRestored"); } @Override public void onStart () { super.onStart(); Log.v(LOG_TAG, "onStart"); } @Override public void onResume () { super.onResume(); Log.v(LOG_TAG, "onResume"); } @Override public void onPause () { super.onPause(); Log.v(LOG_TAG, "onPause"); } @Override public void onStop () { super.onStop(); Log.v(LOG_TAG, "onStop"); } @Override public void onDestroyView () { super.onDestroyView(); Log.v(LOG_TAG, "onDestroyView"); } @Override public void onDestroy () { super.onDestroy(); Log.v(LOG_TAG, "onDestroy"); } @Override public void onDetach () { super.onDetach(); Log.v(LOG_TAG, "onDetach"); } }
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.
Que tal buen día,
Una duda, como hago el ejemplo que colocaste del fragmento y detalle, es decir estoy realizando un ejemplo de colocar una verdura y que contiene solo el nombre y una breve descripción, pero al darle click a la imagen quiero que me arroje un mayor detalle y volver a mostrar el nombre y la imagen, esto lo estoy haciendo de un fragment y al dar click consultar una 2da Activity de detalle, ¿es posible? o ¿como lo realizaste tu?
Buenas Daniel. Tienes como se hace en https://jarroba.com/programar-fragments-fragmentos-en-android/ y más ejemplos y explicaciones en nuestro libro gratuito de Android en https://jarroba.com/libro-android-100-gratis/
Hola, buen tutorial gracias por el tiempo.
Tengo una consulta, en el evento "onCreateView" el layout se asocia al fragment, pero…¿qué layout?
Saludos. Espero tu respuesta.
Los Fragments cambian ligeramente respecto a los Activities, pero son muy sencillos. Para asociarlo al Layour en el mismo onCreateView puede llamar a:
View rootView = inflater.inflate(R.layout.miLayout, container, false);
Donde rootView lo devuelves en el return del método onCreateView.
Hola muy buena enseñanasa pero tengo una pregunta
para agregar una busqueda ¿como se haria ?
Para añadir un SearchView tienes un ejemplo en http://developer.android.com/guide/topics/search/search-dialog.html
Hola buenas noches, llevo toda la tarde con el ejemplo de los fragments, me ha salido de la primera , vengo del vba y he trabajado con C# pero hace 5 años asi que estoy muy oxidado con las classes, tengo un problema, la aplicacion que estoy creando tiene una actividad principal, que desde unos buttons llamo a las demas actividades y todo muy bien , los frames muy buenos para lo que quiero realizar, pero me instala 2 iconos iguales la app , en uno abre la aplicacion perfecta y voy a todas las actividades y el otro icono , me abre los fragmes solamente, hay estoy perdido y no doy con el error.
un cordial saludo kinito y gracias por estas estupendas explicaciones
Prueba a desinstalar la aplicación desde el desinstalador y comprueba que no haya otra segunda con otra firma diferente; si es así desinstálala también y vuelve a probar
Hola Ramón, gracias por responder tan rápido, he desinstalado todo varias veces, hasta he cambiado el proyecto de carpeta y sigo con los dos iconos , debe haber algo en el código que se me escapa, como todavía estoy en pruebas y aprendiendo , creare un nuevo proyecto y are lo mismo a ver si lo arreglo.
Un cordial saludo kinito
Sin ver el código es complicado decirte mucho. Solo se me ocurre que tengas algo mal en el AndroidManifest.xml, revisalo.
Hola Ramon, acertadamente el error estaba en Manifest, problema en los nodos , ya esta solucionado , muchas gracias por tu interes.
Un cordial saludo kinito
Buenas tardes.
Estoy realizando una aplicación con fragments en la que mediante un FragmentLayout en la misma ventana. El problema que tengo es que cargo el fragment1, a continuación cargo el fragment2 y el fragment1 desaparece. Pero lo que sucede es que los botones que tenia el fragment 1 se pueden pulsar desde el fragment2 aunque el fragment1 uno no se vea. Es como si el fragment dos fuera transparente aunque si que tiene background y su propia vista.
Esto solo me sucede cuando la transacción la hago con add() y no con replace(). No entiendo a que se debe esto.
Un saludo.
Buenas Adrian, asegurate de que has sustituido correctamente el Fragment (tienes ejemplos de como hacerlo en http://jarroba.com/libro-android-100-gratis/)
Excelente explicación teórica y gracias por compartir estos temas, pasamos ahora a la segunda parte del tutorial y como lo mencione, está muy bien explicada y detallada la parte teórica de este artículo de Fragments.
¡Muchas gracias!
Hola Ramón, he leído casi todos vuestros posts y me parecen muy trabajados y divulgativos. Gracias.
Tengo una consulta relacionada con los fragments. Estoy acabando una aplicación en la que la pantalla más importante contiene datos de coordenadas, captación de un código, grabación y reproducción de audios, toma de fotos y visualización con una gallery, videos, medidas de sensores, etc. Dado que el número de líneas se acercaba a las diez mil, opté por usar fragments para intentar sacar código de la activity y hacerlo mas mantenible. No todos los fragments se ven a la vez, sino que lo visualizo en función de que actuemos sobre una pestaña (que realmente son botones simulados). Todos los fragments los cargo al cargar la activity y solo trabajo ocultando o no el layout correspondiente en función de la pestaña seleccionada.
Quisiera tu opinión sobre este uso de los fragments que, lo cierto, es que no me acaba de convencer aunque en un primer momento me pareció lo mas pertinente.
Un saludo y gracias. Carlos
Buenas Carlos,
Haces muy bien al dividir código en Fragments para hacer el código más mantenible, ya que los Fragments se pueden emplear tanto visibles como sólo de lógica.
Te recomiendo que uses pestañas de navegación (mira un ejemplo en http://developer.android.com/guide/topics/ui/actionbar.html#NavigationStyles). Así no tendrás que cargar todos los Fragments a la vez, únicamente utilizarás los necesarios cada momento y se descargarán cuando no se necesiten (si cargas todos de golpe, en un dispositivo con poca potencia lo más seguro que tendrás problemas de memoria).