Contenido sobre Android
Buscar
Social
Ofertas laborales ES
« Android wear preview developer | Main | Accesibilidad en Android »
miércoles
mar052014

Fragmentos anidados

 

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.

 

 

Carga de fragmentos anidados.

 

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.

 

Comunicación entre fragmentos.

 

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();
        
    }

...


References (1)

References allow you to track sources for this article, as well as articles that were written in response to this article.

Reader Comments (1)

Buen tuto, sobre todo para los no expertos como yo, con un problema tremendo.
Tengo el siguiente fragment (con varias imágenes) que proviene del menú:


import android.annotation.SuppressLint;
import android.app.Fragment;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.Toast;

@SuppressLint("NewApi")

public class Razas extends Fragment

implements View.OnClickListener {
ImageView imageAmerica;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.continentes, container, false);

imageAmerica = (ImageView) rootView.findViewById(R.id.imageAmerica);

imageAmerica.setOnClickListener(this);


return rootView;
}

@Override
public void onClick(View ImageView) {


Toast.makeText(this.getActivity(),
"Has pulsado America", Toast.LENGTH_LONG).show();


}

}

Hasta aquí funciona.
Pero quiero sustituir el Toast y declarar varias imágenes (en este fragment) y que al pulsar cada una me lance a unas activity llamadas america.xml, europa.xml,...etc.
Estoy perdido...

mayo 12, 2014 | Unregistered CommenterFernan

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>