Inicio Android Interfaz de usuario en Android: Fragments

Interfaz de usuario en Android: Fragments

por sgoliver

Cuando empezaron a aparecer dispositivos de gran tamaño tipo tablet, el equipo de Android tuvo que solucionar el problema de la adaptación de la interfaz gráfica de las aplicaciones a ese nuevo tipo de pantallas. Una interfaz de usuario diseñada para un teléfono móvil no se adaptaba fácilmente a una pantalla varias pulgadas mayor. La solución a esto vino en forma de un nuevo tipo de componente llamado Fragment.

Un fragment no puede considerarse ni un control ni un contenedor, aunque se parecería más a lo segundo. Un fragment podría definirse como una porción de la interfaz de usuario que puede añadirse o eliminarse de la interfaz de forma independiente al resto de elementos de la actividad, y que por supuesto puede reutilizarse en otras actividades. Esto, aunque en principio puede parecer algo trivial, nos va a permitir poder dividir nuestra interfaz en varias porciones de forma que podamos diseñar diversas configuraciones de pantalla, dependiendo de su tamaño y orientación, sin tener que duplicar código en ningún momento, sino tan sólo utilizando o no los distintos fragmentos para cada una de las posibles configuraciones. Intentemos aclarar esto un poco con un ejemplo.

No quería utilizar el ejemplo típico que aparece en todos los tutoriales, pero en este caso creo que es el más ilustrativo. Supongamos una aplicación de correo electrónico, en la que por un lado debemos mostrar la lista de correos disponibles, con sus campos clásicos De y Asunto, y por otro lado debemos mostrar el contenido completo del correo seleccionado. En un teléfono móvil lo habitual será tener una primera actividad que muestre el listado de correos, y cuando el usuario seleccione uno de ellos se navegue a una nueva actividad que muestre el contenido de dicho correo. Sin embargo, en una tablet puede existir espacio suficiente para tener ambas partes de la interfaz en la misma pantalla, por ejemplo en una tablet en posición horizontal podríamos tener una columna a la izquierda con el listado de correos y dedicar la zona derecha a mostrar el detalle del correo seleccionado, todo ello sin tener que cambiar de actividad.

Antes de existir los fragments podríamos haber hecho esto implementando diferentes actividades con diferentes layouts para cada configuración de pantalla, pero esto nos habría obligado a duplicar gran parte del código en cada actividad. Tras la aparición de los fragments, colocaríamos el listado de correos en un fragment y la vista de detalle en otro, cada uno de ellos acompañado de su lógica de negocio asociada, y tan sólo nos quedaría definir varios layouts para cada configuración de pantalla donde se incluyan [o no] cada uno de estos fragment.

A modo de aplicación de ejemplo para este artículo, nosotros vamos a simular la aplicación de correo que hemos comentado antes, adaptándola a tres configuraciones distintas: pantalla «normal» (p.e. un teléfono), pantalla grande horizontal (p.e. tablet) y pantalla grande vertical (p.e. tablet). Para el primer caso colocaremos el listado de correos en una actividad y el detalle en otra, mientras que para el segundo y el tercero ambos elementos estarán en la misma actividad, a derecha/izquierda para el caso horizontal, y arriba/abajo en el caso vertical.

Definiremos por tanto dos fragments: uno para el listado y otro para la vista de detalle. Ambos serán muy sencillos. Al igual que una actividad, cada fragment se compondrá de un fichero de layout XML para la interfaz (colocado en alguna carpeta /res/layout) y una clase kotlin para la lógica asociada.

El primero de los fragment a definir contendrá tan sólo un control RecyclerView, para el que definiremos un adaptador personalizado para mostrar dos campos por fila («De» y «Asunto»). Ya describimos cómo hacer esto en el artículo dedicado al control RecyclerView. El código de este adaptador será totalmente análogo al que ya vimos en el artículo mencionado:

data class Correo(val de: String, val asunto: String, val texto: String)

