Sabemos que podemos cargar varios fragmentos en una actividad, de forma que dependiendo de los tamaños de pantalla, las resoluciones o las orientaciones de los dispositivos podamos reutilizarles. El ejemplo más típico es una lista (listView) que cuando se hace click en un elemento muestra el detalle. Para móviles tendríamos dos actividades cada una de ellas llamando a un fragmento; la lista y el detalle. Sin embargo, en una tableta, tendríamos una única actividad que cargaría en pantalla a la vez tanto la lista como el detalle.
Cada vez, vamos a encontrarnos más ocasiones en que nos vemos obligados a cargar en pantalla un fragmento en vez de una actividad. Por ejemplo, si utilizamos el navigation drawer, cada una de las opciones nos va a cargar un fragmento. Otro ejemplo, sería si utilizamos la librería View pager indicator. En estos casos, también podemos necesitar tener una estructura de fragmentos con lo cuál necesitamos cargar desde un fragmentos varios fragmentos.
Afortunadamente, Android nos permite también que un fragmento a su vez cargue varios fragmentos. Los fragmentos anidados fueron soportados a partir de Android 4.2 o superior y para el resto de las versiones a través de la versión 11 de la librería de soporte. La única limitación es que no vamos a poder utilizar el atributo fragment dentro de nuestros layouts sino que tendremos que cargar los fragmentos dinámicamente mediante la clase FragmentManager.
Disponemos de dos nuevos métodos que nos permiten acceder tanto al fragmento padre con getParentFragment y obtener un objeto FragmentManager para la carga de los fragmentos hijos con getChildFragmentManager.
layout-sw600/anidados_sw600.xml
Este ejemplo es el layout que llamaríamos desde el fragmento padre y que contiene a su vez tres fragmentos anidados; una lista listaFragmento y dos fragmentos con detalles; detalle1Fragmento y detalle2Fragmento. No utlilizamos directamente el atributo <fragment> para indicar qué fragmento va a contener cada sección. Simplemente utilizamos un layout de tipo FrameLayout para ubicar desde código ahí el fragmento.
TabletFragment.java
public class TabletFragment extends Fragment implements ListaFragmento.onListaSelectedListener{ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.anidados_sw600, container, false); Fragment detalle1Fragmento = new Detalle1Fragmento(); Fragment detalle2Fragmento = new Detalle2Fragmento(); Fragment listaFragmento = new ListaFragmento(); FragmentTransaction fragmentTransaction = this.getChildFragmentManager().beginTransaction(); fragmentTransaction.add(R.id.listaFragment, listaFragmento); fragmentTransaction.add(R.id.detalle1Fragmento, detalle1Fragmento); fragmentTransaction.add(R.id.detalle2Fragmento, detalle2Fragmento); fragmentTransaction.commit(); return view; }
Utilizamos el método onCreateView de nuestro fragmento padre para cargar dinámicamente la estructura de fragmentos en el layout. Creamos una instancia de cada uno de los fragmentos que deseamos cargar. Utilizamos el método getChildFragmentManager para obtener un objeto FragmentManager. Mediante el método beginTransaction le indicamos que vamos a empezar a añadir fragmentos (este método nos devuelve un objeto FragmentTransaction mediante el cual podemos añadir (add) nuestros fragmentos) y confirmamos que el proceso es completo con el método commit.
El proceso es el mismo que si estuviésemos añadiendo en una actividad fragmentos dinámicamente pero utilizando el método específico getChildFragmentManager.
Tanto si trabajamos con fragmentos cargados desde una actividad como con fragmentos anidados debemos perservar siempre la independiencia del fragmento en cuanto a comunicación con otros objetos se refiere. Es decir, la forma de pasar parámetros debería de hacerse a través de un listener, de forma, que el fragmento funcione igual se cargue desde una actividad u otra o desde un fragmento padre u otro.
ListaFragmento.java
public class ListaFragmento extends Fragment implements OnItemClickListener{ //interfaz fragmento onElementoSelectedListener mCallback; //Interfaz para comunicación con otros fragmentos del //ID de elemento seleccionadao public interface onElementoSelectedListener { public void onElementoSelected(Long idElemento); } @Override public void onItemClick(AdapterView parent, View view, int position, long id) { mCallback.onElementoSelected(elementos[position].getID()); } @Override public void onAttach(Activity activity) { super.onAttach(activity); try{ Fragment parentFragment = this.getParentFragment(); mCallback = (onElementoSelectedListener)parentFragment; }catch(ClassCastException e){ throw new ClassCastException(activity.toString() + " must implement OnElementoSelectedListener"); } }
En nuestros fragmentos definiremos una interfaz. En nuestro caso, el único método de la interfaz es onElementoSelected que tiene como atributo el id del elemento que hemos pulsado.
public interface onElementoSelectedListener { public void onElementoSelected(Long idElemento); }
En el método onItemClick pasamos el id del elemento pulsado.
@Override public void onItemClick(AdapterView> parent, View view, int position, long id) { mCallback.onElementoSelected(elementos[position].getID()); }
No debemos olvidar, instanciar nuestra variable de instancia. Si trabajamos con actividades en este método tenemos disponible la actividad que está cargando el fragmento. En este caso, estaríamos trabajando con fragmentos anidados, con lo que obtenemos la referencia al fragmento padre mediante el método getParentFragment().
@Override public void onAttach(Activity activity) { super.onAttach(activity); try{ Fragment parentFragment = this.getParentFragment(); mCallback = (onElementoSelectedListener)parentFragment; }catch(ClassCastException e){ throw new ClassCastException(activity.toString() + " must implement OnListaSelectedListener"); } }
En nuestra actividad o fragmento padre implementamos la interfaz. Ahora, ya vamos a saber cuando se ha efectuado alguna acción en nuestros fragmentos y podemos tomar las decisiones oportunas desde el fragmento padre (o actividad) que deben ser los encargados de gestionar lo que suceda con los fragmentos.
En nuestro ejemplo, cuando el usuario pulsa un elemento de la lista, mandamos el id del elemento a los fragmentos detalle. Para pasar parámetros a los fragmentos usamos el método setArguments(Bundle bundle) que podemos recuperar en el fragmento con el método getArguments.
TabletFragment.java
public class TabletFragment extends Fragment implements ListaFragmento.onListaSelectedListener{ @Override public void onListaSelected(Long idElemento) { Fragment detalle1Fragmento = new Detalle1Fragmento(); Fragment detalle2Fragmento = new Detalle2Fragmento(); Bundle bundle = new Bundle(); bundle.putLong(Constantes.ID_ELEMENTO, idElemento); detalle1Fragment.setArguments(bundle); detalle2Fragment.setArguments(bundle); FragmentManager fManager = this.getChildFragmentManager(); FragmentTransaction fragmentTransaction = fManager.beginTransaction(); fragmentTransaction.replace(R.id.detalle1Fragmento, detalle1Fragmento); fragmentTransaction.replace(R.id.detalle2Fragmento, detalle2Fragmento); fragmentTransaction.commit(); } ...