Esegui la migrazione da ViewPager a ViewPager2

ViewPager2 è una versione migliorata della libreria ViewPager che offre funzionalità avanzate e risolve le difficoltà comuni nell'utilizzo di ViewPager. Se la tua app utilizza già ViewPager, leggi questa pagina per scoprire di più sulla migrazione a ViewPager2.

Se vuoi utilizzare ViewPager2 nella tua app e al momento non utilizzi ViewPager, leggi Scorri tra i frammenti utilizzando ViewPager2 e Crea visualizzazioni a scorrimento con schede utilizzando ViewPager2 per ulteriori informazioni.

Vantaggi della migrazione a ViewPager2

Il motivo principale per eseguire la migrazione è che ViewPager2 riceve un supporto attivo per lo sviluppo, mentre ViewPager no. Tuttavia, ViewPager2 offre anche diversi altri vantaggi specifici.

Supporto dell'orientamento verticale

ViewPager2 supporta l'impaginazione verticale oltre alla tradizionale impaginazione orizzontale. Puoi attivare l'impaginazione verticale per un elemento ViewPager2 impostando il relativo attributo android:orientation:

<androidx.viewpager2.widget.ViewPager2
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"
    android:orientation="vertical" />

Puoi anche impostare questo attributo in modo programmatico utilizzando il metodo setOrientation().

Supporto per la scrittura da destra a sinistra

ViewPager2 supporta l'impaginazione da destra a sinistra (RTL). La paginazione RTL viene attivata automaticamente, se opportuno, in base alle impostazioni internazionali, ma puoi anche attivarla manualmente per un elemento ViewPager2 impostando il relativo attributo android:layoutDirection:

<androidx.viewpager2.widget.ViewPager2
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"
    android:layoutDirection="rtl" />

Puoi anche impostare questo attributo in modo programmatico utilizzando il metodo setLayoutDirection().

Raccolte di frammenti modificabili

ViewPager2 supporta l'impaginazione di una raccolta modificabile di frammenti, chiamando notifyDatasetChanged() per aggiornare l'interfaccia utente quando la raccolta sottostante cambia.

Ciò significa che la tua app può modificare dinamicamente la raccolta di frammenti in fase di runtime e ViewPager2 mostrerà correttamente la raccolta modificata.

DiffUtil

ViewPager2 è basato su RecyclerView, il che significa che ha accesso alla classe di utilità DiffUtil. Ciò comporta diversi vantaggi, ma soprattutto significa che gli oggetti ViewPager2 sfruttano in modo nativo le animazioni di modifica del set di dati della classe RecyclerView.

Esegui la migrazione della tua app a ViewPager2

Segui questi passaggi per aggiornare gli oggetti ViewPager nella tua app a ViewPager2:

Aggiorna i file di layout XML

Innanzitutto, sostituisci gli elementi ViewPager nei file di layout XML con gli elementi 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" />

Aggiorna le classi adattatore

Quando utilizzavi ViewPager, dovevi estendere la classe dell'adattatore che forniva nuove pagine all'oggetto. A seconda del caso d'uso, ViewPager ha utilizzato tre diverse classi astratte. ViewPager2 utilizza solo due classi astratte.

Per ogni oggetto ViewPager che stai convertendo in un oggetto ViewPager2, aggiorna la classe dell'adattatore per estendere la classe astratta appropriata come segue:

Parametri del costruttore

Le classi di adattatori basate su frammenti che ereditano da FragmentPagerAdapter o FragmentStatePagerAdapter accettano sempre un singolo oggetto FragmentManager come parametro del costruttore. Quando estendi FragmentStateAdapter per una classe di adattatore ViewPager2, hai invece le seguenti opzioni per i parametri del costruttore:

  • L'oggetto FragmentActivity o l'oggetto Fragment in cui si trova l'oggetto ViewPager2. Nella maggior parte dei casi, questa è l'opzione migliore.
  • Un oggetto FragmentManager e un oggetto Lifecycle.