class AdaptadorCorreos(
    private val datos: MutableList<Correo>,
    private val clickListener : (Correo) -> Unit) :
    RecyclerView.Adapter<AdaptadorCorreos.CorreosViewHolder>() {

    class CorreosViewHolder(val item: View) : RecyclerView.ViewHolder(item) {
        val lblDe = item.findViewById(R.id.lblDe) as TextView
        val lblAsunto = item.findViewById(R.id.lblAsunto) as TextView

        fun bindCorreo(correo: Correo){
            lblDe.text = correo.de
            lblAsunto.text = correo.asunto
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CorreosViewHolder {
        val item = LayoutInflater.from(parent.context)
            .inflate(R.layout.listitem_correo, parent, false) as LinearLayout

        return CorreosViewHolder(item)
    }

    override fun onBindViewHolder(holder: CorreosViewHolder, position: Int) {
        val correo = datos[position]

        holder.bindCorreo(correo)

        holder.item.setOnClickListener{clickListener(correo)};
    }

    override fun getItemCount() = datos.size
}

El layout XML del fragment de listado (lo llamaremos fragment_listado.xml) quedaría de la siguiente forma:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/lstListado"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

Y por último veamos cómo quedaría nuestra clase asociada al fragment de listado:

class FragmentListado : Fragment() {

    private lateinit var lstListado : RecyclerView

    private val datos =
        MutableList(5) { i -> Correo("Persona $i", "Asunto del correo $i", "Texto del correo $i") }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return inflater.inflate(R.layout.fragment_listado, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        lstListado = view!!.findViewById(R.id.lstListado)

        val adaptador = AdaptadorCorreos(datos) {
            //Más adelante veremos qué hacer en este evento
        }

        lstListado.layoutManager =
            LinearLayoutManager(this.context, LinearLayoutManager.VERTICAL, false)

        lstListado.addItemDecoration(
            DividerItemDecoration(this.context, DividerItemDecoration.VERTICAL)
        )

        lstListado.adapter = adaptador
    }
}

Si observamos con detenimiento las clases anteriores veremos que no existe casi ninguna diferencia con los temas ya comentados en artículos anteriores del curso sobre creación de actividades y utilización de controles de tipo lista (RecyclerView) y adaptadores personalizados. La única diferencia que encontramos aquí respecto a ejemplos anteriores, donde definíamos actividades en vez de fragments, son los métodos que sobrescribimos. En el caso de los fragment son normalmente dos: onCreateView() y onActivityCreated().

El primero de ellos, onCreateView(), es el «equivalente» al onCreate() de las actividades, y dentro de él es donde normalmente asignaremos un layout determinado al fragment. En este caso tendremos que «inflarlo» (convertir el XML en la estructura de objetos kotlin equivalente) mediante el método inflate() pasándole como parámetro el ID del layout correspondiente, en nuestro caso fragment_listado.

El segundo de los métodos, onActivityCreated(), se ejecutará cuando la actividad contenedora del fragment esté completamente creada. En nuestro caso, estamos aprovechando este evento para obtener la referencia al control RecyclerView y asociarle su adaptador. Sobre la definición del adaptador personalizado AdaptadorCorreos no comentaremos nada porque es casi idéntico al ya descrito en el artículo sobre RecyclerView.

Con esto ya tenemos creado nuestro fragment de listado, por lo que podemos pasar al segundo, que como ya dijimos se encargará de mostrar la vista de detalle. La definición de este fragment será aún más sencilla que la anterior. El layout, que llamaremos fragment_detalle.xml, tan sólo se compondrá de un cuadro de texto:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#FFBBBBBB"
    android:padding="8dp">

    <TextView
        android:id="@+id/txtDetalle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="25sp"/>

</LinearLayout>

Y por su parte, la clase kotlin asociada, se limitará a inflar el layout de la interfaz. Adicionalmente añadiremos un método público, llamado mostrarDetalle(), que nos ayude posteriormente a asignar el contenido a mostrar en el cuadro de texto.

class FragmentDetalle : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return inflater.inflate(R.layout.fragment_detalle, container, false)
    }

    fun mostrarDetalle(texto: String) {
        val txtDetalle = view!!.findViewById(R.id.txtDetalle) as TextView

        txtDetalle.text = texto
    }
}

Una vez definidos los dos fragments, ya tan solo nos queda definir las actividades de nuestra aplicación, con sus respectivos layouts que harán uso de los fragments que acabamos de implementar.

Para la actividad principal definiremos 3 layouts diferentes: el primero de ellos para los casos en los que la aplicación se ejecute en una pantalla normal (por ejemplo un teléfono móvil) y dos para pantallas grandes (por ejemplo una tablet, uno pensado para orientación horizontal y otro para vertical). Todos se llamarán activity_main.xml, y lo que marcará la diferencia será la carpeta en la que colocaremos cada uno. Así, el primero de ellos lo colocaremos en la carpeta por defecto /res/layout (pantalla normal), y los otros dos en las carpetas /res/layout-large (pantalla grande con orientación horizontal) y /res/latout-large-port (pantalla grande con orientación vertical). De esta forma, según el tamaño y orientación de la pantalla Android utilizará automáticamente un layout u otro de forma automática sin que nosotros tengamos que hacer nada.

Para el caso de pantalla normal, la actividad principal mostrará tan sólo el listado de correos, por lo que el layout incluirá tan sólo el fragment FragmentListado.

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    class="net.sgoliver.android.fragments.FragmentListado"
    android:id="@+id/frgListado"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Como podéis ver, para incluir un fragment en un layout utilizaremos una etiqueta <fragment> con un atributo class que indique la ruta completa de la clase kotlin correspondiente al fragment, en este primer caso net.sgoliver.android.fragments.FragmentListado. Los demás atributos utilizados son los que ya conocemos de idlayout_width y layout_height.

En este caso de pantalla normal, la vista de detalle se mostrará en una segunda actividad, por lo que también tendremos que crear su layout, que llamaremos activity_detalle.xml. Veamos rápidamente su implementación:

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    class="net.sgoliver.android.fragments.FragmentDetalle"
    android:id="@+id/frgDetalle"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Como vemos es análoga a la anterior, con la única diferencia de que añadimos el fragment de detalle en vez de el de listado.

