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:
- Quando
ViewPagerha utilizzatoPagerAdapterper scorrere le visualizzazioni, utilizzaRecyclerView.AdapterconViewPager2. - Quando
ViewPagerha utilizzatoFragmentPagerAdapterper scorrere un numero ridotto e fisso di frammenti, utilizzaFragmentStateAdapterconViewPager2. - Quando
ViewPagerutilizzavaFragmentStatePagerAdapterper scorrere un numero elevato o sconosciuto di frammenti, utilizzaFragmentStateAdapterconViewPager2.
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
FragmentActivityo l'oggettoFragmentin cui si trova l'oggettoViewPager2. Nella maggior parte dei casi, questa è l'opzione migliore. - Un oggetto
FragmentManagere un oggettoLifecycle.
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 digetItemCount(). A parte il nome, questo metodo rimane invariato. - Anziché
getItem(), esegui l'override dicreateFragment()nelle classi di adattatori basate su frammenti. Assicurati che il nuovo metodocreateFragment()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:
- Cambia la superclasse in
RecyclerView.Adapterper scorrere le visualizzazioni o inFragmentStateAdapterper scorrere i frammenti. - Modifica i parametri del costruttore nelle classi di adattatori basati su frammenti.
- Esegui l'override di
getItemCount()anziché digetCount(). - Esegui l'override di
createFragment()anziché digetItem()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
- Turning the Page: Migrating to ViewPager2 (Android Dev Summit '19)