ViewPager2
es una versión mejorada de la biblioteca de ViewPager
que ofrece funciones mejoradas y aborda dificultades comunes con el uso de ViewPager
.
Si tu app ya usa ViewPager
, lee esta página para obtener más información sobre la migración a ViewPager2
.
Si deseas usar ViewPager2
en tu app y actualmente no usas ViewPager
, lee Cómo deslizarse entre fragmentos con ViewPager2 y Cómo crear vistas deslizantes con pestañas con ViewPager2 para obtener más información.
Beneficios de la migración a ViewPager2
El motivo principal para migrar es que ViewPager2
recibe asistencia de desarrollo activa y ViewPager
no. Sin embargo, ViewPager2
también ofrece otras ventajas específicas.
Compatibilidad con orientación vertical
ViewPager2
admite la paginación vertical, además de la paginación horizontal tradicional. Puedes habilitar la paginación vertical de un elemento ViewPager2
configurando su atributo android:orientation
:
<androidx.viewpager2.widget.ViewPager2
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:orientation="vertical" />
También puedes establecer este atributo de manera programática con el método setOrientation().
Asistencia de derecha a izquierda
ViewPager2
admite la paginación de derecha a izquierda (RTL). La paginación de RTL se habilita automáticamente cuando corresponde según la configuración regional, pero también puedes habilitarla manualmente para un elemento ViewPager2
estableciendo su atributo android:layoutDirection
:
<androidx.viewpager2.widget.ViewPager2
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layoutDirection="rtl" />
También puedes establecer este atributo de manera programática con el método setLayoutDirection().
Colecciones de fragmentos modificables
ViewPager2
admite la paginación a través de una colección modificable de fragmentos, que llama a notifyDatasetChanged()
para actualizar la IU cuando cambia la colección subyacente.
Eso significa que tu app puede modificar de forma dinámica la colección de fragmentos en el tiempo de ejecución y ViewPager2
mostrará correctamente la colección modificada.
DiffUtil
ViewPager2
se basa en RecyclerView
, lo que significa que tiene acceso a la clase de utilidad DiffUtil
. Esto tiene varios beneficios, pero, en particular, significa que los objetos ViewPager2
aprovechan de forma nativa las animaciones de cambio de conjunto de datos de la clase RecyclerView
.
Cómo migrar tu app a ViewPager2
Sigue estos pasos para actualizar los objetos ViewPager
en tu app a ViewPager2
:
Cómo actualizar los archivos de diseño XML
Primero, reemplaza los elementos ViewPager
en tus archivos de diseño XML por elementos ViewPager2
:
<!-- A ViewPager element -->
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- A ViewPager2 element -->
<androidx.viewpager2.widget.ViewPager2
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Cómo actualizar las clases de adaptador
Cuando usabas ViewPager
, debías extender la clase de adaptador que proporcionaba páginas nuevas al objeto. Según el caso de uso, ViewPager
usaba tres clases abstractas diferentes. ViewPager2
solo usa dos clases abstractas.
Por cada objeto ViewPager
que conviertas en un objeto ViewPager2
, actualiza la clase de adaptador para extender la clase abstracta adecuada de la siguiente manera:
- Cuando
ViewPager
usabaPagerAdapter
para paginar las vistas, usaRecyclerView.Adapter
conViewPager2
. - Cuando
ViewPager
usabaFragmentPagerAdapter
para paginar una cantidad pequeña y fija de fragmentos, usaFragmentStateAdapter
conViewPager2
. - Cuando
ViewPager
usabaFragmentStatePagerAdapter
para desplazarse por una cantidad de fragmentos grande o desconocida, usaFragmentStateAdapter
conViewPager2
.
Parámetros de constructor
Las clases de adaptador basadas en fragmentos que heredan de FragmentPagerAdapter
o FragmentStatePagerAdapter
siempre aceptan un solo objeto FragmentManager
como parámetro de constructor. Cuando extiendes FragmentStateAdapter
para una clase de adaptador ViewPager2
, tienes las siguientes opciones de parámetros del constructor:
- El objeto
FragmentActivity
o el objetoFragment
donde reside el objetoViewPager2
. Por lo general, esta es la mejor opción. - Un objeto
FragmentManager
y un objetoLifecycle
.
Las clases de adaptador basadas en vistas que heredan directamente de RecyclerView.Adapter
no requieren un parámetro de constructor.
Anular métodos
Las clases de adaptador también deben anular métodos diferentes para ViewPager2
de los que lo hicieron para ViewPager
:
- En lugar de
getCount()
, anulagetItemCount()
. Aparte del nombre, este método no se modifica. - En lugar de
getItem()
, anulacreateFragment()
en las clases de adaptador basadas en fragmentos. Asegúrate de que el nuevo métodocreateFragment()
siempre proporcione una nueva instancia de fragmento cada vez que se llame a la función en lugar de reutilizar instancias.
Resumen
En resumen, a fin de convertir una clase de adaptador ViewPager
para usarla con ViewPager2
, debes realizar los siguientes cambios:
- Cambia la superclase a
RecyclerView.Adapter
para paginar vistas oFragmentStateAdapter
para paginar fragmentos. - Cambia los parámetros del constructor en clases de adaptador basadas en fragmentos.
- Anula
getItemCount()
en lugar degetCount()
. - Anula
createFragment()
en lugar degetItem()
en las clases de adaptador basadas en fragmentos.
Kotlin
// A simple ViewPager adapter class for paging through fragments class ScreenSlidePagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) { override fun getCount(): Int = NUM_PAGES override fun getItem(position: Int): Fragment = ScreenSlidePageFragment() } // An equivalent ViewPager2 adapter class class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) { override fun getItemCount(): Int = NUM_PAGES override fun createFragment(position: Int): Fragment = ScreenSlidePageFragment() }
Java
// A simple ViewPager adapter class for paging through fragments public class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter { public ScreenSlidePagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return new ScreenSlidePageFragment(); } @Override public int getCount() { return NUM_PAGES; } } // An equivalent ViewPager2 adapter class private class ScreenSlidePagerAdapter extends FragmentStateAdapter { public ScreenSlidePagerAdapter(FragmentActivity fa) { super(fa); } @Override public Fragment createFragment(int position) { return new ScreenSlidePageFragment(); } @Override public int getItemCount() { return NUM_PAGES; } }
Cómo refactorizar interfaces de TabLayout
ViewPager2
trae cambios en la integración de TabLayout
. Si actualmente usas un ViewPager
con un objeto TabLayout
para mostrar pestañas horizontales para la navegación, debes refactorizar el objeto TabLayout
para la integración con ViewPager2
.
TabLayout
se desacopló de ViewPager2
y ahora está disponible como parte de componentes de Material. Esto significa que, para usarlo, debes agregar la dependencia adecuada a tu archivo build.gradle
:
Groovy
implementation "com.google.android.material:material:1.1.0-beta01"
Kotlin
implementation("com.google.android.material:material:1.1.0-beta01")
También debes cambiar la ubicación del elemento TabLayout
en la jerarquía del archivo de diseño XML. Con ViewPager
, el elemento TabLayout
se declara como un elemento secundario del elemento ViewPager
; pero con ViewPager2
, el elemento TabLayout
se declara directamente sobre el elemento ViewPager2
, en el mismo nivel:
<!-- A ViewPager element with a TabLayout -->
<androidx.viewpager.widget.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</androidx.viewpager.widget.ViewPager>
<!-- A ViewPager2 element with a TabLayout -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
Por último, debes actualizar el código que adjunta el objeto TabLayout
al objeto ViewPager
. Si bien TabLayout
usa su propio método setupWithViewPager()
para integrarse con ViewPager
, requiere una instancia de TabLayoutMediator
para integrarse con ViewPager2
.
El objeto TabLayoutMediator
también controla la tarea de generar títulos de página para el objeto TabLayout
, lo que significa que la clase del adaptador no necesita anular getPageTitle()
:
Kotlin
// Integrating TabLayout with ViewPager class CollectionDemoFragment : Fragment() { ... override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val tabLayout = view.findViewById(R.id.tab_layout) tabLayout.setupWithViewPager(viewPager) } ... } class DemoCollectionPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) { override fun getCount(): Int = 4 override fun getPageTitle(position: Int): CharSequence { return "OBJECT ${(position + 1)}" } ... } // Integrating TabLayout with ViewPager2 class CollectionDemoFragment : Fragment() { ... override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val tabLayout = view.findViewById(R.id.tab_layout) TabLayoutMediator(tabLayout, viewPager) { tab, position -> tab.text = "OBJECT ${(position + 1)}" }.attach() } ... }
Java
// Integrating TabLayout with ViewPager public class CollectionDemoFragment extends Fragment { ... @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { TabLayout tabLayout = view.findViewById(R.id.tab_layout); tabLayout.setupWithViewPager(viewPager); } ... } public class DemoCollectionPagerAdapter extends FragmentStatePagerAdapter { ... @Override public int getCount() { return 4; } @Override public CharSequence getPageTitle(int position) { return "OBJECT " + (position + 1); } ... } // Integrating TabLayout with ViewPager2 public class CollectionDemoFragment : Fragment() { ... @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { TabLayout tabLayout = view.findViewById(R.id.tab_layout); new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> tab.setText("OBJECT " + (position + 1)) ).attach(); } ... }
Cómo admitir elementos desplazables anidados
ViewPager2
no admite vistas de desplazamiento anidadas de forma nativa en los casos en los que la vista de desplazamiento tiene la misma orientación que el objeto ViewPager2
que la contiene. Por ejemplo, el desplazamiento no funcionaría en una vista de desplazamiento vertical dentro de un objeto ViewPager2
orientado verticalmente.
Para admitir una vista de desplazamiento dentro de un objeto ViewPager2
con la misma orientación, debes llamar a requestDisallowInterceptTouchEvent()
en el objeto ViewPager2
cuando esperas desplazar el elemento anidado. En el ejemplo de desplazamiento anidado de ViewPager2, se muestra una forma de resolver este problema con un diseño de wrapper personalizado y versátil.
Recursos adicionales
Para obtener más información sobre ViewPager2
, consulta los siguientes recursos adicionales.
Ejemplos
- Ejemplos de ViewPager2 en GitHub
Videos
- Pasar la página: cómo migrar a ViewPager2 (Android Dev Summit 2019)