Por su parte, el layout para el caso de pantalla grande horizontal, será de la siguiente forma:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment class="net.sgoliver.android.fragments.FragmentListado"
        android:id="@+id/frgListado"
        android:layout_weight="30"
        android:layout_width="0px"
        android:layout_height="match_parent" />

    <fragment class="net.sgoliver.android.fragments.FragmentDetalle"
        android:id="@+id/frgDetalle"
        android:layout_weight="70"
        android:layout_width="0px"
        android:layout_height="match_parent" />

</LinearLayout>

Como veis en este caso incluimos los dos fragment en la misma pantalla, ya que tendremos espacio de sobra, ambos dentro de un LinearLayout horizontal, asignando al primero de ellos un peso (propiedad layout_weight) de 30 y al segundo de 70 para que la columna de listado ocupe un 30% de la pantalla a la izquierda y la de detalle ocupe el resto.

Por último, para el caso de pantalla grande vertical será prácticamente igual, sólo que usaremos un LinearLayout vertical.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment class="net.sgoliver.android.fragments.FragmentListado"
        android:id="@+id/frgListado"
        android:layout_weight="40"
        android:layout_width="match_parent"
        android:layout_height="0px" />

    <fragment class="net.sgoliver.android.fragments.FragmentDetalle"
        android:id="@+id/frgDetalle"
        android:layout_weight="60"
        android:layout_width="match_parent"
        android:layout_height="0px" />

</LinearLayout>

Hecho esto, ya podríamos ejecutar la aplicación en el emulador y comprobar si se selecciona automáticamente el layout correcto dependiendo de las características del AVD que estemos utilizando. En mi caso he definido 2 AVD, uno con pantalla normal y otro grande al que durante las pruebas he modificado su orientación (pulsando Ctrl+F12). El resultado fue el siguiente:

Pantalla normal:

Pantalla grande vertical:

Pantalla grande horizontal:

Como vemos en las imágenes anteriores, la interfaz se ha adaptado perfectamente a la pantalla en cada caso, mostrándose uno o ambos fragments, y en caso de mostrarse ambos distribuyéndose horizontal o verticalmente.

Lo que aún no hemos implementado en la lógica de la aplicación es lo que debe ocurrir al pulsarse un elemento de la lista de correos. Para ello, empezaremos implementado el evento de click del listado, en la definición de su adaptador, dentro del método onActivityCreated() de la clase FragmentListado. Lo que hagamos al capturar este evento dependerá de si en la pantalla se está viendo el fragment de detalle o no:

  1. Si existe el fragment de detalle habría que obtener una referencia a él y llamar a su método mostrarDetalle() con el texto del correo seleccionado.
  2. En caso contrario, tendríamos que navegar a la actividad secundaria DetalleActivity para mostrar el detalle.

Sin embargo existe un problema, un fragment no tiene por qué conocer la existencia de ningún otro, es más, deberían diseñarse de tal forma que fueran lo más independientes posible, de forma que puedan reutilizarse en distintas situaciones sin problemas de dependencias con otros elementos de la interfaz. Por este motivo, el patrón utilizado normalmente en estas circunstancias no será tratar el evento en el propio fragment, sino definir y lanzar un evento personalizado al pulsarse el item de la lista y delegar a la actividad contenedora la lógica del evento, ya que ella sí debe conocer qué fragments componen su interfaz. ¿Cómo hacemos esto? Pues de forma análoga a cuando definimos eventos personalizados para un control. Primero definimos una interfaz con el método asociado al evento, en este caso llamada CorreosListener con un único método llamado onCorreoSeleccionado()

interface CorreosListener {
    fun onCorreoSeleccionado(correo: Correo)
}

Y posteriormente declaramos un atributo de la clase con esta interfaz y definimos un método setXXXListener() para poder asignar el evento desde fuera de la clase. Volvamos a la clase FragmentListado para ver cómo quedaría:

class FragmentListado : Fragment() {

    //...
    var listener : CorreosListener? = null
    //...

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        lstListado = view!!.findViewById(R.id.lstListado)

        val adaptador = AdaptadorCorreos(datos) {
            listener?.onCorreoSeleccionado(it)
        }

        //...
    }

    fun setCorreosListener(l: CorreosListener) {
        listener = l
    }

    fun setCorreosListener(seleccion: (Correo) -> Unit) {
        listener = object:CorreosListener {
            override fun onCorreoSeleccionado(correo: Correo) {
                seleccion(correo)
            }
        }
    }
}

Como vemos, una vez definida toda esta parafernalia, lo único que deberemos hacer en el evento de click de la lista será lanzar nuestro evento personalizado onCorreoSeleccionado() pasándole como parámetro el correo seleccionado.

Hecho esto, el siguiente paso será tratar este evento en la clase kotlin de nuestra actividad principal. Para ello, en el onCreate() de nuestra actividad, obtendremos una referencia al fragment mediante el método getFragmentById() del fragment manager (componente encargado de gestionar los fragments) y asignaremos el evento mediante su método setCorreosListener() que acabamos de definir.

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val frgListado = supportFragmentManager.findFragmentById(R.id.frgListado) as FragmentListado

        frgListado.setCorreosListener {
            val frgDetalle: Fragment? = supportFragmentManager.findFragmentById(R.id.frgDetalle)

            if (frgDetalle != null)
                (frgDetalle as FragmentDetalle).mostrarDetalle(it.texto)
            else {
                val i = Intent(this, DetalleActivity::class.java)
                i.putExtra(DetalleActivity.EXTRA_TEXTO, it.texto)
                startActivity(i)
            }
        }
    }
}