Le classi di adattatori basate su visualizzazioni che ereditano direttamente da RecyclerView.Adapter non richiedono un parametro del costruttore.

Metodi di override

Le classi dell'adattatore devono anche eseguire l'override di metodi diversi per ViewPager2 rispetto a ViewPager:

  • Anziché getCount(), esegui l'override di getItemCount(). A parte il nome, questo metodo rimane invariato.
  • Anziché getItem(), esegui l'override di createFragment() nelle classi di adattatori basate su frammenti. Assicurati che il nuovo metodo createFragment() fornisca sempre una nuova istanza di frammento ogni volta che viene chiamata la funzione, anziché riutilizzare le istanze.

Riepilogo

In sintesi, per convertire una classe di adattatore ViewPager da utilizzare con ViewPager2, devi apportare le seguenti modifiche:

  1. Cambia la superclasse in RecyclerView.Adapter per scorrere le visualizzazioni o in FragmentStateAdapter per scorrere i frammenti.
  2. Modifica i parametri del costruttore nelle classi di adattatori basati su frammenti.
  3. Esegui l'override di getItemCount() anziché di getCount().
  4. Esegui l'override di createFragment() anziché di getItem() nelle classi di adattatori basate su frammenti.

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;
    }
}

Rifattorizza le interfacce TabLayout

ViewPager2 introduce modifiche all'integrazione di TabLayout. Se al momento utilizzi un ViewPager con un oggetto TabLayout per visualizzare le schede orizzontali per la navigazione, devi rifattorizzare l'oggetto TabLayout per l'integrazione con ViewPager2.

TabLayout è stato disaccoppiato da ViewPager2 ed è ora disponibile come parte dei componenti Material. Ciò significa che, per utilizzarla, devi aggiungere la dipendenza appropriata al file build.gradle:

Alla moda

implementation "com.google.android.material:material:1.1.0-beta01"

Kotlin

implementation("com.google.android.material:material:1.1.0-beta01")

Devi anche modificare la posizione dell'elemento TabLayout nella gerarchia del file di layout XML. Con ViewPager, l'elemento TabLayout viene dichiarato come elemento secondario dell'elemento ViewPager; con ViewPager2, invece, l'elemento TabLayout viene dichiarato direttamente sopra l'elemento ViewPager2, allo stesso livello:

<!-- 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>

Infine, devi aggiornare il codice che collega l'oggetto TabLayout all'oggetto ViewPager. Sebbene TabLayout utilizzi il proprio metodo setupWithViewPager() per l'integrazione con ViewPager, richiede un'istanza TabLayoutMediator per l'integrazione con ViewPager2.

L'oggetto TabLayoutMediator gestisce anche l'attività di generazione dei titoli delle pagine per l'oggetto TabLayout, il che significa che la classe dell'adattatore non deve eseguire l'override di 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();
    }
    ...
}

Supporto di elementi scorrevoli nidificati

ViewPager2 non supporta in modo nativo le visualizzazioni di scorrimento nidificate nei casi in cui la visualizzazione di scorrimento ha lo stesso orientamento dell'oggetto ViewPager2 che la contiene. Ad esempio, lo scorrimento non funzionerebbe per una visualizzazione a scorrimento verticale all'interno di un oggetto ViewPager2 con orientamento verticale.

Per supportare una visualizzazione a scorrimento all'interno di un oggetto ViewPager2 con lo stesso orientamento, devi chiamare requestDisallowInterceptTouchEvent() sull'oggetto ViewPager2 quando prevedi di scorrere l'elemento nidificato. L'esempio di scorrimento nidificato di ViewPager2 mostra un modo per risolvere questo problema con un layout wrapper personalizzato versatile.

Risorse aggiuntive

Per scoprire di più su ViewPager2, consulta le seguenti risorse aggiuntive.

Video