Esegui la migrazione da ViewPager a ViewPager2

ViewPager2 è una versione migliorata della libreria ViewPager che offre funzionalità avanzate e risolve i problemi più 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 Scorrimento tra frammenti utilizzando ViewPager2 e Creare visualizzazioni a scorrimento con schede utilizzando ViewPager2 per ulteriori informazioni.

Vantaggi della migrazione a ViewPager2

Il motivo principale per cui eseguire la migrazione è che ViewPager2 riceve supporto attivo per lo sviluppo, al contrario di ViewPager. Tuttavia, ViewPager2 offre anche diversi altri vantaggi specifici.

Supporto per l'orientamento verticale

ViewPager2 supporta il paging verticale oltre al tradizionale paging orizzontale. Puoi attivare il paging 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 da destra a sinistra

ViewPager2 supporta il paging da destra a sinistra (RTL). Il paging RTL viene attivato automaticamente, ove appropriato in base alle impostazioni internazionali, ma puoi anche attivare manualmente il paging RTL 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 il paging tramite una raccolta modificabile di frammenti, chiamando notifyDatasetChanged() per aggiornare l'interfaccia utente quando la raccolta sottostante viene modificata.

Ciò significa che la tua app può modificare in modo dinamico 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. Questo comporta diversi vantaggi, ma in particolare 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 ViewPager oggetti 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 classi di adattatori

Quando utilizzavi ViewPager, hai dovuto estendere la classe dell'adattatore che forniva nuove pagine all'oggetto. A seconda del caso d'uso, ViewPager ha usato 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 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 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 sulle visualizzazioni che ereditano direttamente da RecyclerView.Adapter non richiedono un parametro del costruttore.

Metodi di override

Le classi di adattatori devono inoltre eseguire l'override di metodi diversi per ViewPager2 rispetto a quelli per ViewPager:

  • Anziché getCount(), sostituisci getItemCount(). A parte il nome, questo metodo non è cambiato.
  • Anziché getItem(), sostituisci 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 richiamata la funzione, anziché riutilizzare le istanze.

Riepilogo

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

  1. Modifica la superclasse in RecyclerView.Adapter per il paging nelle visualizzazioni o in FragmentStateAdapter per il paging tra i frammenti.
  2. Modifica i parametri del costruttore nelle classi di adattatori basate su frammenti.
  3. Esegui l'override di getItemCount() anziché di getCount().
  4. Esegui l'override di createFragment() anziché di getItem() nelle classi di adattatori basati 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;
    }
}

Refactoring delle interfacce TabLayout

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

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

Trendy

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

Kotlin

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

Devi inoltre modificare la posizione dell'elemento TabLayout nella gerarchia del file di layout XML. Con ViewPager, l'elemento TabLayout viene dichiarato come secondario dell'elemento ViewPager; con ViewPager2, l'elemento TabLayout viene dichiarato direttamente sopra l'elemento ViewPager2, sullo 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 di pagina per l'oggetto TabLayout, il che significa che la classe 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 questa ha lo stesso orientamento dell'oggetto ViewPager2 che la contiene. Ad esempio, lo scorrimento non funzionerebbe per una visualizzazione con scorrimento verticale all'interno di un oggetto ViewPager2 orientato verticalmente.

Per supportare una visualizzazione a scorrimento all'interno di un oggetto ViewPager2 con lo stesso orientamento, devi chiamare requestDisallowInterceptTouchEvent() nell'oggetto ViewPager2 se 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.

Samples

Video