La mayor parte del interés de la clase anterior está en la implementación pasada al método setCorreosListener(). Ésta es la lógica que se ejecutará cuando el fragment de listado nos avise de que se ha seleccionado un determinado item de la lista. Esta vez sí, la lógica será la ya mencionada, es decir, si en la pantalla existe el fragment de detalle simplemente lo actualizaremos mediante mostrarDetalle() y en caso contrario navegaremos a la actividad DetalleActivity. Para este segundo caso, crearemos un nuevo Intent con la referencia a dicha clase, y le añadiremos como parámetro extra un campo de texto con el contenido del correo seleccionado. Finalmente llamamos a startActivity() para iniciar la nueva actividad. En el artículo que dedicamos a crear nuestra primera aplicación Android sencilla explicamos con más detalle todo este proceso de iniciar una nueva actividad pasándole datos extra.

Y ya sólo nos queda comentar la implementación de esta segunda actividad, DetalleActivity. El código será muy sencillo, y se limitará a recuperar el parámetro extra pasado desde la actividad anterior y mostrarlo en el fragment de detalle mediante su método mostrarDetalle(), todo ello dentro de su método onCreate().

class DetalleActivity : AppCompatActivity() {
    companion object {
        val EXTRA_TEXTO : String = "net.sgoliver.android.fragments.EXTRA_TEXTO"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_detalle)

        val detalle = supportFragmentManager.findFragmentById(R.id.frgDetalle) as FragmentDetalle

        detalle.mostrarDetalle(intent.getStringExtra(EXTRA_TEXTO))
    }
}

Y con esto finalizaríamos nuestro ejemplo. Si ahora volvemos a ejecutar la aplicación en el emulador podremos comprobar el funcionamiento de la selección en las distintas configuraciones de pantalla.

Puedes consultar y/o descargar el código completo de los ejemplos desarrollados en este artículo accediendo a la pagina del curso en GitHub.

También te puede interesar

58 comentarios

Petru Rares 27/01/2013 - 11:53

Sin duda el mejor post de este año, muy útil para la mayoria de la gente. Ya espero llegar a este tema que es muy interesante.

¡Gracias por el gran trabajo que estas haciendo!

Responder
Usuario 31/01/2013 - 7:47

Gracias por dedicar tu tiempo a realizar este curso que en lo personal me es muy útil.

Solo como comentario y con el fin de mejorar esta sección de Fragments quisiera mencionar que el archivo XML listitem_correo.xml no lo describes, pero sin embargo se hace referencia a el en la clase AdaptadorCorreos.

Menciono lo anterior porque muchos somos nuevos en Android y a veces nos cuesta trabajo llenar los espacios vacíos que en ocasiones se omiten por ser sencillos.

Saludos y felicidades por tu trabajo.

Responder
sgoliver 31/01/2013 - 7:57

Hola Luis, ese fichero no lo describo porque nada tiene que ver con el tema de los fragments, sino con el de las listas y adaptadores, que como indico un par de veces en el texto ya tiene otro artículo del curso dedicado especialmente a ellos. Saludos.

Responder
Lugh 31/01/2013 - 23:42

Consulta no entiendo porque usas esta linea » public static final String EXTRA_TEXTO =
«net.sgoliver.android.fragments.EXTRA_TEXTO»;» para tomar el dato.
y cual es su finalidad. Tampoco entiendo donde definis este atributo DetalleActivity.EXTRA_TEXTO.

Estoy tratando de implemtar un aplicacion utilzando los fragment, por ejemplo cuando tenes varios niveles de navegacion esto como lo haces con los fragment. Porque en activitus por ejemplo. La actividad A llama a la B y esta llama a la C, me explico. La A pasa datos a la B y esta pasa datos a la C.

Esto tenes idea como se puede hacer los fragment.

Responder
Luis Alejandro 04/02/2013 - 23:11

Hola, muchas felicidades por los tutoriales son de lo mejor, y muchas gracias, tengo una duda y es que justo cuando en el xml llamado activity_main de la carpeta res/layout indico el Fragment llamado «FragmentListado», colocando el paquete y el nombre de su clase, justo haciendo esto la clase mencionada me genera muchos errores, me subraya entre esos la parte en donde heredo de Fragment, dejándome esto subrayado, en cambio la clase «FragmentDetalle» si queda sin ningún error, quisiera que me ayudaran con esta inquietud, Gracias nuevamente!

Responder
Jose Maria 05/02/2013 - 12:09

Hola, llevo siguiendo el curso que has creado y, sinceramente, este apartado deja muchísimo que desear en cuanto a explicaciones desde el punto de vista global de «que estoy haciendo?».
Creo que no vendría mal un punto de vista general, incluso con algún diagrama básico para no perdernos en los detalles.
El curso me estaba gustando mucho pero en este apartado me he perdido y la aplicación no responde.

Responder
sgoliver 05/02/2013 - 13:09

