Programar Fragments (fragmentos) en Android


Después del primer artículo (ver primera parte aquí), continuamos donde lo dejamos y preparados para programar con Fragments en Android. 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:

Diseño listado Fragments Android - www.Jarroba.com

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:

Diseño detalle Fragments Android - www.Jarroba.com

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):

Diseño detalle layouts Fragments Android - www.Jarroba.com

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.

Diseño listado layouts  Fragments Android - www.Jarroba.com

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).

Diseño tablet Fragments Android - www.Jarroba.com

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.

Diseño tablet Layouts Fragments Android - www.Jarroba.com

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):

Directorio Layout - www.Jarroba.com

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á.

Fragments estaticos y dinamicos de Android - www.Jarroba.com

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.

Layout tablet Fragments Android - www.Jarroba.com

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"
        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. Para layout_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"
        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"
        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”.

Directorio Values - www.Jarroba.com

Tendremos 3 carpetas “values” (más información en http://developer.android.com/guide/practices/screens_support.html#DeclaringTabletLayouts):

  • 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).
  • 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:

Tipo de pantalla influye en lo mostrado en la aplicacion Fragments Android - www.Jarroba.com

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.

Estructura completa de la aplicacion  Fragments Android - www.Jarroba.com

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:

  1. 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.
  2. 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):

Listeners y callback Fragments Android - www.Jarroba.com

 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.

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;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.layout_activity_principal_listado);

		if (findViewById(R.id.framelayout_contenedor_detalle) != null) {
			dosFragmentos = true;
		}
	}

	@Override
	public 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);
		}
	}
}

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

	@Override
	public 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
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
		View rootView = inflater.inflate(R.layout.layout_fragment_detalle, container, false);

		//Mostramos el contenido al usuario
		if (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.app.*" (utiliza "android.support.v4.app.*" si vas a desarrollar para versiones muy antiguas de Android, tal como la 2.1). 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.app.FragmentManager" y "android.app.Fragment" ).

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() {
		@Override
		public void onEntradaSelecionada(String id) {
		}
	};

	public Fragment_Lista() {
	}

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

        setListAdapter(new Lista_adaptador(getActivity(), R.layout.layout_elemento_listado, Lista_contenido.ENTRADAS_LISTA){
			@Override
			public 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);
		        }
			}
		});

	}

	@Override
	public 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;
	}

	@Override
	public void onDetach() {
		super.onDetach();
		mCallbacks = CallbacksVacios;
	}

	@Override
	public 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 {

	@Override
	protected 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 actividad
			Bundle 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);
			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 ..."));
		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"
    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 “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).

Nuevo aplicacion Android con Fragments - www.Jarroba.com

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 😀

Comparte esta entrada en:
Safe Creative #1401310112503
Programar Fragments (fragmentos) en Android por "www.jarroba.com" esta bajo una licencia Creative Commons
Reconocimiento-NoComercial-CompartirIgual 3.0 Unported License.
Creado a partir de la obra en www.jarroba.com

