Fragments en Android
Uno de los conceptos más interesantes que introdujo Android 3.0 fue el de los Fragments. Los Fragments nos permiten segmentar la aplicación de tal modo que podemos crear aplicaciones únicas que se comporten de manera diferente en función del dispositivo sobre el que se ejecuten o de la orientación del mismo. Así podremos dar una solución visual para teléfonos y otra para tablets, por poner un ejemplo.
Introducción
Definiéndolos de una manera informal, los Fragments son trozos de vista que se pueden mostrar o no dependiendo de lo que nos interese en cada momento, ya sea basándonos en la orientación o tamaño del dispositivo normalmente, aunque como siempre podremos añadir cualquier calificador a los XML en los que se definen.
Los Fragments tienen su propio ciclo de vida y su propia interfaz. En cualquier caso, su contexto de ejecución es una Activity, por lo que si la Activity a la que pertenecen es destruida, sus Fragments también serán destruidos. Estos Fragments pueden ser añadidos y eliminados de la Activity de forma dinámica siempre que no se hayan definido en el XML.
La mejor forma de entenderlo es, como siempre, mediante ejemplos. Vamos a desarrollar el caso más típico, el de un listado de registros. Al pinchar sobre uno de ellos nos muestra una vista de detalles. En posición horizontal mostraremos sólo el listado, y en vertical el listado más el detalle del elemento seleccionado.
Aquí podéis ver lo que conseguiremos una vez terminado el tutorial:
Desarrollo
Creando el proyecto
Como os comentaba al principio, desde Android 3.0 ya tenemos incluidas las clases necesarias en el SDK, pero ya sabemos que aún falta mucho para que la gran mayoría actualice a Android 4 en smartphones, por lo que trabajaremos con el Android Support Package, que añade las clases básicas para realizar esta tarea. Los cambios serían mínimos si trabajásemos con un SDK 3.0 en adelante.
Para empezar, nos creamos un nuevo proyecto. Llámalo por ejemplo FragmentTest y elige el API que prefieras, yo trabajaré con el 8 (Android 2.2). Añadimos la Support Library (botón derecho -> Android Tools -> Add Support Library) para poder trabajar con los Fragments y ya estamos listos para empezar a codificar.
Estructura de archivos del proyecto
Es indispensable entender la estructura:
- src
- MainActivity.java -> Clase de la actividad principal
- DetallesActivity.java -> Actividad de detalles, necesaria para la orientación vertical
- ListaFragment.java -> Fragment con el listado de registros a elegir
- DetallesFragment.java -> Fragment que muestra los detalles del registro seleccionado
- res
- layout-> Layouts usados en el caso más genérico, en este caso se corresponderá con la orientación horizontal
- detalles_fragment.xml -> Layout para el Fragment de detalles
- main.xml -> Layout para la actividad principal
- layout-port-> Layouts usados con el dispositivo en vertical
- main.xml -> Layout para la actividad principal en vertical
- detalles_activity.xml -> Layout para la actividad de detalles, que sólo se utiliza en vertical
- layout-> Layouts usados en el caso más genérico, en este caso se corresponderá con la orientación horizontal
Tendremos una actividad principal que en modo horizontal contendrá los dos Fragments, el de Lista y el de Detalles, definido en el main.xml. Si embargo, para la orientación vertical (layout-port) utilizará dos actividades, la Main y la de Detalles, y cada una de ellas contendrá un Fragment, como podrás observar en main.xml y detalles_activity.xml.
Layouts
Comenzamos definiendo todos los layouts:
layout/main.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="fill_parent"
android:orientation="horizontal" >
<fragment
android:id="@+id/listaFragment"
android:layout_width="150dip"
android:layout_height="match_parent"
class="com.limecreativelabs.fragmenttest.ListaFragment" ></fragment>
<fragment
android:id="@+id/detalleFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.limecreativelabs.fragmenttest.ListaFragment" >
</fragment>
</LinearLayout>
layout/detalles_fragment.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/detallesTxt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Texto Detalle"
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
layout-port/main.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="fill_parent" android:orientation="horizontal" > <fragment android:id="@+id/listaFragment" android:layout_width="match_parent" android:layout_height="match_parent" class="com.limecreativelabs.fragmenttest.ListaFragment" /> </LinearLayout>
detalles_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<fragment
android:id="@+id/detallesFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.limecreativelabs.fragmenttest.DetallesFragment" />
</LinearLayout>
Clases Java
Ahora es el turno de las clases.
DetallesFragment simplemente infla su layout cuando se crea la vista y aporta un método para modificar el texto que muestra:
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class DetallesFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.detalles_fragment, container, false);
return view;
}
public void establecerTexto(String item) {
TextView view = (TextView) getView().findViewById(R.id.detallesTxt);
view.setText(item);
}
}
DetallesActivity se utilizará sólo en orientación vertical. Recupera el texto que le enviará la ListaActivity (enseguida lo vemos) a través del Intent y se lo asigna al TextView. En el onCreate hay que comprobar si se encuentra en orientación horizontal, ya que se puede llegar hasta aquí al rotar la pantalla. Si es el caso, se finaliza la actividad.
import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.widget.TextView;
public class DetallesActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getResources().getConfiguration().orientation ==
Configuration.ORIENTATION_LANDSCAPE) {
finish();
return;
}
setContentView(R.layout.detalles_activity);
Bundle extras = getIntent().getExtras();
if (extras != null) {
String s = extras.getString("texto");
TextView view = (TextView) findViewById(R.id.detallesTxt);
view.setText(s);
}
}
}
Nuestra clase de Lista extenderá ListFragment, que es un Fragment especializado en el que el elemento básico es un ListView. Cuando se crea la actividad que incluye este Fragment, se crea el listado de elementos a mostrar y un adaptador para la lista con un estilo de fila de entre los predefinidos que aporta Android.
En el evento que se lanza al seleccionar un elemento, obtenemos el texto y buscamos el Fragment ayudándonos del FragmentManager. Si el Fragment está en el layout, entonces quiere decir que se encuentra actualmente cargado en la vista, y por tanto estamos en la disposicion horizontal. Lo único que hacemos es modificar el texto que se muestra en dicho Fragment. En caso contrario, estámos en la situación en la que tenemos que lanzar una actividad para el detalle, a la que le pasaremos el texto a mostrar.
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class ListaFragment extends ListFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
String[] listItems = new String[] { "Alfa", "Beta", "Gamma",
"Delta", "Epsilon", "Dseta", "Eta", "Theta", "Iota" };
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1, listItems);
setListAdapter(adapter);
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
String registro = (String) getListAdapter().getItem(position);
DetallesFragment fragment = (DetallesFragment) getFragmentManager()
.findFragmentById(R.id.detallesFragment);
if (fragment != null && fragment.isInLayout()) {
fragment.establecerTexto(registro);
}
else {
Intent intent = new Intent(getActivity().getApplicationContext(),
DetallesActivity.class);
intent.putExtra("texto", registro);
startActivity(intent);
}
}
}
A la MainActivity no hay que añadirle nada. Asegúrate de que en el onCreate está inflando el main.xml y que extiende FragmentActivity en lugar de Activity.
Esto es todo lo que necesitas para crear tu primer proyecto con Fragments. Si tienes alguna duda o quieres que haga algún otro tutorial extendiendo este tema para algún caso más concreto, házmelo saber en el área de comentarios.
¿Te ha gustado? Compártelo
43 Comentarios
Trackbacks/Pingbacks
- Ejemplo de vista de detalle en Android usando Fragments | LiME - [...] Tutorial sobre Fragments en Android [...]
- Activities en Android | LiME Creative Labs | LiME Creative Labs - [...] se desarrollan los temas de Services y Broadcasts, quizá quieras echarle un vistazo a los Fragments, las estructuras que ...
- 10 librerías gratuitas que todo desarrollador Android debe conocer - [...] las innovaciones de los últimos SDK con versiones antiguas. Puedes por ejemplo desarrollar con Fragments o utilizar los fantásticos ...