Hola José María, ¿dónde te has perdido? ¿qué no entiendes? Indicas que la aplicación «no responde» pero no das más información. ¿te da algún error? ¿aparece algún mensaje en el log? ¿dónde estás probando: en el emulador o en un dispositivo real? ¿no te funciona en algún AVD concreto o en ninguno? ¿estás ejecutando directamente el código que he colgado en github, o has realizado alguna modificación para algún otro AVD o configuración?

Responder
Luis Alejandro 05/02/2013 - 21:10

Hola sgoliver, mi problema es que al ejecutar la aplicacion en un AVD tipo Tablet me funciona a la perfeccion, el problema es en el AVD tipo celular, en donde tenemos que usar 2 actividades, hay la aplicacion no me funciona, cabe anotar que:
public class MainActivity extends FragmentActivity implements CorreosListener, me pone error al poner «FragmentActivity», lo cambie solo por «Activity» y no me muestra error, Igual para la segunda actividad, la de «Detalle», y el otro fragmento de codigo que no me funciona es «getSupportFragmentManager()», me dice que puebe escribiendo «getFragmentManager()», no se si eso sea lo que genere que no me funcione en la AVD de pantalla pequeña, espero una pronta respuesta, Gracias!

Responder
Jesús 14/02/2013 - 3:30

Hola, muy buen tema, ya tenia rato buscando algo sobre las listas personalizadas para Fragments, ahora el problema que tengo es que a la hora de correr el proyecto me marca error y no me deja ejecutarlo, me pone este error:

02-13 20:27:18.278: E/AndroidRuntime(450): FATAL EXCEPTION: main
02-13 20:27:18.278: E/AndroidRuntime(450): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.android_fragments/com.example.android.fragments.MainActivity}: android.view.InflateException: Binary XML file line #2: Error inflating class fragment
02-13 20:27:18.278: E/AndroidRuntime(450): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1967)
02-13 20:27:18.278: E/AndroidRuntime(450): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1992)
02-13 20:27:18.278: E/AndroidRuntime(450): at android.app.ActivityThread.access$600(ActivityThread.java:127)
02-13 20:27:18.278: E/AndroidRuntime(450): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1158)

Responder
Jesús 14/02/2013 - 3:39

Ya pude corregir el error, me hacia falta cambiar la dirección de los paquetes de los fragment.

Responder
Johan Quijano 18/02/2013 - 17:11

Saludos,

Por que todos los ejemplos de Fragmentos es con Listas,

Un ejemplo con un simple click en un boton como seria?

xD

Responder
Johan Quijano 21/02/2013 - 17:29

Saludos,

Una pregunta. Puede insertar un fragment dentro de otro fragment?

Resulta que tengo un Viewpager con dos fragments. Pero dentro de cada fragment de los dos principales necesito tener más elementos dentro. Puedo hacer esto?

Por ejemplo en el Fragment # 2 tengo un horizontal View. Dentro de este horizontalView necesito colocar varios fragments.

Gracias

Responder
Jose Vazquez 27/02/2013 - 13:14

Gran articulo como siempre, me ha venido muy bien muchos de tu post sobre android para mis aplicaciones, muchas gracias por tan gran trabajo.

Responder
Jesús 21/03/2013 - 0:26

¿Hay forma de llenar el Arreglo de Objetos a partir de un archivo Json? para no llenar la lista manualmente

Responder
Draco Doranters 14/04/2013 - 22:08

Me he bajado los archivos de ejemplo de la pagina de GitHub pero en el apartado de Fragments me encuentro que hace algunas referencias a la liberia ActionBarSherlock, que por lo que he leido es muy buena, pero en esta parte de explicación nunca se menciona el uso de esta libreria, en resumen no he podido corres ni el codigo de esta pagina ni el codigo que he descargado del gitHub

Responder
Jon 25/04/2013 - 11:46

¿Alguien sabe como abrir un intent desde un fragmento?
Me refiero a :

Intent intent= new Intent(Intent.ACTION_VIEW, Uri.parse(«http://www.as.com/»)); startActivity(intent);

Ese tipo de intent en concreto abre el navegador. Eso es lo que no consigo hacer de ninguna manera.

Alguien sabe algo?

Responder
Christian 28/04/2013 - 1:16

Tengo una duda con respeto a al interfaz que se usa para comunicar el fragmento con la actividad principal. Es posible usar la interfaz sin necesidad de implementarla en la actividad? . Algo asi como definir un listener de un botón?… lo intente pero me lanza ClassCastException… y lo peor es que ya me ha funcionado con otras clases extendidas de view..:S

Estoy seguro que tiene que ver con el evento onAtach()

Responder
Jesus 04/06/2013 - 4:37

Hola, tengo una pregunta, como hago para actualizar un Fragment, por ejemplo al principio carga con unos datos, pero después le envío mas información mediante un método, por ejemplo:

nombreFragment.newInstance(valores enviados);

y en el fragment los recibo así:

public static nombreFragment newInstance(variable) {

nombreFragment resultado = new nombreFragment();
Bundle bundle = new Bundle();

bundle.putString(«variable», variable);

resultado.setArguments(bundle);
variable= bundle.getString(«variable»);

return resultado;
}

Pero no eh logrado mostrar la nueva información en el Fragment, de donde envío los datos no es ni Activity ni Fragment, es una clase normal.

Responder
Geo 04/06/2013 - 23:19

Hola estuve creando la carpeta /res/latout-large-port y el proyecto me dice que no es un nombre apropiado para una carpeta, que podria ser?

Responder
Aldo 23/06/2013 - 3:45

muy bueno pero como muestro un layout diferente segun la opcion que pulse???

Responder
Aldo 23/06/2013 - 3:46

muy bueno pero como muestro un layout diferente segun la opcion que pulse???
al estilo de la aplicacion de ajustes de un tablet no se me explico

Responder
Joseph 09/08/2013 - 20:01

gran tutorial =), con respecto a este ejemplo tengo un problema que cuando le doy click a uno de la lista de correos para que me muestre la segunda actividad, que seria el detalle del correo me da error.

Como poder resolverlo? Algun tipo de sugerencia, para que en un futuro puede solventar fallos que se me presenten??

Un Saludo..

Responder
Johan Quijano 12/08/2013 - 23:50

Acá no resuelven dudas,,,,

Gracias por el tutorial de todas formas ;)