194 comentarios en “Programar Fragments (fragmentos) en Android”

  1. Hola Ramón, gracias por el tutorial. Aunque ha pasado mucho tiempo desde que lo publicaste, te hago una consulta.

    cuando creas CallbacksVacios lo haces instanciando Callbacks que es un interface. Tenía entendido que no se podía instanciar los interfaces por ser abstractas. ¿Es correcto?. Un saludo. Carlos Salvador

  2. Buenas estoy tratando de aplicar el codigo y me quede atrapado en  (android:name="jarroba.invarato.fragmentos.Fragment_Lista")  me sale en rojo; especificamente a que hace referencia.. como quitarlo de la conotacion de alerta en android studio.

    Saludos Muy buen manual-

    1. Hola Dario. Lo que ocurre es que tienes que poner el nombre del paquete y del proyecto que hayas puesto tú en el atributo “android:name” 😉

  3. Hola Ramón, antes de todo, muy buen post, yodo muy claro y muy bien explicado.

    Estoy desarrollando un app que hace algo parecido a esta del ejemplo, muestro una lista de opciones y al pulsar sobre una de ellas, en vez de mostrar un fragment dinamico como muestras tu en la parte del móvil, quiero mostrar los elementos que hay en otra lista con fragments dinámicos mostrando cada elemento en un fragment y que cambien de forma automatica cada X segundos, pero se queda la pantalla en negra y al final solo aparece el último fragment (si en vez de replace uso add al final se muestran todos los fragments uno encima de otro).  Lo que pienso es que carga los fragment pero hasta que no finaliza la Activity que deriva de Fragment Activity no carga la vista.

    Dentro del FOR para cargar las vistas tengo:
                        getSupportFragmentManager().beginTransaction().replace(R.id.framelayout_contenedor_bits, fragment).commit();

    ¿algún consejo que me pueda ayudar para que los muestre todos y vayan cambiado cada X tiempo?

    Muchas gracias

  4. Buenas tardes. En un fragment tengo un listview, cuando pulso en un elemento del listview me lleva a otro fragment para editar el contenido del elemento pulsado del listview. En este segundo fragment tengo un edittext y un botón enviar. Cuando pulso el botón enviar entonces vuelvo a mostrar el listview y el elemento aparece modificado. Pero ahora viene el problema (yo creo que es un problema): cuando vuelvo a hacer el mismo proceso entonces el primer elemento del listview que modifiqué vuelve a su estado original. 

    Por ejemplo: una lista con números del cero al diez….pulso el uno, uno por por ejemplo 1, pulso enviar, aparece otra vez la lista y veo "1" en vez de "uno……..ahora por ejemplo pulso el siete, hago el mismo proceso y al volver a mostrar la lista de número el "1" aparece de nuevo como "uno".

    Creo que lo que ocurre es que vuelvo a generar de nuevo la lista y que para que todos los cambios queden guardados necesito Shared Preferences o File o DB.

    Gracias.

    1. Hola Antonio. Lo mejor es que guardes el estado, para si se recarga el Fragment o la Activity volver a cargarlos en caso de necesidad (según el ciclo de vida de Fragment y Activity) 🙂

  5. Tengo una duda, como puedo utilizar uno de los botones de animales que vienen en el ejemplo, pero que en vez de tomar el texto, pudiera desplegar un activity nuevo, y allí poder implementar algo personalizado ???

      1. No entendí, 🙁  , mi intención es conservar el comportamiento de todos los botones, excepto uno, donde quiero mostrar una activity que contiene un layout y éste texto y un botón.

        Se podrá ??? 

        Gracias de antemano.

         

  6. Hola Ramón, tengo una pregunta que no sé si es posible, por lo que he leído en el post(muy bueno, por cierto) un fragment tiene una estructura concreta. Si yo quisiera "reemplazar" una estructura de imagen, texto en el mismo sitio(Activity) pero por otra que por ejemplo sea texto sólo, que tendría que hacer? No sé si es una estupidez o no me he enterado bien, muchas gracias. 

    1. Hola Alfred. Se puede perfectamente, pero te tienes que asegurar que el Fragment sea dinámico para poner el que quieras y reeplazarlo cuantas veces sea necesario 🙂

  7. Hola: Ramon gracias por compartir tu conocimiento: tengo una aplicacion la cual cuenta con dos botones en la actividad principal, la idea es que al darle click en ellos, me envíen a la actividad del recyclerview el cual sería el mismo para los dos botones, lo unico es que al darle click en el botón 1 muestre la información solo de ese botón y en el botón 2 al darle click se muestre solo la información de ese botón, agradezco toda la información que me puedas dar, gracias.

  8. Hola la pregunta que voy a hacer es muy simple pero estoy aprendiendo y vengo del mundo del cobol o sea que me falta mucho.

    Por ejemplo he bajado el zip con el codigo, lo he extraido y con el Android Studio, he ido a open project y he seleccionado directamente la carpeta.

    Pero no se me abre bien. Se queda así.

    Esto es porque esta hecho en eclipse?

    Si es así, sabes algun sitio que explique claramente y para lerdos como yo, como cargarlo

    Gracias

     

    1. Correcto, el ZIP que has descargado es para Eclipse y debido a configuraciones diferentes no puedes importarlo de golpe en Android Studio. Puedes realizar una importación de un proyecto de Eclipse a Android Studio con http://developer.android.com/sdk/installing/migrate.html. Cambian los proyectos, el código de este artículo es el mismo tanto para Android Studio como para Eclipse. Te recomiendo que crees un nuevo proyecto de Android Studio y vayas creando los ficheros desde cero con su código, fijándote en el artículo, de este modo lo irás viendo como funciona 🙂

  9. Gracias por este artículo, Ramón. Tengo una duda, estoy con una activity que extiende de  AppCompatActivity porque uso una barra de acciones. Me encuentro el problema que ya no puedo extender de FragmentActivity. ¿Cómo podría resolver esto?. Un saludo y gracias. Carlos

    1. No os molesto con esta pregunta. He visto que AppCompatActivity hereda de FragmentActivity, con lo que queda resuelto el problema. Se extiende de AppCompatActivity  y tenemos todo. Gracias de todos modos. Un saludo.

  10. buenas amigo tengo una duda al momento de pasar una variable desde un fragment a una activity como seria….
    aqui mi codigo del fragment.

    public View onCreateView(LayoutInflater inflater, ViewGroup container,
    Bundle savedInstanceState) {
    View r = inflater.inflate(R.layout.activity_search, container, false);
    btnsearch = (ImageButton) r.findViewById(R.id.btnsearch);
    txtkeyword=(EditText)r.findViewById(R.id.txtkeyword);
    btnsearch.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View view) {
    Intent i = new Intent(getActivity(), ListResult.class);
    i.putExtra("texto", txtkeyword.getText().toString());
    startActivity(i);

    }
    });

    return r;
    }

    y aqui cuando lo recibo en mi actividad.

    Intent myIntent = getIntent();
    searchkey = myIntent.getStringExtra("texto");

    me puedes decir en donde estoy cometiendo el error al pasar la variable?

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

      //Aquí van todos los elementos a los cuales les haré llamamiento
      ibAcercade = (ImageButton)rootView.findViewById(R.id.ibAcercade);

      ibAcercade.setOnClickListener(new View.OnClickListener()
      {
      @Override
      public void onClick(View paramAnonymousView)
      {
      Intent intent = new Intent(getActivity(),AcercaDe.class);
      startActivity(intent);
      }
      });

      return rootView;

      }

  11. Hola! Gracias por el tutorial esta muy bien

    Tengo una pregunta, en un fragment tengo un boton, necesito que al presionar ese boton, se redireccione a una activity!

    Como lo hago?

  12. Una consulta, puedo usar fragments con recyclerview? en este ejercicio para presentar la lista utilizan una clase que extiende de ListFragment? mi pregunta es que si se podría usar en vez de ListFragment un RecyclerView para mostrar una lista horizontal? si pudieran ayudarme con un ejemplo sería de mucha ayuda

  13. Hola Ramón, 

    Tengo un problema y agaradecería si pudieras ayudarme, tengo un proyecto con Cajón de Navegación (Navigation Drawer), desde  las opciones del cajón de navegación dirijo a distintos "activitys" que heredan (extends) de fragment para poder que  funcionen. Resulta que quiero generar una base de datos SQLite pero al parecer no me quiere leer el construtctor  de la clase de base de datos en las activitys que heredan de fragament. Cree otro proyecto sólo con la activity y la clase de base de datos, hice que esa activity heredara de Activity y no de Fragment y sí leyó el constructor de la clase de base de datos,obciamente tuve que dejar de lado el concepto de cajón de navegación. En resumen la pregunta es: Cómo hago para que una activity que hereda de Fragment lea el constructor de una clase de base de datos que hereda de SQLiteOpenHelper?

    1. Hola Bernado. Una Activity no puede heredar de Fragment, si la clase hereda de Fragment se convierte en un “fragmento”, un Fragment está contenido siempre en una Activity. La clase SQLiteOpenHelper la tienes que hacer desde una clase aparte (tienes ejemplos en http://developer.android.com/training/basics/data-storage/databases.html). No deberías de tener ningún problema para realizar consultas tanto desde un Activity como desde un Fragment.

  14. Gracias por el tutorial, me gustaría saber cómo enviar los datos de una variable del activity, a uno de los fragments, ya pase los datos de un activity a otro con intent, pero no encuentro cómo enviarlos de dicho activity a uno de sus fragments, saludos y gracias.

    1. Es muy sencillo. Tienes un montón de ejemplos explicados en nuestro libro gratuito de android http://jarroba.com/libro-android-100-gratis/

          1. Y cómo puedo aplicar el mismo principio del ejemplo si mis fragments los declaré con ActionBar.Tab?

          2. De igual manera, casi todo se basa en aplicar programación por eventos 😉

            De cualquier manera no es la única forma de pasar información, aunque es la mejor.

  15. Hola hola!!

    Tengo un peque problema que no se como resolver.

    Tengo un fragment que tiene un list view. Cuando selecciono un elemento del listView, se muestra un layout(activity) con los datos correspondientes. Modifico alguna cosa y guardo. Al regresar al fragment que tiene el listView, no se actualiza la lista…!!! Cómo soluciono eso???

    Agradezco la atención!!! de veras 🙂

    1. Hola. Para que la lista se refresque después de un cambio tienes que llamara a notifydatasetchanged() del adapter que esté utilizando el listado.

  16. Que tal, es muy util toda la informacion, pero quisiera que me saquen de una duda.

     

    Estoy programando un menu de servicios, que al seleccionar una opcion sumara el valor de este servicio y lo ira mostrando, mas no se como hacerlo, etendi las opciones, pero que pasar si me extralimito de opciones, iran recorriendose como si se tratara de un spinner o como hago  el efecto de desplazamiento???

     

    de antemano, gracias

    1. Puedes realizar un efecto de desplazamiento con Swipe de Views. Tienes un ejemplo en: http://developer.android.com/training/implementing-navigation/lateral.html

  17. Un gran Post pero tengo una duda.

    Yo estoy desarrollando una aplicacion con NavigationDrawer(Menu lateral),y tengo un frameLayout donde voy mostrando el fragment que corresponde con la pestaña seleccionada.(Para esto uso la libreria support.v4 y un replace sin problema)perooooo….

    Tengo una pestaña que es de Notificaciones donde tengo creado un FragmentPreferences(Y este no admite la libreria support.v4)

    Con lo cual no me funciona correctamente el replace, y no puedo aplicar la solución que tu das en este post 🙁

    Los fragments que tengo creados con la libreria suppor.v4 funcionan correctamente y se remplazan bien pero a la hora de llamar al otro (usa la android.app) se me queda fijo en el FrameLayout que utilizo para mostrarlos:( Los otros cambian pero ese no.No encuentro solucion al problema.

    Necesito alguna respuesta, se agradece muchisimo!!

    Un saludo;)

    1. Hola Miguel. Te recomiendo que no utilices la biblioteca “support.v4” y menos que las mezcles. Utiliza directamente la nueva 😉

  18. Hola como muchos antes quiero dar las gracias por tomarse el tiempo y dar estas valiosas explicaciones.  

    Por otro lado estoy usando tu ejemplo y quiero añadir un menu, antes explico la idea que quiero hacer:

    1.- En modo smartphone en el listado (Activity_Principal) quiero mostrar un menu con dos opciones: opción A y opción B. 

    2.- En modo smartphone en el detalle quiero mostrar otro menu que tenga 4 opciones: opción A, B, C y D.

    En ambos casos las opciones A y B realizaran lo mismo, y las opciones C y D abriran activitys distintas de acuerdo a la información del detalle.

    3.- Para cuando sea modo Tablet requiero que se muestren siempre las opciones: A, B, C y D. En ambos casos smartphone y tablet las opciones del menu van a realizar las mismas funciones.

    Solucion y errores que llevo:

    Ya defini 3 archivos xml: menu_listado_a_b.xml, menu_detalle_c_d.xml, menu_tablet_a_b_c_d.xml 

    Para la idea 1 en modo smartphone: Ya defini un archivo menu_listado_a_b.xml con dos opciones y lo invoco en el metodo onCreateOptionsMenu en la clase Activity_Principal el menu ya se muestra hasta aqui todo funciona, el menu solo se crea cuando dosFragmentos  = false.

    Para la idea 2 en modo smartphone: El archivo menu_detalle_c_d.xml con las opciones C y D lo invoco en la clase Activity_Detalle, el método nunca se invoca (nunca se llama) y por lo tanto no se crea el menu, no me marca error solo no se lanza

     @Override
        public boolean onCreateOptionsMenu(Menu menu) {

            getMenuInflater().inflate(R.menu.menu_detalle_c_d, menu);

            return super.onCreateOptionsMenu(menu);
        }

    Igual comente el codigo anterior de la clase Activity_Detalle y defini en la clase Fragment_Detalle el siguiente código:

     

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

           .
           .
           .
             setHasOptionsMenu(true); //para que se lance onCreateOptionsMenu pero no se invoca
        }

     

     @Override
        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {

            if (dosFragmentos){
                inflater.inflate(R.menu.menu_tablet_a_b_c_d, menu);
            }
            else{
                inflater.inflate(R.menu.menu_detalle_c_d, menu);    
            }

            super.onCreateOptionsMenu(menu, inflater);

        }

    **la variable dosFragmentos se la paso como arguments (true o false) para en el detalle saber que menú mostrar, pero no funciona no se lanza el metodo onCreateOptionsMenu

    Me pueden orientar que estoy haciendo mal?? o si hay una mejor manera de implementar mi idea

    Basicamente lo que quiero es mostrar las distintas opcines del menu de acuerdo al tipo de vista smartphone o tablet.

    Saludos!!

     

     

    1. El problema es onCreateOptionsMenu() que solo se ejecuta la primera vez que se muestra el menú para prepararlo. Si quieres actualizar el menú, tienes que utilizar onPrepareOptionsMenu(), que se llama cada vez que se muestra el menú.

      1. Hola gracias, finalmente encontre el detalle del proque ni la primera vez se lanzaba en el fragment el onCreateOptionsMenu y tampoco onPrepareOptionsMenu, era porque estaba probando el funcionamiento de toolbar con tu ejemplo y me faltaba hacer lo mismo en Activity_Detalle, al inicializar el componente toolbar con eso funciono.

        Gracias, seguire tus ejemplos me parecen muy completos para los que estamos iniciando, saludos!!

         

  19. Hola Ramon, lo primero agradecerte el inmenso trabajo que realizas con estos tutoriales, y por supuesto con tu libro, dandonos a todos los que te leemos la oportunidad de poder aprender mas en esto de Android.

    En segundo lugar, quisiera hacerte un par de consultas, a ver si es posible que me orientes un poco (o mucho).

    He seguido este tutorial al dedillo y todo va perfecto, todo va bien, pero mi pregunta es, ¿como podria implementar la ActionBar  y tabs en la activity principal?

    y,los datos que nos muestra el ListView, vienen desde una lista, ¿como se haria para que se obtuvieran desde una base de datos externa?

    De nuevo, mil gracias por enseñarnos. Un saludo

     

     

    1. De momento no tenemos artículos sobre lo que necesitas. Te voy a recomendar de la web oficial donde tienes ejemplos de cada cosa:

      • Para el ActionBar tienes los ejemplos en: http://developer.android.com/guide/topics/ui/actionbar.html
      • Para implementar los Tabs puedes seguir el siguiente tutorial: http://developer.android.com/training/implementing-navigation/lateral.html

      Entiendo que base de datos externa te refieras a una base de datos en un servidor. Desde la base de datos lo mejor es que conviertas los datos JSON y los envíes al móvil:

      • Para JSON: http://developer.android.com/reference/android/util/JsonReader.html
      • Para realizar la conexión con el servidor desde el móvil: http://developer.android.com/training/basics/network-ops/connecting.html
  20. Hola Ramón.

    Antes de nada quería darte las gracias por compartir tus conocmientos, son de mucha ayuda y el tutorial es estupendo, muy sencillo y bien explicado.

    Te cuento, he seguido el tutorial al pie de la letra (para practicar los fragments), y tengo el código exactamente igual, cuando lo ejecuto en el emulador, me sale la lista y hago click en una de las opciones y se queda la pantalla en blanco, es decir, fragment_detalle no se muestra como debería, he probado con otros emuladores y con mi smartphone y nada, el resultado es el mismo, no tengo ningún error de compilación ni de sintaxis, por lo que no estoy segura de que es lo que he hecho mal, la verdad no lo entiendo, si pudieras ayudarme te lo agradecería mucho.

    Estoy trabajando en Android Studio y uso las librerias android.support.v4.app.*

    Un saludo.

    1. Hola Leticia. Cuando escribí este tutorial era mejor utilizar la biblioteca “android.support.v4.app”, pero ahora ya no (al menos que quieras compatibilidad para dispositivos muy antiguos). Ahora es mejor utilizar la biblioteca “android.app“. Es fácil de realizar la conversión, simplemente hay que quitar las palabras “support” y en vez de utilizar “ActivityFragment”, utilizar una simple “Activity”. Tienes más ejemplos de esto en nuestro libro gratuito de Android en http://jarroba.com/libro-android-100-gratis/

      1. Buenas Ramón. Gracias por contestar.

        He cambiado las librerias, y en las clases Activity_Principal y Activity_Detalles me dice que el método getSupportFragmentManager(), no esta declarado. ¿Qué método debería usar en sustitución a este?

        1. He cambiado las librerias y le método que me daba error, reuslta que para la libreria import android.app.Activity el método cambia  a getFragmentManager()  pero sigue sin mostrarse el layout fragment_detalle, cual podría ser el fallo para que no se muestre.

        2. Es lo que te decía, quitale la palabra “Support” a getSupportFragmentManager(), que quede getFragmentManager()

          1. Si eso he hecho pero sigo teninendo el mismo problema, cuando cargo la app en el emulador y selecciono un elemento de la lista la pantalla se queda en blanco, no carga el layout fragment_detalle, quería saber cual puede ser el error para que este fragment no se muestre.

          2. Podría ser que el Fragment no esté cargando la vista. Fíjate que en el onCreateView() que tengas bien configurado el adjuntado e inflado de la vista (más información en http://jarroba.com/libro-android-100-gratis/):

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

            View rootView = inflater.inflate(R.layout.

            fragment_main, container,false);

            return rootView;

            }

          3. Vale ya he conseguido arreglarlo, era un tontería, muchas gracias por todo! me has sido de gran ayuda.

            Un saludo.

  21. Hola, quisiera saber como se puede utilizar o sustituir un metodo

     protected void onNewIntent(Intent intent) {
     super.onNewIntent(intent);

    ya que al parecer este solo se puede usar en activity y no en fragments y no sé como solucionar ese problema, agradeceria mucho la ayuda.

    Saludos y muy buen dia 🙂

    1. Puedes pasar el mensaje al Fragment en cuanto se ejecute el método onNewIntent(), de manera semejante al ejemplo de onEntradaSelecionada().

  22. Hola, antes que todo muy buen articulo, tengo un pequeño problema y espero me ayuden.
    Tengo una app la cual se alimenta de rss, la hice con ayuda de otros tutoriales. Esta basada en Activities, las cuales quiero cambiar a Fragments para poder utilizar el Viewpager, pero muchos metodos me están marcando errores.

    No soy muy hábil en android, es más, es mi primera app y estoy aprendiendo a programar en android, quisiera que me ayudarán a comprender bien el asunto y poder cambiar las Activities por Fragments.

    Gracias de antemano!
    Saludos!

  23. Buenos días.

    Antes de nada agradeceros el post tan currado que habéis hecho. Además de funcionar perfectamente, sirve para aclarar todas las dudas respecto a los fragments y conocer perfectamente su funcionamiento. Ahora bien, hay una cosa que no se como hacerla y me está dando algún quebradero de cabeza, ya que no lo contemplais en este post tampoco.

    Mi problema es el siguiente:

    Yo tengo un Activity que contiene un fragment estático, que es un listView, y también contiene un par de radioButton. Estos radioButton son los que van a condicionar los datos que se carguen en el fragment. Inicialmente, está marcado un radioButton por lo que cuando se crea el fragment, se carga con unos datos determinados, pero cuando se selecciona el otro radioButton, los datos son distintos y por tanto debo actualizar el contenido del fragment pero no se como hacerlo…alguna idea que pueda guiarme??

    Muchas gracias!!!

      1. Muchísimas gracias!!! No sabía que tuvierais un libro propio tan completo, me parece genial!! Voy a buscar la solución a mi problema. Gracias de nuevo.

      2. Hola de nuevo.

        Siento molestar otra vez, pero he estado mirando el libro y no he encontrado lo que necesito exactamente…
        Sí he visto como modificar desde la Activity contenedora el texto de un botón del fragment haciendo un findById, pero no es lo que necesito, si no que quiero actualizar el listview de dentro del fragment pasandole un array de datos distinto al que se le pasa al crear dicho fragment. No se si al ser estático se podrá hacer o necesito cambiarlo por un dinámico y destruir y crear cada vez que quiera recargarlo.

        Muchas gracias.

        1. Para eso tienes dos opciones. O bien creas un Fragment dinámico que sustituya al otro con los nuevos datos, o bien cambias los datos del Adapter del listado.

          1. Ok gracias. Probaré con un fragment dinámico porque ya he intentado modificar los datos del adaptador pero aunque los modifico no me recarga la lista… Muchas gracias!

  24. Es posible ir de una activity a un fragment que se encuentra a una ActionBar en NavegationModeTabs, si es asi podrian hacer un ejemplo muchisiimas gracias :D!!

  25. Felicidades por tan escrupuloso tutorial,

    no sé que hago mal que cuando salgo de la aplicación, al volver a ejecutarla tarda mucho en aparecer la pantalla inicial.

    Feliz año nuevo 2015

    1. Hola Antonio,
      entiendo que ejecutas la aplicación, la pones en pausa y vuelves. Comprueba en tu código que cargas bien las cosas en el onResume() y que no haces ciclos innecesarios (for o while)

      1. Gracias por atenderme,

        salgo de la aplicación con la tecla backpressed o con finish(), y cuando vuelvo a ejecutar la aplicación tarda mucho en mostrar el layout.

        Saludos

        1. ¿Te pasa en el emulador o también en un dispositivo físico? Prueba a rebajar en el emulador tanto la resolución como la versión del sistema operativo y a subir la RAM. También, mira si en el log dice algo que pueda dar pistas.

          1. Hola
            En el emulador funciona bien, es en un samsung s4 donde me ocurre el retardo.
            Saludos

          2. El retardo pueden venir dado por muchos motivos. Es mejor que estés atento al logcat a ver que dice. Otra cosa que puede ser es que haya aplicaciones que realenticen el dispositivo, prueba a cerrarlas antes (fuerza el cierre desde el administrador hasta que descubras que aplicación o aplicaciones provocan el retardo; si luego el dispositivo funciona mal simplemente reinícialo para que todos los servicios vuelvan a iniciarse). También, te recomiendo que desinstales y borres todos los datos de la aplicación que tarda en arrancar por completo antes de volver a instalarla, puede que tenga algún dato guardado que provoque esto, como una base de datos inconsistente. Y ya por último quedaría revisar el código, para ver si hay algo que provoca la lentitud (como un excesivo trabajo en el hilo principal. Echa un vistazo a http://jarroba.com/multitarea-en-android/)

  26. Hola que tal muy buen tutorial, tengo un problema en el fragment_lista no se como resolverlo te agredceria mucho de antemano muchas gracias por compartir tu conocimiento

    setListAdapter(new Lista_adaptador(getActivity(), R.layout.layout_elemento_listado, Lista_contenido.ENTRADAS_LISTA){
    @Override
    public 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);
    }
    }
    });

  27. hola
    si quiero integrar un text to speech donde lo pondria ??
    el text to speech se mostraria en la segunda ventana dentro del contenedor de fragmentos.
    espero que me ayudes de antemano gracias

  28. Hola amigos..

    Me presenta el sgte error en la clase “Lista_contenido.java”

    …..
    static
    {
    aniadirEntrada(new Lista_entrada(“0”, R.drawable.im_buho, “BUHO”, “Búho es el nombre común…”));
    }

    Me lo marca en rojo y me dice que … “im_buho cannot be resolved or is not a field”

    Los archivos de imágenes estan en la carpeta “res”…

  29. Este código saca la informcaión que estoy buscando y la muestra en un toast

    public void BuscaVista2(View v ){

    carnet= (EditText) findViewById(R.id.carnetv2); //se inicializa aquí por que al igual que el anterior no funciona afuera
    String carnetv2=carnet.getText().toString(); //carga el valor digitado
    //String recupera=””; //se crear el string para recibir cada una de las posiciones del objeto de base de datos que se retorna al realizar la busqueda
    datas=new Datos(this);//objeto de base de datos
    datas.open();//abre la base de datos de lectura y escritura
    try{

    lista = datas.buscardata(carnetv2);//busca por carnet, envia carnet por parametro

    for(DataIUD obj:lista){//se declara un tipo objeto para cargar el resultado en un string
    //recupera el objeto devuelto por el metodo buscardata en un string llamado recupera
    recupera+=obj.getId()+” “+obj.getCarnet()+” “+obj.getNombre()+” “+obj.getApe1()+” “+obj.getApe2()+” “+obj.getEdad()+” “+obj.getDireccion()+” “+obj.getSugerencia()+”n”;
    Toast.makeText(getApplicationContext(),recupera,6000).show(); //muestra el contenido del string
    carnet.setText(“”);// limpia el campo de busqueda

    }

    Pero la necesito en un list view

    Gracias

    Nana

  30. Necesito cargar la información de una base de datos en un list view.
    Y ya lo he hecho de varias maneras pero nad me resulta .

    Agradecería su ayuda

    Nana

  31. Oye me manda el siguiente error

    10-31 09:58:18.650 12805-12805/maba.fragmentos E/AndroidRuntime﹕ FATAL EXCEPTION: main
    java.lang.OutOfMemoryError
    at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:596)
    at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:444)
    at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:832)
    at android.content.res.Resources.loadDrawable(Resources.java:2988)
    at android.content.res.Resources.getDrawable(Resources.java:1558)
    at android.widget.ImageView.resolveUri(ImageView.java:646)
    at android.widget.ImageView.setImageResource(ImageView.java:375)
    at maba.fragmentos.Fragment_Lista$2.onEntrada(Fragment_Lista.java:42)
    at maba.fragmentos.Lista_adaptador.getView(Lista_adaptador.java:36)
    at android.widget.AbsListView.obtainView(AbsListView.java:2627)
    at android.widget.ListView.makeAndAddView(ListView.java:1852)
    at android.widget.ListView.fillDown(ListView.java:682)
    at android.widget.ListView.fillFromTop(ListView.java:748)
    at android.widget.ListView.layoutChildren(ListView.java:1667)
    at android.widget.AbsListView.onLayout(AbsListView.java:2447)
    at android.view.View.layout(View.java:15204)
    at android.view.ViewGroup.layout(ViewGroup.java:4793)
    at android.widget.FrameLayout.onLayout(FrameLayout.java:448)
    at android.view.View.layout(View.java:15204)
    at android.view.ViewGroup.layout(ViewGroup.java:4793)
    at android.widget.FrameLayout.onLayout(FrameLayout.java:448)
    at android.view.View.layout(View.java:15204)
    at android.view.ViewGroup.layout(ViewGroup.java:4793)
    at android.widget.FrameLayout.onLayout(FrameLayout.java:448)
    at android.view.View.layout(View.java:15204)
    at android.view.ViewGroup.layout(ViewGroup.java:4793)
    at android.widget.FrameLayout.onLayout(FrameLayout.java:448)
    at android.view.View.layout(View.java:15204)
    at android.view.ViewGroup.layout(ViewGroup.java:4793)
    at com.android.internal.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:349)
    at android.view.View.layout(View.java:15204)
    at android.view.ViewGroup.layout(ViewGroup.java:4793)
    at android.widget.FrameLayout.onLayout(FrameLayout.java:448)
    at android.view.View.layout(View.java:15204)
    at android.view.ViewGroup.layout(ViewGroup.java:4793)
    at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2260)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2007)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1249)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6364)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:791)
    at android.view.Choreographer.doCallbacks(Choreographer.java:591)
    at android.view.Choreographer.doFrame(Choreographer.java:561)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:777)
    at android.os.Handler.handleCallback(Handler.java:730)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:176)
    at android.app.ActivityThread.main(ActivityThread.java:5419)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:525)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862)
    at dalvik.system.NativeStart.main(Native Method)

  32. Hola,
    Primero que todo quiero agradecerte por tan buen tutorial.

    ahora mi problema: En cada Activity_Detalle tengo un botón, que tiene su setOnClickListener para que realice una acción, pero cada vez que selecciono un item de la lista la app se cierra.
    agradezco cualquier ayuda.

    saludos

      1. Es un boton compartir
        ImageButton share = (ImageButton) findViewById(R.id.compartir);
        share.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
        // TODO Auto-generated method stub
        Intent sharingIntent = new Intent(Intent.ACTION_SEND);
        Uri screenshotUri = Uri.parse(“android.resource://”+”com.android.deplan”+”/”+R.drawable.e1);

        sharingIntent.setType(“image/png”);
        sharingIntent.putExtra(Intent.EXTRA_STREAM, screenshotUri);
        sharingIntent.putExtra(Intent.EXTRA_TEXT, “Todos los eventos de boyaca los tienes con DePlan”);

        startActivity(Intent.createChooser(sharingIntent, “Share using”));
        }
        });

        pero incluso poniendo solamente el setOnClickListener vació la aplicación se cierra al dar clic sobre el elemento de la lista

        ImageButton share = (ImageButton) findViewById(R.id.compartir);
        share.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

        }
        });

        Gracias.

        1. Hola

          Ya lo solucione poniendo los eventos de los botones en el onCreateView de la clase Fragment_detalle, sin embargo antes estaba compartiendo la imagen de forma estática y ahora necesito compartirla de forma dinámica, es decir de acuerdo al fragment que esté en ese momento.

          Agradezco cualquier ayuda

  33. Sólo quiero darles las gracias.. tenía un caos en la cabeza que sólo ustedes pudieron resolver, de una manera muy amigable y simple.. se fueron a mis favoritos 😀

    Saludos y exito en todo!

  34. Hola, muy bueno el tutorial me sirvió a las mil maravillas, pero tengo una pega si quieres poner un en la activity detalle botón para que al pulsarlo te aparezca la descripción y si no lo pulsas solo ves la foto como se hace por que el botón me esta dando pegas no se bien donde ponerlo la verdad :S Gracias por su atención

    1. Hola Daniel,

      poder puedes poner el botón tanto en el Layout de la Activity como del Fragment. Luego te queda enviar la orden en un listener para mostrar u ocultar la foto o la descripción (cada una de estas podría estar en un Fragment aparte y valdría con ocultar uno y mostrar otro, o directamente tratarlos en el mismo layout ocultando y mostrando Views).

  35. muchas gracias por la ayuda, solo una pregunta a este proyecto le quiero agregar la barra de acciones y es ahi donde me da problema… si me pudieras ayudar te lo agradeceria mucho

  36. I have resolved the error now it’s working fine.

    and Thank you for such a nice sharing i was looking for this from a long time.

  37. Lista_contenido.java displaying this error
    Multiple markers at this line
    – The type java.lang.String cannot be resolved. It is indirectly referenced from
    required .class files
    – The type java.lang.Object cannot be resolved. It is indirectly referenced from
    required .class files
    and it’s also displaying this below error message in console of my eclipse adt

    [2014-09-15 22:11:50 – Fragmentos] Unable to resolve target ‘android-18’

    please help me how can i resolve this error

  38. Buenas Ramón,
    ¿Cuál es la forma más fácil de hacer que al cambiar la orientación de la tablet en modo vertical los fragments pasen a estar uno encima de otro en lugar de al lado?

    Gracias.
    Saludos, y enhorabuena por el post.

    1. Hola Nacho,
      tan solo tienes que cambiar el diseño (Layout) de la orientación que necesitas. Lo más rápido es que vayas al “layout_activity_principal_dospaneles.xml” y el LinearLayout lo pongas en android:orientation=”vertical”.

      1. Lo que necesito es que la orientación de los fragments cambie al girar la pantalla del dispositivo, y no al definirlos en el layout porque si lo hago así, después no puedo ver los fragments en horizontal.
        Además, al intentar cambiar la orientación en el layout me dice error en: ‘android:layout_width=”0dp”‘, por lo que, entiendo que ahora debe valer cero la altura y no la anchura, pero esto no provoca que la orientación se cambie al girar el dispositivo.

        ¿Qué opinas? Gracias.

          1. Pero, tanto si en el layout defino la orientación como horizontal o vertical, ¿al girar el dispositivo deben cambiarse las posiciones de los fragments uno encima del otro o uno al lado del otro?
            Si defino la orientación como horizontal me aparecen los fragments uno al lado del otro, pero al girar la pantalla a modo vertical me siguen apareciendo de este modo, y como es lógico se ve peor porque tiene menor anchura. (Lo ideal sería que en modo vertical aparecieran uno debajo del otro).
            Si que se me están girando, pero no se me realiza el cambio de pasar a estar un fragment al lado del otro a estar encima.

            Gracias por el interés.

          2. Entonces lo que necesitas es crear otro Layout que dibuje los Fragments de una manera diferente (un Layout con los Fragments uno encima de otro y otro con los Fragments uno al lado del otro).

          3. Ramón, ya tengo 2 layouts (uno para cada posición, horizontal y vertical) donde lo único que se diferencian es en lo siguiente:
            LINEAR LAYOUT
            android:orientation=”horizontal” …o… “vertical”
            FRAGMENT
            android:layout_width=”0dp” …o… android:layout_height=”0dp”

            y sigue sin cambiar la posición de los fragment, siempre me salen uno al lado del otro.
            ¿Qué hay que tener en cuenta en el Manifest?
            Ahora mismo en la activity donde se muestran los fragment incluyo:
            android:configChanges=”orientation|keyboardHidden|screenSize|locale|layoutDirection”

            Espero puedas ayudarme. Gracias de nuevo.

          4. Entiendo que has puesto un layout en la carpeta “layout” y el otro en la carpeta “layout-land” (o con el sufijo deseado) con el mismo nombre de fichero layout, que es lo correcto para que Android cambie la vista por la que debe. Luego la anchura de los layout en los Fragment es un poco indiferente para el giro, salvo por temas de diseño. Por defecto gira solo, con lo que no hace falta que lo definas en el android:configChanges del AndroidManifest.xml.

            Otra cosa que se me ocurre es que si estás probándolo en el emulador puede que no te gire por un bug. Tienes que cambiar la versión del emulador, prueba con la 4.0.

          5. Así es, tengo un layout denominado ‘buscador.xml’ con la configuración de fragments en vertical en la carpeta ‘layout’, y otro layout con el mismo nombre ‘buscador.xml’ en la carpeta ‘layout-land’ con la configuración de fragments en horizontal.
            Estoy probando con un emulador smartphone de 5.4″ en vertical que coge el layout vertical (fragments uno debajo del otro) pero pone el layout con fragments, no el layout sin fragments como debería ser.
            También pruebo con un emulador tablet de 10.1″ y si coge la configuración horizontal (fragments uno al lado del otro).
            Además, en ambos casos, al girar el dispositivo no cambia de layout, es decir, el vertical al ponerlo horizontal sigue con los fragments en la configuración del vertical, e igual sucede con el horizontal.
            ¿Cómo puedo solucionar este desbarajuste? XD

          6. Ambos emuladores son Android versión 4.2.2 ¿es esto a lo que te refieres?
            En ese caso, ¿creo nuevos emuladores con una versión de Android 4.0?

          7. He creado dos emuladores con las mismas características pero con versión Android 4.0 y todo sigue igual.
            A ver, tengo una clase donde quiero mostrar 2 fragments, A y B (1 fragment solo si es smartphone, A), siguiendo tu ejemplo, esto es lo que he hecho:
            1) La clase carga un layout que contiene únicamente un FrameLayout (A).
            2) En el onCreate de la clase:
            if ((getFragmentManager().findFragmentById(R.id.fragment (B)) != null))
            Entonces cargo el fragment (A) en el FrameLayout, puesto que es tablet.
            3) En la carpeta values-large tengo un archivo ‘layout.xml’ que carga el xml versión tablet cuando así lo considera oportuno:

            @layout/layout_tablet(2fragments)

            Pensaba que con esto, al detectar smartphone me cogería el layout del punto 1) (solo FrameLayout con una activity), y al detectar tablet con la configuración del punto 3) me cargaría el layout con 2 fragments y cargaría el nuevo layout con lo establecido en el punto 2) …. veo que no es así, ¿sabes en que me puedo estar equivocando?

            Gracias por dedicar parte de tu tiempo a ayudarme, un saludo.

          8. Es correcto lo que estás haciendo. Hay que probar si es problema del código o del emulador, por lo que:
            1) Prueba a hacerlo con sustitución por alias como en el artículo, puedes hacerlo por sustitución de nombres también, pero visto que no te funciona ya es probar.
            2) Para el tema de probar el giro es recomendable probarlo sobre un dispositivo físico.

            Y de nada hombre, siempre que podamos cuenta con que te echaremos una mano 🙂

          9. Una cosa que me puede estar pasando es que, el archivo xml que tu has llamado ‘layout_activity_principal_listado.xml’ que es el que carga por defecto la clase principal y el que se menciona en el archivo ‘layout.xml’ (carpeta values-large) por si detecta que es tablet coja la versión con 2 fragments, tu estableces que es un fragment y con el atributo android:name le asignas una clase, pero el que yo uso en su lugar es un FrameLayout y por tanto, no tiene asignada clase hasta que no se le carga un fragment dinámicamente en la clase … por eso creo que al iniciarse la clase cuando lo llama como no tiene clase asignada carga directamente el layout con 2 fragments aún con smartphone.
            No sé si me he explicado, tu tienes 2 fragments, el de la izquierda un fragment y el de la derecha un FrameLayout y cargas por defecto el fragment .. yo tengo 2 fragments, el de la izquierda un FrameLayout (que es el que se carga por defecto al iniciar la clase) y el de la derecha un fragment.
            Siendo así, ¿es necesario asignar una clase al FrameLayout antes de que la activity principal intente cargarlo con ‘setContentView()? ¿Cómo hago esto?

            Gracias.

          10. Ya he solucionado el tema de que cuando se gire el dispositivo cambien los fragments de estar encima a estar al lado, era esta condición que te comenté incluía en el Manifest:
            android:configChanges=”orientation”
            Al quitarla funciona de maravilla !!!

            Ahora solo me queda por solucionar el que me cargue el layout correspondiente en caso de usar smartphone o tablet, que siempre me coge el layout con 2 fragments para tablet indistintamente del dispositivo.
            Me dices que emplee alias como en el artículo, y es eso lo que hago. Tengo 3 carpetas values/values-large/values-sw600dp con un archivo layout.xml (igual en todas ellas) con el siguiente contenido:

            resources
            item name=”fragment_smartphone” type=”layout”>@layout/fragment_tablet
            /resources

            Pero no me coge el layout de smartphone en ningún caso (quizás por lo que te comento en el mensaje anterior). ¿Cómo es sustitución por nombre en vez de alias?

            Thanks again. XD

          11. Me alegro que se haya solucionado, no hay nada que sienta mejor que los programas funcionen 😀

            Lo que te puede estar pasando en el Smartphone puede ser por las densidades de pantalla (que hayas elegido una densidad demasiado alta y que se esté dibujando el formato Tablet).

            Sustitución me refiero a que si pones en la carpeta “layout-land” por ejemplo, un fichero layout con un nombre y un contenido; y en la carpeta “layout” el fichero con el mismo nombre pero distinto contenido. Android utilizará el que corresponda en cada caso utilizando uno u otro (en este caso si la pantalla está en posición vertical o en posición horizontal el layout elegido, aunque con el mismo nombre cambiará de contenido).

          12. Sí puede tener que ver con las densidades, porque estaba empleando un emulador de 5.4″ para las pruebas de smartphone (cuando en realidad eso es un phablet) y por eso me cogía el layout de tablet.
            Ahora he empleado un emulador de 4.0″ (smartphone) y me da error al lanzar la clase, se me cierra la aplicación, y creo que tiene que ver con lo que te he comentado en otro mensaje de poner en el ‘setContentView()’ un FrameLayout en lugar de un Fragment, entonces al ejecutar la clase el FrameLayout no tiene ninguna clase asignada, a pesar de que he probado a asignarle una clase antes y después del ‘setContentView()’, y tampoco:
            Menu_Filter fragment = new Menu_Filter();
            getFragmentManager().beginTransaction().replace(R.id.framelayout, fragment).commit();
            ¿Alguna solución?

            PD. Siento hacerte el blog tan extenso con tanto comentario.

          13. El FrameLayout es para cargar los Fragments de manera dinámica. Llamar al setContentView() has de pasarle la vista de la Activity (donde va a ir el Fragment), y luego cargar el Fragment (No pongas nada por delante del setContentView() te puede dar problemas por esto). También comprueba que esté declarada la Activity en el AndroidManifest.xml.

            No te preocupes por los comentarios, para eso tenemos la web, para resolver dudas y ayudar 🙂

          14. Sí si, ya sé que el FrameLayout es para cargar fragments de forma dinámica y que al método ‘setContentView()’ hay que pasarle la vista de una activity.
            Me refería a que en el ejemplo tu pasas la vista de una activity en la que se incluye un fragment (lista de animales, tu vista principal), mientras que yo paso la vista de una activity en la que se incluye un FrameLayout (mi vista principal, que es la que va cambiando de clase). En la declaración del fragment se incluye un atributo ‘android:name’ donde se declara la clase que se va a cargar en él, mientras que en el FrameLayout no se incluye este parámetro porque se carga después en la clase de forma dinámica, entonces no sé si por este motivo al ejecutar el método ‘setContentView()’ se produce el error, porque el FrameLayout no incluye ninguna vista por defecto.
            La activity claro que está declarada en el Manifest, en versión tablet no hay problema de ejecución.

          15. Ok, entonces lo que te debe de estar ocurriendo será debido al ciclo de vida de Fragments. Has de asegurarte que se cargue el FrameLayout antes de solicitar a Android que instancie un Fragment con getFragmentManager().beginTransaction().add(). Fíjate en donde estás llamando a este método. Si puedes cópiame el error.

          16. Sí, siempre cargo el FrameLayout después de cargar la vista del layout de la clase:
            protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.fragment_seeker_filter);
            //Detección de fragment (en caso de tablet)
            if ((getSupportFragmentManager().findFragmentById(R.id.results_fragment) != null)) { //Fragments
            mTwoPane = true;
            Menu_Seeker_Filter fragment = new Menu_Seeker_Filter();
            getSupportFragmentManager().beginTransaction().replace(R.id.seeker_fragment,fragment).commit(); //seeker_fragment es el FrameLayout del layout para tablet (fragment y FrameLayout)
            }else { //No Fragments
            Menu_Seeker_Filter fragment2 = new Menu_Seeker_Filter();
            getSupportFragmentManager().beginTransaction().replace(R.id.framelayout_filter,fragment2).commit(); //framelayout_fitler es el FrameLayout del layout para smartphone (solo FrameLayout)
            }

            Este es el error que me da:
            FATAL EXCEPTION: main
            java.lang.RuntimeException: Unable to start activity ComponentInfo{proyecto/proyecto.Menu_Seeker}: java.lang.NullPointerException

            Caused by: java.lang.NullPointerException at proyecto.Menu_Seeker.onCreate(Menu_Seeker.java:173)
            at android.app.Activity.performCreate(Activity.java:5104)
            at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2144)

          17. Es un NullPointerException, posiblemente no está encontrando R.id.framelayout_filter. Prueba con:
            if (findViewById(R.id.framelayout_filter) != null)

            Aunque en el artículo recomendaba usar la librería de soporte (la sigo recomendando para Android 2.3, aunque ya queda un poco antiguo y sin cuota de mercado), ya no con las nuevas versiones de Android. Te recomiendo que uses mejor la nueva versión y quites la palabra Support de todo método para que quede por ejemplo:
            getFragmentManager()
            y que los imports los cambies todos de “android.support.v4.app…” a “android.app…”.

  39. Hola, antes que nada gracias por los tutoriales.. muy bien explicados y didacticos.
    Estoy comenzando con la programacion en android, asi que m vienen muy bien todos estos tutoriales.
    Te comento que descargue el proyecto Fragment.zip.. lo descomprimi y lo importe desde Eclipse.. cuando quise lanzar la aplicacion y verla en mi dispositivo.. me sale un mensaje que la aplicacion se detuvo.
    Que puede estar pasando ? gracias

  40. esta genial tu código solo que tengo una duda, estoy tomando la certificación en Android jr. pero mi proyecto final dice que tengo que abrir los documento en pdf me podrán ayudar con eso es que no se como hacerlo. gracias

    1. Hola Lupita,
      para abrir documentos (por ejemplos PDF) has de recurrir a programas externos fuera del SDK de Android. Supongo que accederás al PDF de manera online, con lo que te valdrá con abrirlo en el navegador, tan fácil como utilizarel siguiente código:
      String urlPdf = "www.laRutaAlPDF.com/miPdf.pdf"
      Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(urlPdf));
      startActivity(intent);

  41. Buenas me gusto mucho este tutorial me ha ayudado mucho, pero tengo un problema me puse a combinar los Fragments con Activitys normales, ¿Que sucede? bueno a un boton le di la funcion de verificar(View view) que cuando lo pulsa verifica el usuario y lo lleva al Menú principal (El menú es un Swipleabe Tabs con fragments) y no se cuelga y se cierra.

    estoy usando el llamado de la activity con fragments de la siguiente forma:

    Intent i = new Intent(this, Pprincipal.class);
    startActivity(i);

    del login un Activity Cualquiera a un Pprincipal que es una Activity de Fragments

    Gracias espero que me puedan ayudar

  42. El artículo es excelente, ahora bien ya he implementando un proyecto con fragmentos tal como lo explicas aquí.

    Sin embargo necesito cargar dinámicamente el contenido de ambos listados desde un servicio REST, donde hago la llamada al servicio y como hago para que se actualice por que estoy usando un AsyncTask y mando ejecutar la clase AsyncTask en el onCreateView pero al ser asíncrona la llamada no refresca la vista.

    saludos y gracias.

    1. Hola Mav,

      al actualizar el Adapter del ListView no se refresca la vista de manera automática. Para que se refresque las vistas del ListView, tienes que llamar desde el adaptador al método notifyDataSetChanged() (este método refresca las vistas, lo tienes que llamar desde el hilo principal).

      1. Muchas gracias Ramón, ya he logrado cargar los datos en el ListView, ahora tengo otro problema, cuando uso el scroll o se gira la pantalla se vuelve a cargar, generando una nueva llamada al servicio. Como evito esto??

        saludos y gracias

        1. No entiendo muy bien lo que quieres decir con llamada al servicio. Supongo que quieres decir que vuelve a llamar al OnCreate() de la Activity contenedora. Cuando se gira la pantalla se destruye la anterior vista y se genera una nueva, por lo que o bien guardas el estado y lo cargas al girar, o desde el AndroidManifest.xml impides el giro de pantalla.

  43. Hola, y muchas gracias por tomarte la paciencia de redactar este articulo, justo tengo un cliente que quiere trabajos que sin fragment, hubiera echos muchas maniobras en el code, y ya lo tengo claro, Saludos desde Peru!

  44. Hola otra vez…

    después de mucho pelear, creo haber resuelto el problema. Veréis, pensado pensando llegué a la conclusión que el problema del fragment googlemaps es que al volver a él daba error porque ya estaba creado, por lo que se intentaba duplicar. Lo obvio es eliminarlo para que no dé esa duplicidad, pero la duda es dónde hacerlo.
    Probé en el onDestroy de la actividad, pero no servía porque si no salta ese evento tenía el error. Así que decidí interceptar el click en el botón atrás. Es ahí donde compruebo si el mapa está cargado… si lo está, lo remove y dejo que el evento onKeyDown siga su curso.

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
    // TODO Auto-generated method stub

    if (keyCode == KeyEvent.KEYCODE_BACK) {
    if (!esUnTablet) {

    // Si no es un tablet, tengo que eliminar el mapa
    // para que no me dé error de duplicidad cuando vuelva a él
    // miro si estoy viendo el mapa
    Fragment f = getSupportFragmentManager().findFragmentById(
    R.id.googleMapa);
    if (f != null) {
    Toast.makeText(this, “Existe el mapa”, Toast.LENGTH_SHORT)
    .show();
    // ==> elimino el mapa para que no se duplique
    FragmentTransaction tr = getSupportFragmentManager()
    .beginTransaction();
    tr.remove(f).commit();
    }
    }
    }

    return super.onKeyDown(keyCode, event);
    }

    Si la aplicación se ejecuta en un tablet hacer remove no tiene sentido ya que no va a haber movimientos entre fragments.

    Con esta solución funciona, por lo menos en las pruebas preliminares, perfectametne. Ahora toca implementar la lógica de la interacción con el mapa… a ver qué pasa.

    Ya os iré contando.

    Saludos a tod@s

    1. Mi duda ahora es… ya que destruyo el mapa, todos los markets que tenía sobre él se pierden, de forma que cuando vuelva a cargar el mapa de alguna forma tengo que recuperar esas marcas, y pueden ser muchas..

      ¿Alguna sugerencia de cómo hacerlo?

      Gracias nuevamente…

      Saludos

      1. Tienes varias opciones para guardar los markets, como son objetos que se guardan en un objeto de tipo GoogleMap (o puedes guardar directamente el tipo Marker, esto como te resulte mejor), te recomendaría una de estas dos: crear un objeto Singleton para que persistan los datos, o crear una clase Pojo que extienda de Serializable con los objetos anteriores para poder mantenerlos en memoria. Ya no sé si se te complicaría mucho la base de datos, pero es otra alternativa y además persistirían más allá del cierre completo de la aplicación.

  45. Estoy empezando, por lo que espero seais comprensivos/copasivos conmigo.
    Veréis, estoy programando una aplicación android de manera que la transición entre los distintos layouts la hago mediante fragments para poder controlar correctamente la ejecución tanto en smartphines como en tablets, de forma que en el tablet salen todos los fragments y en el smartphone de uno en uno dependiendo de la opcións seleccionada.
    Mi problema está en los smartphone, ya que al girar el movil se re-crea el fragment y me lo duplica por lo que al interactuar con él me da error. Buscando encontré una solución, que es destruir el fragment (remove), pero me da nuevamente error diciendo que no se puede hacer “after saveinstance” o algo así, colgándose todo al interactuar con él dando nullpointererror. En concreto es un mapa google que responde a los clicks sobre él. La primera vez funciona correctametne, pero al girar la pantalla ya da el nulpointererror al clickar… creo que el comando remove no está funcionando. Los fragmets los añado al backstage, de forma que si despué sde girar le doy apra atras todo funcina como debe… por lo que está claro que el remove no va…

    Alguna idea?? Solución?? Explicación que pueda seguir para conseguir transiciones entre fragments correctas incluso girando l apantalla??

    Un saludo y muchas gracias por llegar a leer esto.

    1. Hola Nitram,

      Has de tener en cuenta que al girar la pantalla la Activity contenedora queda destruida (Para que no haya problemas de punteros a Fragments que existen se suele forzar el remplazo con replace() o eliminándolos con remove() ) y se vuelve a crear desde cero otra con los Fragments. Por lo que debes guardar el estado con onSaveInstanceState() y se recuperará en los datos que se pasan en el onCreate(). Aquí existe un error conocido que puedes solventar con esta solución (que es el que te está dando): http://stackoverflow.com/questions/7469082/getting-exception-illegalstateexception-can-not-perform-this-action-after-onsa. Por lo demás, estás utilizando correctamente los Fragments tanto para Smartphones como para Tablets

      Aquí somos poco comprensivos/compasivos… , jeje es broma 😉 Pregunta lo que necesites y siempre que podamos te echaremos una mano.

      1. Muchas gracias por la respuesta…

        he leído la info del enlace que me das y no consigo entender nada. He hecho varias pruebas según todo lo que indican ahí, pero siempre es lo mismo… giro la pantalla y no hay problema, le doy para atrás para volver al layout desde el que lanzo el fragment de googlemaps y cuando clicko otra vez para ver el mapa da el error:
        06-28 18:12:42.739: E/AndroidRuntime(15281): FATAL EXCEPTION: main
        06-28 18:12:42.739: E/AndroidRuntime(15281): java.lang.IllegalStateException: Can’t change container ID of fragment SupportMapFragment{4181aad8 #2 id=0x7f07005f}: was 2131165279 now 2131165262
        06-28 18:12:42.739: E/AndroidRuntime(15281): at android.support.v4.app.BackStackRecord.doAddOp(BackStackRecord.java:407)
        06-28 18:12:42.739: E/AndroidRuntime(15281): at android.support.v4.app.BackStackRecord.replace(BackStackRecord.java:429)
        06-28 18:12:42.739: E/AndroidRuntime(15281): at android.support.v4.app.BackStackRecord.replace(BackStackRecord.java:421)
        06-28 18:12:42.739: E/AndroidRuntime(15281): at mvm.nitram.lugarespersonales.Principal.Click_Boton(Principal.java:88)
        06-28 18:12:42.739: E/AndroidRuntime(15281): at mvm.nitram.lugarespersonales.Botones.onClick(Botones.java:59)
        06-28 18:12:42.739: E/AndroidRuntime(15281): at android.view.View.performClick(View.java:4091)
        06-28 18:12:42.739: E/AndroidRuntime(15281): at android.view.View$PerformClick.run(View.java:17072)
        06-28 18:12:42.739: E/AndroidRuntime(15281): at android.os.Handler.handleCallback(Handler.java:615)
        06-28 18:12:42.739: E/AndroidRuntime(15281): at android.os.Handler.dispatchMessage(Handler.java:92)
        06-28 18:12:42.739: E/AndroidRuntime(15281): at android.os.Looper.loop(Looper.java:153)
        06-28 18:12:42.739: E/AndroidRuntime(15281): at android.app.ActivityThread.main(ActivityThread.java:5086)
        06-28 18:12:42.739: E/AndroidRuntime(15281): at java.lang.reflect.Method.invokeNative(Native Method)
        06-28 18:12:42.739: E/AndroidRuntime(15281): at java.lang.reflect.Method.invoke(Method.java:511)
        06-28 18:12:42.739: E/AndroidRuntime(15281): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:821)
        06-28 18:12:42.739: E/AndroidRuntime(15281): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:584)
        06-28 18:12:42.739: E/AndroidRuntime(15281): at dalvik.system.NativeStart.main(Native Method)
        06-28 18:12:44.388: I/Process(15281): Sending signal. PID: 15281 SIG: 9

        Estoy colgadísimo ya que si no consigo pasar de aquí poco puedo hacer ya que toda la UI depende de las transiciones entre fragments y la interacción con ellos.

        Un saludo y nuevamente gracias por la ayuda.

        1. Mapalugares.java:
          ===============
          package mvm.nitram.lugarespersonales;

          import android.os.Bundle;
          import android.support.v4.app.Fragment;
          import android.view.LayoutInflater;
          import android.view.View;
          import android.view.ViewGroup;

          import com.google.android.gms.maps.GoogleMap;

          public class Mapalugares extends Fragment {

          private GoogleMap mapa;

          public Mapalugares() {
          // constructor
          }

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

          return inflater.inflate(R.layout.frg_mapalugares, container, false);

          }

          }

          Principal.java:
          ===========
          package mvm.nitram.lugarespersonales;

          import android.os.Bundle;
          import android.support.v4.app.Fragment;
          import android.support.v4.app.FragmentTransaction;
          import android.support.v7.app.ActionBarActivity;
          import android.view.View;
          import android.widget.Toast;

          public class Principal extends ActionBarActivity implements
          Botones.PulseUnBoton {

          private boolean esUnTablet = false; // inicializo a que es un smartphone

          @Override
          protected void onCreate(Bundle savedInstanceState) {
          // TODO Auto-generated method stub
          super.onCreate(savedInstanceState);

          setContentView(R.layout.form_ppal);

          // Si se ve el fragment del mapa de google es que estamos en un tablet
          if (findViewById(R.id.esTablet) != null) {
          esUnTablet = true;
          // Mapalugares mapa = (Mapalugares) getSupportFragmentManager()
          // .findFragmentById(R.id.googleMapa);
          // mapa.setMapaListener(this);

          } else {
          esUnTablet = false;
          // Cargo el fragment BOTONES
          // Check that the activity is using the layout version with
          // the fragment_container FrameLayout
          if (findViewById(R.id.fragment_container) != null) {

          Toast.makeText(this, “Creo PANTALLA”, Toast.LENGTH_SHORT)
          .show();

          // However, if we’re being restored from a previous state,
          // then we don’t need to do anything and should return or else
          // we could end up with overlapping fragments.
          if (savedInstanceState != null) {

          Toast.makeText(this, “Creo PANTALLA RETURN”,
          Toast.LENGTH_SHORT).show();
          return;
          }

          // Create a new Fragment to be placed in the activity layout
          Botones firstFragment = new Botones();

          // Add the fragment to the ‘fragment_container’ FrameLayout
          getSupportFragmentManager().beginTransaction()
          .add(R.id.fragment_container, firstFragment).commit();

          }
          }

          }

          @Override
          public void Click_Boton(View btn) {
          Fragment nuevoFRG = null;

          switch (btn.getId()) {
          case R.id.btn_lista:

          nuevoFRG = new Listalugares();

          break;
          case R.id.btn_mapa:

          Fragment mapalugares = getSupportFragmentManager()
          .findFragmentById(R.id.googleMapa);
          if (mapalugares == null) {
          nuevoFRG = new Mapalugares();
          } else {
          nuevoFRG = mapalugares;
          }
          default:
          break;
          }

          // Toast.makeText(this, mensaje, Toast.LENGTH_SHORT).show();

          FragmentTransaction transicion = getSupportFragmentManager()
          .beginTransaction();
          transicion.replace(R.id.fragment_container, nuevoFRG);
          transicion.addToBackStack(null);

          transicion.commit();

          }
          }

          form_ppal.xml:
          =============

          frg_botones.xml:

          frg_mapalugares.xml:
          ===================

          fgr_listalugares.xml:
          ===============

          1. He visto es que estás usando las bibliotecas antiguas, no las recomiendo ya que dan algunos problemas y podría ser lo que te está ocasionando todo (y más si estás utilizando las nuevas versiones de mapas). Utiliza mejor que ActionBarActivity directamente Fragment con la biblioteca nueva; y consecuentemente en todos los métodos que ponga “Support” elimina esta palabra para poder utilizar la librería nueva. Con la nueva librería no debería de darte ningún problema con la rotación.

  46. Hola Ramón, excelente tutorial.
    Tengo una duda, quería hacer una pequeña aplicación mediante un “Navigation Drawer” con Fragments.
    Mediante el navigation selecciono diferentes sub-aplicaciones las cuales no tienen mucha relación (aunque si comparten alguna variable) y sus layouts serían diferentes.
    Si comienzo desde el Android Studio para que genere la app con el navigation y fragment, parece ser que genera un solo fragment común llamado “fragment_main.xml”.
    Entiendo que ese fragment es común para todas las entradas del navigation y dinamicamente se crearía dentro de el otro fragment mediante el “PlaceholderFragment.newInstance” del método “onNavigationDrawerItemSelected” por lo cual no me sirve y debería eliminar el PlaceholderFragment y crear yo los fragments manualmente y poder seleccionarlos con un switch.
    Espero haberme explicado, estoy hecho un lío. Muchas gracias.

    1. Buenas Pablo,
      el Fragment “fragment_main.xml” no es un Fragment “común”, es un Fragment “Hello Word!”. Es decir, que sirve para empezar a programar y hacer lo que quieras con él.
      Para el Navigation Drawer lo único que tienes que hacer es modificar de este artículo los dos “layout_activity_principal_xxxxx.xml”, y cambiarlos a tu gusto por el xml que figura en el tutorial oficial de Android de este link (el que tiene de tag padre ): https://developer.android.com/training/implementing-navigation/nav-drawer.html. Ya el resto es fácil 🙂

        1. Ya he conseguido que aparezca el fragment correspondiente en función del elemento seleccionado del Navigator, gracias por tu aclaración.

          La duda que quizás no he expuesto bien antes es, si ahora para cada sub-aplicación seleccionada (e independiente de las demás) me basta con el fragment correspondiente o tengo que crear una activity.
          Esto es lo que me confunde, cuando se usa Fragment o Activity.

          Muchas gracias.

          1. Lo sé, lo sé, al principio es un poco lioso de entender la diferencia entre Activity y Fragment. Pero ya verás como todo queda muy claro 🙂

            En definición rápida, una Activity ocupa toda la pantalla y un Fragment una porción de una Activity.

            Por esto (puedes ir siguiendo el código de las vistas) y como ejemplo: cuando se utiliza Navigation Drawer, este se sitúa sobre una Activity, y junto a este menú se ubica un hueco para el Fragment que será lo que tenga que cambiar (sobre el FrameLayout). Es decir, el cajón del menú estará en la misma pantalla que lo que éste cambie, por lo que todo en la misma Activity.

            Espero que quede un poco más claro.

  47. Hola Ramón, muy buena vuestra publicación!! Todo muy claro 😀

    Os cuento mi problema a ver si alguien me puede echar una mano ya que ando algo perdida en el uso de fragmentos. Estoy creando una app la cual tiene una Activity con un menú del tipo DrawerLayout y quiero que cuando se pulse una opción de este menú aparezca un listado de este tipo, el problema es que al hacer esto se debe crear un fragment pudiendo hacer la transición mediante:

    FragmentManager fragmentManager = getFragmentManager();
    getFragmentManager().beginTransaction().replace(R.id.activityPrincipal,fragment).commit();

    Pero aquí la clase principal es un FragmentActivity y no puedo hacer la transición entre el Activity que contiene el menú y esta clase ya que el método getSupportFragmentManager().beginTransaction() (utilizando la librería “android.support.v4.app”) se utiliza en FragmentActivity y no en Activity.

    ¿Alguien sabría como puedo solucionar este problema?

    Muchas gracias de antemano!

    Un saludo, María.

    1. Buenas María,

      No me queda muy claro que necesitas, puede que hayas mezclado conceptos. FragmentActivity es lo mismo que una Activity para las versiones antiguas de Android, por lo que se comporta igual que Activity pudiendo utilizar startActivity() para la transición de Activities. FragmentManager es solo para Fragments. Si ves que no te quedan claros los conceptos, te recomiendo que antes lo programes a un nivel del API de 11 con la biblioteca de android.app y que una vez te funcione ya cambies las partes. Más que nada, porque no existe FragmentActivity y puedes utilizar Activity normal, para que no te tengas que preocupar y funciona mucho mejor, luego realizar el cambio no es difícil (siempre que la biblioteca de soporte lo permita).

      Y ya sabes que te echaremos las manos que hagan falta 🙂

  48. Buen articulo de fragmentos, describo a continuación el problema que tengo con respecto a tomar una cadena desde los recursos en este caso string.xml.

    Ya dentro del archivo Lista_contenido.java

    quiero llamar a esa cadena con:

    aniadirEntrada(new Lista_entrada(“0”, R.drawable.im_buho, “BUHO”, getString(R.string.ejemplotexto)));

    y es ahi donde me aparece el error “Cannot make a static reference to the non-static method getString(int) from the type Context”

    Saludos y gracias

    1. string name=”ejemplotexto”>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum commodo viverra diam, eget bibendum sem tincidunt vel.

      </string

    2. Ya sé lo que te ocurre David. Aquí en el artículo hemos puesto el mínimo código para entenderlo y hemos usado para ello estáticos dentro de Lista_contenido.java, y getString() es dinámico. Tendrás que modificar ésta y que se añadan las entradas desde un lugar que no se a estático (por ejemplo el constructor). Además, getString() requiere del Context de Activity y Lista_contenido.java no hereda de Activity (o bien le pasas el Context de Activity o lo haces todo dentro de tal).

  49. Buen tuto, sobre todo para los no expertos como yo.
    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…

  50. Buenas tardes!

    Tremendo aporte de verdad, gracias. Tengo una duda. Si tengo un fragmentList (izquierdo), un fragmentDetail (centro) y otro fragmentList(derecho) y quisiera hacer que los fragmentLists laterales aparecieran con arrastrar el dedo, como lo hace facebook o Viber, cómo sería?? Me gustaría algo como eso, y también como lo hace Hangouts,que el fragmentList derecho siempre está (un pedacito), luego lo termino de arrastrar con el dedo.

    Les agradecería la ayuda.

  51. Buenas, quisiera hacer otra consulta. Si lo que quisiera implementar que los detalles fueran listview. Por ejemplo, los elementos serian Continente y cada vez que elijo uno, se muestren en detalles, el listado de los países que pertenecen a ese contienente. Tendría que modificar Fragment_Detalle????.
    Desde ya muchas gracias

      1. bien, lo hice y funciono perfecto, me quedaron duplicadas las listas. Despues lo que quise hacer es modificar el contenido del listado, por ejemplo. Si presiono en buho, quiero que en detalles me aparezca un listado de los distintos tipos de buhos.

        por lo que antes de la linea que se llama al adaptador,
        setListAdapter(new Lista_adaptador(getActivity(), R.layout.layout_elemento_listado, Lista_contenido.ENTRADAS_LISTA)

        agrego datos a una clase que es parecida a “Lista_contenido”, pero no están las entradas estaticas, es decir agrego datos de los distintos tipos de buhos. Pero cuando llamo esa linea, salta un error.

        mi pregunta es, el adaptador “Lista_adaptador”, ¿solo funciona con variables estáticas??? porque es como si no me aceptara los datos que le envio.
        Y en caso de ser otro el inconveniente,¿ cual seria tu sugerencia para mostrar datos diferentes en los detalles?.
        Desde ya muchas gracias, y mil disculpas por algunas preguntas muy obvias, pero todavia estoy aprendiendo.

        1. Lo del truco de las variables estáticas de “Lista_contenido.java” es para simular la base de datos, descarga de internet, arrays, etc. Fíjate en el ejemplo de http://jarroba.com/listview-o-listado-en-android/, podrás comprobar como se añaden datos dinámicos (que no son estáticos).

          Este ejemplo está muy bien para aprender. Ahora que necesitas datos que vienen de otro lado, te sugiero que elimines (o modifiques) la clase “Lista_contenido.java”, pues como he dicho es una simulación de una base de datos que deberás de modificar.

          Cualquier cosa que necesites nos dices 🙂

          1. Te agradezco mucho por tu ayuda, tenia un error simple y no lo podia ver.
            usaba la linea:
            setListAdapter(new Lista_adaptador(getActivity(), R.layout.fragment_person_detail,datos)…

            donde “datos” es una variable local por ejemplo
            (seguí tu consejo y creé una nueva clase llamada “List_Entrada”)

            List_Entrada datos = new ArrayList();
            datos.add(new List_Entrada(“01″,R.drawable.buho1,”Buho”,”descripcion 1″));
            datos.add(new List_Entrada(“02″,R.drawable.buho2,”Buho”,”descripcion 2″));

            cuando implementaba el adapter, el error era que en el metodo …

            onEntrada(Object entrada, View view)

            nunca actualizaba la variable “entrada”, que al modificar mis datos es del tipo “List_Entrada” y ya no la del ejemplo jajaj xD. y quedaría

            texto_superior_entrada.setText(((List_Entrada) entrada).textoEncima);

            bueno, gracias nuevamente. Y lo mas probable es que seguiré haciendo preguntas, porque ahora me falta el tema de los listener de estas nuevas listas, tengo que leer el articulo de nuevo, donde explicas eso.
            gracias again xD

  52. Hola, primero que nada, muchas gracias por el aporte. Tengo una consulta, en Fragment_Lista.java en el cogido es:
    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);
    }
    No haría falta hacer lo mismo con el texto inferior? o no es necesario?
    Desde ya muchas gracias

    1. Buenas Cristian,

      No es necesario, ya que en Fragment_Lista.java se toma el elemento del listado (ya que un elemento del listado tiene imagen y título, pero no descripción que es el texto inferior), no del detalle ¡ojo!.

      Dónde sí se añade el texto inferior es en Fragment_Detalle.java

      1. bien, muchas gracias, mi confucion vino a raíz de leer el otro articulo de ListView, ya que en ese ejemplo el listView viene con tres elementos. En este caso se trata del listado de titulo e imagen, no del detalle. Gracias nuevamente por el tutorial y por aclarar dudas.

  53. Buenas excelente tutorial pero tengo una duda
    estoy usando las carpetas layout-large y layout-large-portal respectivamente para que se acomode de acuerdo a la pantalla si es grande sea vertical u horizontal, y me sale un inconveniente al momento de darle click a la lista el detalle no me aparece en el framelayout del activityhorizontal o vertical sino que me carga ocupando todo en entorno y no se en que me pueda estar equivocando.

    Clase detalle
    public class Activity_Detalle extends FragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.layout_activity_detalle);

    Fragment_Detalle fragment =null;

    if (savedInstanceState == null) {
    fragment = new Fragment_Detalle();
    getSupportFragmentManager().beginTransaction().add(R.id.framelayout_contenedor_detalle, fragment).commit();

    }
    }
    }

    como veras en el codigo es como que solo escojiese el layout_activity_detalle que es el que es para smarthphone y no me escoje el activity compuesto layout_activity_principal_dospaneles (por el fragment y framelayout )

    en que me puedo estar equivocando?

    1. Buenas Jhon,

      por lo que nos comentas, lo más seguro sea que no esté correctamente configurado los ficheros layout.xml que identifican los alias. Revisa que estén los nombres de los ficheros bien puestos en este alias.

      1. Buenas, gracias ya lo pude resolver era que yo estaba preguntando mal lo siguiente:
        if (findViewById(R.id.framelayout_contenedor_detalle) != null) {
        dosFragmentos = true;
        }
        }
        en vez de findViewById() , estaba poniendo getSupportFragmentManager().findFragmentById() por eso siempre me cojia por falso cargar el intent y no el fragment que le habia indicado.

        gracias con todo

        saludos
        Jhon

  54. Buen material. Enhorabuena.
    La verdad que lo he seguido y me ha quedado muy claro el como hay que usar fragments para distintas pantallas. Pero tengo una duda (o por lo menos no se como enfocarlo). Tengo un listado con Nombres (por ejemplo Clientes) y en el fragment de detalle quiero poder editar esos clientes. Hasta hay todo bien, ¿pero que sucede cuando no hay ningún dato en la base de datos? El listado evidentemente no aparece (y creo que en versiones 4.x aparece el típico circulo dando vueltas) y por lo tanto el detalle tampoco según esta el código. ¿Como podríamos implantarlo para que sino hay ningún dato se muestre el detalle en modo de alta o como como mostrar el detalle ya que el modo de trabajo se puede pasar por argumentos….

    Gracias por la ayuda, saludos!!!

  55. Hola, excelente tutorial y muy bien explicado, en este momento tengo una duda.. Como puedo implementar esta lista donde el contenido de la lista se debe de mostrar dentro de un fragment. Porque tengo otra lista principal donde para ingresar a la lista del tutorial tengo que seleccionar un elemento de la lista y asi llegar a visualizar la lista del tutorial, el detalle es que en mi lista principal se implemente un Fragment no un Activity. Me doy a entender? Gracias….

      1. Lo que necesito es que este listado (ListFragment) este al iniciar junto a otros fragments en otro listado:
        private void displayView(int position) {
        Fragment fragment = null;
        switch (position) {
        case 0:
        fragment = new NuevoListado();
        break;

        ……
        Donde nuevo listado es la lista del tutorial,

  56. Excelente tutorial.
    Una forma facil de realizar un filtrado de un listview normal seria esta :

    //create an ArrayAdaptar from the String Array
    dataAdapter = new ArrayAdapter(this,
    R.layout.country_list, countryList);
    ListView listView = (ListView) findViewById(R.id.listView1);
    // Assign adapter to ListView
    listView.setAdapter(dataAdapter);

    //enables filtering for the contents of the given ListView
    listView.setTextFilterEnabled(true);

    listView.setOnItemClickListener(new OnItemClickListener() {
    public void onItemClick(AdapterView parent, View view,
    int position, long id) {
    // When clicked, show a toast with the TextView text
    Toast.makeText(getApplicationContext(),
    ((TextView) view).getText(), Toast.LENGTH_SHORT).show();
    }
    });

    EditText myFilter = (EditText) findViewById(R.id.myFilter);
    myFilter.addTextChangedListener(new TextWatcher() {

    public void afterTextChanged(Editable s) {
    }

    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    }

    public void onTextChanged(CharSequence s, int start, int before, int count) {
    dataAdapter.getFilter().filter(s.toString());
    }
    });

    Para adaptarlo a vuestro caso, debería de realizar el filtrado en la clase Lista_adaptador pasandole en la cabecera del constructor el string por el que filtro?

    Muchas gracias.

    1. Buenas Adrian,

      para hacerlo lo tienes muy fácil desde ahí, solo has de implementar el interfaz Filterable en el adapatador. En el método getFilter() tienes que crear un nuevo filtro a devolver con new Filter(){} he implementar sus métodos. El resto ya es lógica de lo que tiene que hacer tu filtro 🙂

  57. Hola muy buenas. Soy nuevo en esto, llevo poco tiempo con programación android. El tutorial me sirvió de mucho. Lo que no se bien como hacer es implementar esta aplicación con base de datos sqlite. Que las imágenes se guarden el sd y se carguen a través de la base de datos. Gracias.

    1. Muy buenas Miguel,

      De momento no tenemos tutoriales para lo que nos pides pero te recomiendo algunos 😀

      Para la base de datos, como bien indicas tienes que usar Sqlite, te recomiendo que eches un vistazo a: http://developer.android.com/training/basics/data-storage/databases.html.

      Para guardar las imágenes en la memoria interna o en la externa tienes información en: http://developer.android.com/guide/topics/data/data-storage.html#filesInternal

      1. Hola, buen tutorial para iniciar android, tengo una consulta:

        He tenido problemas tratando de obtener la descripcion de los animales desde el archivo string.xml, me aparece
        Cannot make a static reference to the non-static method getString(int) from the type Context

        Cuando en vez de la descripcion llama al nombre del string:

        getString(R.string.descripcionanimal1)

        Saludos

  58. Excelente tutorial, pero ayúdame con algo:
    Al momento de la implementación, en el activity “Activity_Detalle.java” y en el “Activity_Principal.java” tengo un problema solo en 2 métodos: el add y el replace.

    Activity_Detalle.java

    import android.os.Bundle;
    import android.support.v4.app.FragmentActivity;

    getSupportFragmentManager().beginTransaction().add(R.id.framelayout_contenedor_detalle, fragment).commit();
    “The method add(int, Fragment) in the type FragmentTransaction is not applicable for the arguments (int, Fragment_Detalle)”

    Activity_Principal.java

    import android.content.Intent;
    import android.os.Bundle;
    import android.support.v4.app.FragmentActivity;
    import android.app.Fragment;

    getSupportFragmentManager().beginTransaction().replace(R.id.framelayout_contenedor_detalle, fragment).commit();
    “The method replace(int, Fragment) in the type FragmentTransaction is not applicable for the arguments (int, Fragment_Detalle)”

    Gracias por la ayuda

    1. El error que comentas es típico de exportar la librería de Fragments que no son. Eclipse te da a elegir entre dos. Tienes que exportar “android.support.v4.app.Fragment”. Fíjate que has exportado “android.app.Fragment” y con esta no funcionan bien los Fragments.

      Voy a añadir una nota al tutorial para evitar que se tenga este problema. Gracias por el feedback Jonathan 🙂

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies

ACEPTAR