Fragments Dinámicos en Android

En esta entrada vamos a ver como podemos usar Fragments pero de manera dinámica. Esto quiere decir que podemos cambiar entre unos y otros según la disposición del dispositivo, o según el tamaño de la pantalla. En este caso vamos a hacerlo de manera que cuando cambiemos la orientación del dispositivo, cambiará el fragment.

Este pequeño tutorial lo he hecho con la librería de soporte de los Fragments, para poder usarlo en versiones de Android anteriores a la 3.0 HoneyComb. De esta manera, nuestra aplicación funcionará por igual en versiones anteriores y posteriores, pero si en otro caso quisieramos solo desarrollar para versiones superiores, solo deberíamos cambiar un par de nombres de métodos que vamos a usar, que os avisaré en su debido momento.


Para empezar, vamos a crear nuestro proyecto android, el cual llamaremos como queramos, y en mi caso se llama “EjemploFragmentDinamicos”. Cuando salga la ventana para dar nombre a nuestra Activity.

Una vez creado el proyecto tendremos nuestra primera Activity creada y su layout.xml también creado.

Ahora vamos a empezar creando nuestros Fragments, dejando un poco de lado nuestra Activity, ya que volveremos después para aplicarle el código.

  • Fragment1

    Este es el nombre que le pondremos a nuestro primer Fragment. Para crearlo, tenemos que crear una clase nueva de java que herede de la superclase Fragment y sustituir el método onCreateView(). Una vez tengamos nuestra clase creada, procedemos a crear un nuevo archivo xml en la carpeta res/layout, para crear la interfaz que tendrá nuestro Fragment1:

    		<?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:background="#00FF00"
    		    android:orientation="vertical" >
    		
    		    <TextView
    		        android:layout_width="fill_parent"
    		        android:layout_height="wrap_content"
    		        android:text="Este es el Fragment #1"
    		        android:textColor="#000000"
    		        android:textSize="25sp" />
    		
    		</LinearLayout>
    		

    Como vemos, no tiene nada del otro mundo, un fondo de un color muy chillón, para diferenciarlo bien en la ejecución, y un TextView que nos informa que es el Fragment 1.

    En el código java de la clase, solo vamos a sustituir el método onCreateView, que retornará un View, en este caso, será un View que habrá sido inflado por nuestro fragment1.xml.

    		public class Fragment1 extends Fragment {
    
    			@Override
    			public View onCreateView(LayoutInflater inflater, ViewGroup container,
    					Bundle savedInstanceState) {
    				// TODO Auto-generated method stub
    				return inflater.inflate(R.layout.fragment1, container, false);
    			}
    		
    		}
    		
  • Fragment2

    Este es el nombre que tendrá nuestro segundo Fragment. Para crearlo vamos a seguir las mismas pautas que el Fragment 1, una nueva clase java que herede de Fragment, y a continuación crearemos el archivo xml que irá asociado a este Fragment.

    		<?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:background="#FFFE00"
    		    android:orientation="vertical" >
    		
    		    <TextView
    		        android:layout_width="fill_parent"
    		        android:layout_height="wrap_content"
    		        android:text="Este es el Fragment #2"
    		        android:textColor="#000000"
    		        android:textSize="25sp" />
    		
    		</LinearLayout>
    				

    Las diferencias con el anterior no son demasiado grandes, por lo que vamos con el código java, el cual tampoco es diferente excepto por el layout que utiliza:

    				public class Fragment2 extends Fragment {
    			
    			@Override
    			public View onCreateView(LayoutInflater inflater, ViewGroup container,
    					Bundle savedInstanceState) {
    				// TODO Auto-generated method stub
    				return inflater.inflate(R.layout.fragment2, container, false);
    			}
    		
    		}
    		
  • FragmentDinamico

    Este es el nombre que he elegido para mi Activity principal, aquella que se nos creó al inicio junto al proyecto. La diferencia en esta activity respecto a una en la que usamos Fragments de manera estática, es que aquí, según la rotación de la pantalla, usaremos un Fragment u otro, todo ello gestionado por el código que vamos a insertar.

    Primero vamos a ver lo que contiene su archivo xml:

    		<LinearLayout 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:orientation="horizontal"
    		    tools:context=".DinamycFragmentsActivity" >
    		
    		</LinearLayout>
    		

    Esta vez aquí no hemos agregado los como hicimos con los Fragment estáticos, porque como ya he dicho antes, aquí los manejaremos por el código.

    Veamos lo que contiene el código java de nustra actividad principal:

    		public class FragmentDinamico extends FragmentActivity {
    
    			@Override
    			protected void onCreate(Bundle savedInstanceState) {
    				super.onCreate(savedInstanceState);
    				setContentView(R.layout.activity_dinamyc_fragments);
    		
    				FragmentManager fragmentManager = getSupportFragmentManager();
    				FragmentTransaction fragmentTransaction = fragmentManager
    						.beginTransaction();
    		
    				WindowManager wm = getWindowManager();
    				Display d = wm.getDefaultDisplay();
    				
    				if (d.getRotation() == Surface.ROTATION_90){
    					Fragment1 fragment1 = new Fragment1();
    					fragmentTransaction.replace(android.R.id.content, fragment1)
    							.commit();
    				}else {
    					Fragment2 fragment2 = new Fragment2();
    					fragmentTransaction.replace(android.R.id.content, fragment2)
    							.commit();
    				}
    				
    			}
    		
    		}
    		

    Tenemos que analizar ciertos aspectos:

    • FragmentManager

      Es una clase que nos permite interactuar con Fragment dentro de una Activity.

    • FragmentTransaction

      Es la API que nos permite realizar transacciones con los Fragment.

    • getSupportFragmentManager()

      Este método es el usado si se quiere usar Fragment dinámicos en versiones de Android inferiores a 3.0, en el caso de las superiores y la 3.0 incluida, el método que debe ser llamado es getFragmentManager().

    Visto este resumen sobre los 3 puntos que hemos visto, en el código, lo primero que hacemos es declarar una variable de referencia de la clase FragmentManager, el cual inicializamos con el método getSupportFragmentManager(). Con esto podremos administrar los Fragments mas tarde.

    A continuación creamos otra nueva variable de referencia pero esta vez de la clase FragmentTransaction, la cual es la encargada de realizar las transacciones entre fragment, e inmediatamente llamamos al método beginTransaction().

    En las 2 líneas siguientes hemos creado un objeto de la clase WindowManager, el cual usaremos para inicializar otro objeto de la clase Display. Con este objeto Display, podemos saber si el dispositivo ha sido rotado, y según su estado, usaremos el Fragment1 o el Fragment2.

    		if (d.getRotation() == Surface.ROTATION_90){
    			Fragment1 fragment1 = new Fragment1();
    			fragmentTransaction.replace(android.R.id.content, fragment1)
    					.commit();
    		}else {
    			Fragment2 fragment2 = new Fragment2();
    			fragmentTransaction.replace(android.R.id.content, fragment2)
    					.commit();
    		}
    		

    Si el método d.getRotation() es igual que el valor de la constante ROTATION_90 de la clase Surface, entonces instanciaremos la clase Fragment1, y mediante el objeto fragmentTransaction vamos a reemplazar el contenido por nuestro Fragment1. A continuación, se debe llamar al método commit() para que el cambio se lleve a cabo.

    Por el contrario, si la rotación no se ha producido, vamos a instanciar el Fragment2, y al igual que hemos hecho anteriormente, reemplazamos el contenido por el Fragment2, y luego, de nuevo llamamos al método commit().