Responder
ariel 04/09/2013 - 13:38

hola, primero felicidades por tu trabajo y gracias por aportar con mucho, pero muchos somos nuevos y si no detallas archivos por lo menos deberias habilitar un link de descarga del codigo(sugerencia), en el link que hay no hay este ejemplo. gracias

Responder
admin 05/09/2013 - 9:11

Hola Ariel, no sé a qué link te refieres exactamente, pero el que aparece al final del artículo te lleva al código fuente completo del ejemplo en GitHub, donde puedes consultar el código online o descargarlo.

Adicionalmente, si eres nuevo en Android, no deberías empezar por este capítulo. Esto pretende ser un curso completo, y como este capítulo es de los últimos que he añadido se basa en conocimientos que se deberían haber adquirido en capítulos anteriores. Te recomiendo que empieces por el principio en vez de intentar ir directamente a los temas que te interesan, así te será mucho más sencillo avanzar.

Saludos.

Responder
roberto 06/10/2013 - 12:11

Tengo una duda, para la version tablet es exactamente como quiero que se comporte mi app, pero para la version del smartphone me gustaria que la lista de las personas sea que se pueda deslizar como si se tratase de un slide.. en plan como lo hace youtube o gmail.

¿Es posible hacer eso?

Responder
mario 03/11/2013 - 11:34

Hola SGOliver, quiero felicitarte por el trabajo que haces. La claridad con la que explicaste un concepto como los fragmentos te situan como un referente para quienes hacemos capacitacion. He comprado en su dia tu libro y lo considero el mejor material para impartir formacion en Android. Espero que te lleguen los exitos que sin duda mereces y ojala continues con tu importante trabajo.

Saludos.

Responder
Jorge 07/11/2013 - 5:35

Hola es verdaderamente útil lo que haces te felicito. Yo estoy comenzando a programar pero por la novatez me resulta entender algunos conceptos. Quiero pedirles a ustedes que sin dudaalguna son expertos en la materia me puedan dar referencias donde me pueda ir explicando las funciones de casa clase y métodos y yo poder ir haciendo un resumen, estudiarlo y luego poder hacer mis aplicaciones sin tener que copiar y pegar de foros, que al final del dia gente como ustedes terminan ahorrándonos mucho tiempo por su buena voluntad. Gracias y saludos a todos

Responder
Guillermo 23/11/2013 - 20:22

Antes de nada, felicitarte y darte las gracias por este fabuloso curso. He aplicado esto en una app y me va muy bien, pero me estaba preguntando si el método que habías explicado en el tema de las listas para ahorrar batería y mejorar el rendimiento en el Arrayadapter se podría usar en este caso. Muchas gracias de antemano!

Responder
jorge 20/01/2014 - 3:15

Por que no extendieron de ListFragment ?? Alguna razón en especial ??

Responder
Jonathan V. 20/01/2014 - 22:03

Hola!! primero que nada felicitarte por el gran trabajo del curso, estoy esperando que tengas listo la v4 para conseguir el pdf por que esta de lujo. Tambien me gustaria aprovechar para comentarte mi problema, al seguir todo este capitulo al pie de la letra tengo problemas al correr la aplicacion en pantallas grandes, por que la app para telefonos si me funciona bien, pero para tablets al correrla me manda error el dispositivo de que desafortunadamente tuvo que ser parada, pongo el error del log:

01-20 14:52:47.788 11297-11297/? E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.example.fragments, PID: 11297
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.fragments/com.example.fragments.MainActivity}: java.lang.ClassCastException: com.example.fragments.FragmentDetalle cannot be cast to com.example.fragments.FragmentListado
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2195)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245)
at android.app.ActivityThread.access$800(ActivityThread.java:135)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:126)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.ClassCastException: com.example.fragments.FragmentDetalle cannot be cast to com.example.fragments.FragmentListado
at com.example.fragments.MainActivity.onCreate(MainActivity.java:16)
at android.app.Activity.performCreate(Activity.java:5231)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2159)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245)
            at android.app.ActivityThread.access$800(ActivityThread.java:135)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5017)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)

