ViewPager2

Explicación del concepto de ViewPager2

Fuente: developer.android
Fuente: developer.android
Fuente: developer.android

DEFINICIÓN

Hereda de ViewGroup.

Muestra objetos de Views o Fragments en formato deslizable, como si de diapositivas se tratara.

RecyclerView VS ViewPager2

La realidad es que ViewPager2 es muy parecido a un RecyclerView y, de hecho, puede utilizar el Adapter y ViewHolder de un RecyclerView. Sin embargo, tiene una serie de diferencias:

Fuente: medium (Elye)

RecyclerView

Las ventajas que tiene RecyclerView son:

  • Permite utilizar GridLayoutManager.

  • Permite ver varias "Páginas" por VIsta.

  • No frena el Scroll en cada "página".

  • Con LinearSnapHelper se puede hacer que el scroll siempre frene en una "página" y no entre medio.

ViewPager2

Las ventajas que tiene ViewPager2 son:

  • Con TabLayoutMediator permite configurar un TabLayout que funcione en conjunto con el ViewPager2.

  • Con PageTransformer se pueden desarrollar nuevas transiciones entre páginas.

  • con FragmentStateAdaptor permite que se utilicen Fragments en vez de ViewHolders.

  • con FakeDrag se puede crear una zona de la pantalla que controle el scroll.

Elementos comunes

ViewPager2 coje muchos elementos de RecyclerView como son:

  • El uso de ViewHolder para controlar lo que se muestra en cada una de las "páginas".

  • El uso de RecyclerViewAdaptor(ViewHolder) cuando no se utilizan Fragments.

  • El uso de LinearLayoutManager (no se define explícitamente pues es obligatorio).

  • La posibilidad de mostrar una "página" por Vista.

  • El uso de PagerSnapHelper que permite a RecyclerView paginar las vistas de la misma manera que lo hace ViewPager2 y que se pare el scroll en cada una de las "páginas".

CONFIGURACIÓN

Una vez visto lo anterior, sabemos que puede tener dos usos:

  • Uso de ViewPager2 con ViewHolder.

  • Uso de ViewPager2 con Fragments.

Uso de ViewPager2 con ViewHolder

Este caso de uso es muy parecido al funcionamiento de un RecyclerView. Necesitamos desarrollar los siguientes archivos:

  • MainActivity.kt

  • activity_main.xml

  • page_model.xml

  • CharacterModel.kt

  • CharacterProvider.kt

  • PagerAdapter.kt

  • PagerViewHolder.kt

Vamos a comenzar:

Crear la Activity y el Layout

Lo primero que debemos hacer es crear una Activity y en el Layout definir una vista ViewPager2:

Por ahora con el archivo MainActivity.kt no vamos a hacer nada.

Definir el modelo de datos

Los datos que se asignan a las entradas de un RecyclerView pueden provenir de muchos sitios.

  • Pueden encontrarse hardcodeados en la Activity, cosa que no es adecuada.

  • Pueden encontrarse en una data class.

  • Pueden obtenerse de una API.

  • Pueden obtenerse de una consulta a una base de datos.

En este caso, se van a obtener de una data class ya que el resto de opciones son bastante más complejas y requieren de la preexistencia de una API o de una Base de Datos.

Para el modelo de datos, por tanto, vamos a crear una Data Class llamada CharacterModel.kt que va a definir el modelo de personaje y una clase con un método público (companion object en Kotlin) que hará de Proveedor, en este caso CharacterProvider.kt

Definir el Layout de cada página

El Layout de cada página se define de la misma manera que en un RecyclerView con un archivo de Layout en xml llamado page_model.xml.

Y queda de esta manera tan atractiva:

page-model.xml

Definir el Adapter y el ViewHolder

Esto se ha visto en profundidad en la entrada de RecyclerView por lo que aquí no vamos a parar tanto:

Función bind(CharacterModel)

En este caso quería explicar esta función por que es importante a la hora de implementar la lógica de este formato.

Si recordamos en el CharacterModel, teníamos:

Como vemos, la descripción a pesar de ser (físicamente) una String, es de tipo Int. Esto se debe a que al rescatarla del CharacterProvider, se está llamando a un recurso string almacenado en el archivo strings.xml esta llamada devuelve un identificador entero de ese recurso. Es por eso que su tipo es Int a pesar de ser un texto.

Lo mismo ocurre con el background, gender y photo. En este caso, podemos ver como su rescate del CharacterProvider sigue otra lógica que la de la description. Esto se debe a que la description es un recurso string mientras que el resto son recursos drawables.

Por último, la parte que hace referencia a los botones es fundamental para que en la primera página no se muestra el botón de atrás y en la última se muestre el botón de finalizar en vez de el de siguiente.

Su comportamiento lo desarrollaremos posteriormente.

Ejecutar el Pager en la Activity

Una vez tenemos todo lo anterior hecho, podemos ir a MainActivity.kt y crear la función setUpPager().

Esto ya funciona, sin embargo, cada página tiene unos botones que debemos configurar:

Dar funcionalidad a los botones