Ahora nuestra aplicación es completamente válida para ser ejecutada, y ver como funciona. Recordamos, que si no se ha llevado a cabo rotación, debe aparecer el Fragment2, y si ha habido rotación de 90º del dispositivo, debe aparecer el Fragment1.

Dispositivo en Portrait:

FragmentPortrait

Dispositivo en Landscape:

FragmentLandscape

Como vemos, cuando el disopsitivo está en Portrait, el Fragment que aparece es el Fragment2, y si está en Landscape, el Fragment que aparece es el 1. A continuación podemos ver un vídeo de nuestra aplicación siendo ejecutada:


Con esta entrada ya hemos visto como podemos usar Fragments de manera dinámica según la rotación que se use en nuestra aplicación, aunque se puede aplicar por otros diferentes motivos, pero con esto ya tenemos la base para empezar a practicar nuevas cosas y descubrir otras tantas. El link de descarga del ejemplo puedes descargarlo de aquí.

Sin mas, cualquier corrección o aporte es bienvenido.

Saludos!!!

  • jrobayna

    Hola, buen aporte para los novatos en andorid, como es mi caso, Por favor, puedes indicar como implementar una aplicación masetro-detalle con fragmentos dinámicos?. He buscado en la red y no he nada convincente.
    Muchas gracias….

    • Buenas jrobayna, justo acabo de subir una entrada con esta duda, ya que me la hacen a menudo y he visto buena idea hacerla. Aqui la tienes: sekthdroid.wordpress.com/2013/04/10/implementar-maestro-detalle-con-fragments-en-android/

  • anabrc

    Hola queria hacerte dos preguntas,
    1. donde está el ‘activity_dinamyc_fragments’ ?? y que contiene??
    2. Si uso el getSupportFragmentManager me salia error entonces lo cambie a getFragmentManager peero ahora me sale error en los ‘replace’ del fragmentTransaction 🙁
    Por favor si puedes me ayudas. Graciassss 🙂

    • Buenas anabrc, perdona que conteste tan tarde,

      El xml con el nombre “activity_dinamyc_fragments” contiene lo siguiente:

      <LinearLayout 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:orientation="horizontal"
          tools:context=".DinamycFragmentsActivity" >
       
      </LinearLayout>
      

      Como ves no tiene contenido, simplemente tiene un LinearLayout que usaremos como contenedor para los Fragment.

      Respecto al error de usar getSupportFragmentManager() o getFragmentManager() puede ser por el sdk para el que estés programando. Si usas getSupportFragmentManager() estarás usando la librería de compatibilidad, por lo cuál debe ser empleado para aplicaciones cuyo objetivo sean dispositivos con android inferior a v3.0, y la Activity debe heredar de FragmentActivity. Sin embargo, si tu objetivo es solo cubrir dispositivos mayores a Android 3.0, deberías usar únicamente getFragmentManager(), y tu Activity heredará de Activity (tal cual nos viene).

      Por otro lado, lo mejor para detectar el error en el replace() es ver el log y ver que excepción te está dando y donde ;).

      Espero que sea de ayuda a pesar de la tardanza.

      Saludos!!