Te menciono tambien que estoy trabajano con el IDE Intellij IDEA 13, y este no me daba la opcion por ninguna parte de agregar el android support v4, asi que he descargado de tu github la carpeta lib con el archivo android-support-v4.jar y lo he metido a mi carpeta del proyecto. Habra que hacer algo adicional?

Muchas gracias

Responder
Gsun 28/01/2014 - 9:56

Hola, muy buen tutorial me ha servido de mucho. Te queria preguntar como añadir un imageview utilizando los recursos para persnoalizarlo segun el elemento seleccionado. Estoy teniendo porblemas con esto ya que no estoy en un acitvity.

Responder
Gsun 28/01/2014 - 13:40

EN FRAGMENT DETALLE.

public void mostrarFoto(String texto) {

texto = «drawable/»+texto;
TextView lblFoto = (TextView)getView().findViewById(R.id.lblFoto);
lblFoto.setText(texto); //para ver si recibo el nombre del fichero, y si

ImageView foto = (ImageView)getView().findViewById(R.id.imageView1);
int imageResource = this.getActivity().getResources().getIdentifier(texto, null, this.getActivity().getPackageName());

}

Responder
Joao 29/01/2014 - 3:33

Hermano Gracias por tu apoyo me sirvio de mucho, pero me gustaria que me explicaras algo en lo que estoy metiendo la pata… Sabes que tengo mi fragment y todo, llamemoslo fragment 1, en ese fragment 1 quiero insertar un boton que me lleve a un fragment o una actividad, o sea en mi fragment 1 quiero poner un boton o imagebutton por ejemplo pongo un perro o un boton que diga perro, quiero que ese boton me lleve a la descripcion de los perros, yo se implementar botones desde una activity pero desde un fragment no lo se… Me gustaria mucho tu ayuda!

Responder
Viyi 07/02/2014 - 12:11

Hola Oliver! ERES UN CRACK! (lástima que no lo puedo poner más grande)
Estoy enganchada a tus documentos (de hecho es con lo que estudio). Sólo una cosa que creo que no te has dado cuenta, pero algunas capturas que has hecho para mostrar el código (por ejemplo, la de la actividad Frament_listado.java, están incompletas (has truncado la imagen). Intentaba hacer este mismo ejercicio en eclipse y me saltaban errores y es que me falta esa parte de código que está cortado.

Igual se agradece la labor, facilitas el entendimiento sobre todo para los que estamos novatos en esto.

GRACIAS!

Responder
Lucho 07/02/2014 - 16:22

Hola, primero que nada felicitaciones por el tutorial.

Segundo, tengo un problema con este ejemplo, ya que al rotar la tablet se pierde el estado, Y por ejemplo si yo llamaria a otra pantalla, tambien se perdería el estado. Tenes idea que se puede implementar para corregir dicho error?

Gracias!

Responder
Alberto 18/02/2014 - 2:12

A la consulta que me refiero, y no dejas clara en el artículo es

«Consulta no entiendo porque usas esta linea ” public static final String EXTRA_TEXTO =
“net.sgoliver.android.fragments.EXTRA_TEXTO”;” para tomar el dato.
y cual es su finalidad. Tampoco entiendo donde definis este atributo DetalleActivity.EXTRA_TEXTO.»

Responder
Danersido 20/02/2014 - 13:18

Genial tutorial.

Pero me gustaría ir más lejos, y la cuestión es la siguiente: en cada elemento del listview necesito añadir un radiogroup y un button. Al pulsar sobre el button es cuando se pasaría al segundo fragment. ¿Alguna ayuda?

Gracias.

Responder
Luis 21/02/2014 - 22:34

Genial este tema de los fragments, casi que fue una recopilación de lo que hemos visto hasta aquí, gracias por tu dedicación, no sabes a cuantas personas ayudas con esto :D

Responder
Prometenn 23/02/2014 - 11:43

Genial! Chapó! Ahora mismo a mi barra de favoritos.

Responder
Ethnoss 17/03/2014 - 9:54

En la linea del ejemplo….¿como se podría implementar un boton que actualizara los correos recibidos a un nuevo contenido, imaginando por ejemplo, otro array Correo[] datos2?. Creo que sería de gran utilidad.

Responder
Martín 06/05/2014 - 2:01

Muy buen post!! Felicitaciones y muchos Agradecimientos por este trabajo Santiago!! Saludos desde Argentina!

Responder
Fernan 12/05/2014 - 12:38

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

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

@SuppressLint(«NewApi»)

public class Razas extends Fragment

implements View.OnClickListener {
ImageView imageAmerica;

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

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

imageAmerica.setOnClickListener(this);

return rootView;
}

@Override
public void onClick(View ImageView) {

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

}

}

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

Responder
sebamawa 16/05/2014 - 6:51

Buen tutorial. Me parece que sería bueno complementarlo con algo de Fragments dinámicos, usando Transactions.
Saludos!

Responder
María 22/05/2014 - 9:56

Hola Salvador, realmente bueno tu post!! :D

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.

Responder
Jordi 23/05/2014 - 15:32