http://www.mediafire.com/download.php?4j3yn5l0p3epy00
Ya os comento.
Ahora a investigar a realizar cosas mas complejas, como por ejemplo en funcion de la lista de la izq abrir distintas activity
He creado una nueva clase que extiende de Fragment, con su correspondiente xml y en la clase ListaFragment y creado un caso en que al pulsar al primer elemento de la lista me cambie el fragment de la siguiente manera:
DetallesFragment fragment = (DetallesFragment) getFragmentManager().findFragmentById(R.id.detalle_Fragment);
Fragment newFragment = new NuevaClaseFragment(); FragmentTransaction transaction = getFragmentManager().beginTransaction(); transaction.replace(fragment.getId(),newFragment); transaction.addToBackStack(null); transaction.commit();
El problema es que se me muestra los dos fragment, uno encima de otro y no se porqué.
De este modo se ejecuta la app y no muestra nada en el FrameLayout.
Otro de los cambios es utilizar
transaction.add en vez de
transaction.replace. Ahora puedo ir cambiando de uno a otro, lo que realmente se hace es crear una nueva clase y mostrarla.
Lo que no he conseguido resolver es lo del pulsar al botón atras, ya que me salta un cierre forzoso.
Gracias de todos modos.
- Fragments y Action Bar se pueden usar en versiones anteriores a ICS ( Froyo, Gingerbread ) con la libreria ActionBarSherlock?? o mejor usar el Android Support Package?
- La siguiente no se como explicarla muy bien. Si tengo un ListActivity y a partir de cada uno de los items lanzo una Activity distinta, para este caso es mejor usar Fragments??
Gracias.
Creo que sí entiendo lo segundo. Cada elemento de la lista abre una Activity distinta, no es la misma con distintos datos. Eso más que de la funcionalidad, depende del aspecto. Si quieres que en las tablets se vea a la izquierda la lista y a la derecha esa Activity, mientras que en los móviles sólo se vea la lista y al pinchar sólo la Activity, necesitas obligatoriamente Fragments.
Si no queda algo claro, escribe de nuevo sin problema. Saludos!
Gracias.
Gracias de nuevo.
Gracias.
Luego también puedes seguir este artículo que hice. Te genera un proyecto preparado con Fragments del support package. Si quieres usarlo con el sdk anterior al 3, tendrás que hacer algún apaño, pero como poco lo puedes usar para mirar el código y ver en qué está fallando el tuyo: http://goo.gl/uJGfW
Como inflo el main.xml??. Porque de hecho me salen “2 columnas” con el alfabeto griego, pero en el momento que pulso sobre algun item, la aplicacion “se rompe” y se cierra.
Siento tanta insistencia, pero es que me gusta bastante la distribucion de este codigo.
En el código de la actividad principal tienes que usar la siguiente línea:
setContentView(R.layout.main);
Eso es inflar, decirle a la actividad que use un layout. El proceso de inflar es convertir el xml en objetos de la interfaz.
Pero si te está mostrando inicialmente la lista es porque ya lo estás haciendo. Esta tarde busco el código, y si lo localizo lo añado al final del artículo para que lo descargues y lo pruebes. Eso de que te salgan dos columnas no me queda muy claro
(Aclaracion: dos columnas esta muy mal explicado, perdona, son dos list con el alfabeto griego en cada una, lo digo porque lo he “volcado” en un tablet con froyo)
Si quiero meter en el fragment de la izquierda, 2 listview o algo más “compuesto”, ya no tendría que extender ese fragment de “ListFragment” no? Tendría que ser un fragment normal?
Y a la hora de cargar ese layout complejo, como sería? En Oncreate llamo a “setContentView” y cargo ese layout?.
Un saludo!
Un saludo.
Enhorabuena por la páginas… es realmente útil.
Simplemente comentar que aunque es una tontería, a lo mejor a alguno le da al ojo… En el código: layout/main.xml hay un error -> en el 2º fragment deberia poner: DetallesFragment.
Un saludo