Cómo migrar contenido de ViewPager a ViewPager2

ViewPager2 es una versión mejorada de la biblioteca ViewPager que ofrece Se mejoró la funcionalidad y aborda dificultades comunes con el uso de ViewPager. Si tu app ya usa ViewPager, consulta esta página para obtener más información sobre el tema. migrando a ViewPager2.

Si quieres usar ViewPager2 en tu app y actualmente no lo usas ViewPager, lee Cómo deslizar entre fragmentos con ViewPager2 y Cómo crear vistas deslizantes con pestañas que usan ViewPager2 para más información.

Beneficios de la migración a ViewPager2

El motivo principal para migrar es que ViewPager2 recibe para la asistencia de desarrollo y ViewPager no. Sin embargo, ViewPager2 también ofrece muchas otras ventajas específicas.

Compatibilidad con orientación vertical

ViewPager2 admite la paginación vertical y la horizontal tradicional. la paginación. Puedes habilitar la paginación vertical de un elemento ViewPager2 si configuras 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 el atributo de forma programática con el setOrientation(). .

Asistencia de derecha a izquierda

ViewPager2 admite la paginación de derecha a izquierda (RTL). Se habilitó la paginación RTL automáticamente cuando corresponda según la configuración regional, pero también puedes Habilita la paginación de derecha a izquierda para un elemento ViewPager2 configurando 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 el atributo de forma programática con el setLayoutDirections(); .

Colecciones de fragmentos modificables

ViewPager2 admite la paginación a través de una colección modificable de fragmentos, llamando notifyDatasetChanged() para actualizar la IU cuando cambie la colección subyacente.

Esto significa que tu app puede modificar dinámicamente la colección de fragmentos en y ViewPager2 mostrará correctamente la colección modificada.

DiffUtil

ViewPager2 se basa en RecyclerView, lo que significa que tiene acceso Servicio DiffUtil . Esto trae como resultado varios beneficios, pero notablemente 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 de los 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 usaste ViewPager, tuviste que extender la clase de adaptador que proporcionaron páginas nuevas al objeto. Según el caso de uso, ViewPager usó tres clases abstractas diferentes. ViewPager2 solo usa dos clases abstractas.

Por cada objeto ViewPager que conviertas en un objeto ViewPager2, haz lo siguiente: actualiza la clase de adaptador para extender la clase abstracta adecuada de la siguiente manera:

Parámetros de constructor

Las clases de adaptador basadas en fragmentos que heredan de FragmentPagerAdapter o FragmentStatePagerAdapter siempre acepta un único objeto FragmentManager. como parámetro de constructor. Cuando extiendes FragmentStateAdapter por un ViewPager2, tienes las siguientes opciones para el constructor parámetros en su lugar:

Las clases de adaptador basadas en vistas que se heredan directamente de RecyclerView.Adapter do no requieren un parámetro de constructor.

Anular métodos

Las clases de adaptador también deben anular métodos diferentes para ViewPager2. que en ViewPager:

  • En lugar de getCount(), anula getItemCount(). Aparte del nombre, este método no se modificó.
  • En lugar de getItem(), anula createFragment() en objetos de adaptador de alimentación. Asegúrate de que tu nuevo método createFragment() siempre proporciona una nueva instancia de fragmento cada vez que se llama a la función, en lugar de volver a usar instancias.

Resumen

En resumen, si deseas convertir una clase de adaptador ViewPager para usarla con ViewPager2, haz lo siguiente: debes realizar los siguientes cambios:

  1. Cambia la superclase a RecyclerView.Adapter para paginar las vistas. FragmentStateAdapter para paginar fragmentos
  2. Cambia los parámetros del constructor en clases de adaptador basadas en fragmentos.
  3. Anula getItemCount() en lugar de getCount().
  4. Anula createFragment() en lugar de getItem() en el adaptador basado 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 ahora usas un ViewPager con un objeto TabLayout para que se muestre de forma horizontal pestañas para la navegación, debes refactorizar el objeto TabLayout de 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 de tu 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 ViewPager. Si bien TabLayout usa su propio setupWithViewPager() de integración con ViewPager, se requiere un elemento TabLayoutMediator para integrarla 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 de 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 que el elemento tiene la misma orientación que el objeto ViewPager2 que contiene que la modifica. Por ejemplo, el desplazamiento no funcionaría en una vista de desplazamiento vertical dentro de una Objeto ViewPager2 orientado verticalmente

Para admitir una vista de desplazamiento dentro de un objeto ViewPager2 con la misma orientación, debes llamar requestDisallowInterceptTouchEvent() en el objeto ViewPager2 cuando esperas desplazar el elemento anidado en su lugar. El desplazamiento anidado de ViewPager2 demuestra una forma de resolver este problema con una solución diseño de wrapper personalizado.

Recursos adicionales

Para obtener más información sobre ViewPager2, consulta los siguientes recursos adicionales.

Ejemplos

Videos