Jonathan V,

Vigila porque creo que el import de Fragment que importas es el de android.support.v4.app.Fragment;

y no android.app.Fragment . Creo que no con éste si que funciona. Por eso Fragment no castea bien. Hereda del incorrecto.

Saludos

Responder
Nitram 24/06/2014 - 16:41

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.

Responder
Guillermo 04/08/2014 - 10:12

Hola Salvador,

He seguido este manual, y funciona correctamente, felicidades

Una pregunta para poder actualizar el listview se suele utilizar el notifyDataSetChanged()

pero al adapterCorreos no admite el metodo y no se como refrescar el listview.

muchas gracias y un saludo

Responder
jorge 30/10/2014 - 20:31

me sale erroe a cada rato mal escrito el codigo

Responder
Carles Quintana 10/12/2014 - 18:02

Me he leído atentamente tu tutorial.
Es tremendamente difícil, al menos para los novatos en fragments. Este es el primer que hago.
Y tengo un gran problema.
El ListView con el que trabajo es un array de string, un «string[]» que contiene un listado.
Y claro, cuando intento adaptarlo al tuyo, debo hacer algo mal, ya que intento ejecutar la aplicación y se me cierra.
Y finalmente, me da la impresión que escribes código y a continuación lo eliminas y lo sustituyes por otro diferente.
Voy a empezar de nuevo desde cero, a ver si me sale.
Pero si puedes darme alguna indicación con lo de la lista, te estaré agradecido.

Responder
Eduardo Mereles 23/01/2015 - 13:30

Muy buen tutorial, yo me preguntaba como usar fragments dentro de una misma activity y todo el tema de multiples pantallas y ahora me quedo mucho mas claro.

Responder
yoel 09/02/2015 - 17:35

bien con el manual, sin embargo en la parte de fragments omites varias cosas, he segui al pie de la letra tus instrucciones pero tengo error al ejecutar

Error:Execution failed for task ‘:app:dexDebug’.
> com.android.ide.common.internal.LoggedErrorException: Failed to run command:
C:\Users\acer\AppData\Local\Android\sdk\build-tools\21.1.2\dx.bat –dex –no-optimize –output C:\Users\acer\AndroidStudioProjects\Fragments\app\build\intermediates\dex\debug –input-list=C:\Users\acer\AndroidStudioProjects\Fragments\app\build\intermediates\tmp\dex\debug\inputList.txt
Error Code:
2
Output:
UNEXPECTED TOP-LEVEL EXCEPTION:
com.android.dex.DexException: Multiple dex files define Landroid/support/annotation/AnimRes;
at com.android.dx.merge.DexMerger.readSortableTypes(DexMerger.java:596)
at com.android.dx.merge.DexMerger.getSortedTypes(DexMerger.java:554)
at com.android.dx.merge.DexMerger.mergeClassDefs(DexMerger.java:535)
at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:171)
at com.android.dx.merge.DexMerger.merge(DexMerger.java:189)
at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:454)
at com.android.dx.command.dexer.Main.runMonoDex(Main.java:303)
at com.android.dx.command.dexer.Main.run(Main.java:246)
at com.android.dx.command.dexer.Main.main(Main.java:215)
at com.android.dx.command.Main.main(Main.java:106)

Responder
Dare 19/02/2015 - 15:27

No me muestra nada en el fragmento de detalle al dar click en alguna opción del menu

Responder
Curso Programación Android por Salvador Gómez – Indice de Contenidos | Miguel Moyetones 17/06/2015 - 17:52

[…] Interfaz de usuario en Android: Fragments [v3] […]

Responder
checho 30/08/2015 - 1:48

Como habria que modificar el ejemplo para el caso que se quisiera agregar botones en el detalle? Ya sea para el caso del detalle del fragment como tambien para el caso del detalle de la nueva actividad (en este ultimo caso por ahora solo se muestra el texto del correo).
Desde ya muchas gracias.
Saludos y muy buenos los tutoriales.

Responder
Francisco 22/09/2015 - 19:02

como agrego esto dentro de un navigation drawer ?

Responder
Luis 15/10/2015 - 11:28

Llevaba tiempo sin entender bien como implementar lo de los fragments limitándome a modificar otros ejemplos que encontraba, pero con tu tutorial me ha quedado todo clarísimo.

Gracias!

Responder
Cristóbal 18/05/2017 - 10:39

Tremendo tutorial!!!
Esta vez que dispongo de tiempo sí que estoy siguiéndolo desde el principio y sin ideas preconcebidas de alguna cosilla que haya podido mirarme antes, y entonces creo que, para la gente que igualmente lo empiece a seguir desde cero, estaría bien que añadieses que la nueva actividad para mostrar los detalles hay que declararla en el manifest, pues en caso contrario la aplicación da error y se aborta de forma repentina cuando se hace click sobre un item para que se muestren sus detalles (lógicamente este problema solo ocurre para el dispositivo de tamaño normal, que es para el único que se intenta lanzar la actividad de detalle).

Responder

Responder a Carles Quintana Cancelar respuesta

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, pinche el enlace para mayor información. Aceptar Más Información

Política de Privacidad y Cookies