Lo primero que vamos a hacer es crear una interface que llamaremos OnItemSelected y nos permitirá transmitir esta información entre el Adapter, el ViewHolder y la Activity.

Ahora debemos implementar estas funciones:

PagerViewHolder:

En el ViewHolder introducimos como parámetro la interface y dentro de la función bind definimos que cuando se clique sobre uno de los dos botones se hará una llamada la función que para cada uno corresponde dentro de la interface:

Como además, vamos a utilizar los botones para navegar, necesitamos saber cual es la posición del adaptador, o lo que es lo mismo, la página en la que nos encontramos. Es por eso que en las llamadas a las funciones de la interface se pasa por parámetro la absoluteAdapterPosition.

PagerAdapter

Al PagerAdapter también le debemos mandar por parámetro la interface ya que en la función miembro onCreateViewHolder, le tenemos que pasar por parámetro dicha interface.

MainActivity

En el MainActivity indicamos que la clase extiende de OnItemSelected y nos pedirá que implementemos los miembros. De esta manera podremos darle lógica al comportamiento de los botones:

Finalmente, el resultado es bastante gratificante:

Transformer

Si no nos gusta que la animación sea la clásica de diapositiva, podemos crear nuestras propias animaciones y añadirlas a nuestro ViewPager2.

En este ejemplo sólo vamos a utilizar los dos ejemplos de Google:

Esto lo aplicamos en la función setUpPager() de la MainActivity:

Ejemplo DepthPageTransformer

Ejemplo ZoomOutPageTransformer

Uso de ViewPager2 con Fragments

El caso de los Fragments es un poco más tedioso por que implica un poco más de configuración, sin embargo, es más versátil.

Para este segundo ejemplo vamos a necesitar desarrollar los siguientes archivos:

  • MainActivity.kt

  • activity_main.xml

  • PageFragment.kt

  • fragment_page.xml

  • CharacterModel.kt

  • CharacterProvider.kt

  • MyFragmentStateAdapter.kt

Empecemos:

Crear la Activity y el Layout

Lo primero que debemos hacer es crear una Activity y en el Layout definir una vista ViewPager2:

Por ahora con el archivo MainActivity.kt no vamos a hacer nada.

El CharacterProvider.kt es exactamente el mismo que en el ejemplo anterior.

Sin embargo la CharacterModel.kt que es la data class que ofrece el modelo, es muy importante que implemente la Interface Parcelable.

PARCELABLE vs SERIALIZABLE

En Android, no se pueden mandar objetos entre Activities por medio de Intents o a Fragments a través de los parámetros de manera directa.

Es necesario que el objeto que mandemos implemente la interfaz Parcelable o la interfaz Serializable.

Serializable

https://developer.android.com/reference/java/io/Serializable

Fue la primera interfaz que se implementó para este proceso. Está implementada en Java y es bastante lenta en comparación con Parcelable ya que genera objetos temporales y, por ende, recolección de basura. Su implementación es un poco más sencilla.

Parcelable

https://developer.android.com/reference/android/os/Parcelable

Es más rápida y eficiente que la anterior. Además se encuentra implementada en Kotlin. Es la recomendada.

Una vez la implementemos, nos pedirá que implementemos sus miembros. En este caso, los miembros que implementa Android suelen ser siempre iguales y no hay que hacer ninguna modificación (de manera general):

Crear el Fragment que contendrá la información

Este Fragment será el que indique el Layout de cada una de las páginas:

El archivo PageFragment.kt será el que actue como ViewHolder. Lo configuraremos en pasos posteriores.

Definir el FragmentStateAdapter

A continuación procederemos a definir el Adapter. Este Adapter extenderá de FragmentStateAdapter(fragment) y por ello tendrá que implementar dos miembros:

  • getItemCount()

  • createFragment()

Como necesitamos saber cuantos Ítems tenemos, tendremos que pasarle al Adapter una lista por parámetro.

Ahora que tenemos todo lo necesario podemos implementar los miembros:

getItemCount()

createFragment()

Esta función simplemente llamará al newInstance del Fragment con los parámetros que necesita para bindear las vistas, luego lo veremos:

Y así nos queda un Adapter de la siguiente manera:

Implementar la lógica del Fragment

El Fragment recibirá por parámetro la posición de la página actual y el objeto del tipo CharacterModel que tiene que bindear.

AVISO

La sintaxis del Fragment parece compleja a simple vista pero es la forma estándar de recuperar la información de los parámetro y poderlos utilizar.

Tambien se hace referencia a diferentes etapas del ciclo de vida del Fragment.

Estas conceptos se explicará en la entrada sobre Fragments.

En el Fragment, lo único que afecta a la Lógica de la App es la parte de OnViewCreated que es el momento en el que Bindeamos las vistas con la información del modelo.

Bindear el adapter con el ViewPager2

Lo único que nos queda es relacionar nuestro adapter con el ViewPager2 en la MainActivity y asignarle un Transformer como hemos visto arriba:

Como resultado tenemos la siguiente grabación:

Resultado final con Fragments

Last updated