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:
- Se
ViewPager
ha utilizzatoPagerAdapter
per sfogliare le visualizzazioni, utilizzaRecyclerView.Adapter
conViewPager2
. - Quando
ViewPager
ha utilizzatoFragmentPagerAdapter
per sfogliare un numero ridotto e fisso di frammenti, utilizzaFragmentStateAdapter
conViewPager2
. - Quando
ViewPager
ha utilizzatoFragmentStatePagerAdapter
per sfogliare un numero elevato o sconosciuto di frammento, usaFragmentStateAdapter
conViewPager2
.
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'oggettoFragment
in cui si trova l'oggettoViewPager2
. Nella maggior parte dei casi, questa è l'opzione migliore. - Un oggetto
FragmentManager
e un oggettoLifecycle
.
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()
, sostituiscigetItemCount()
. A parte il nome, questo metodo non è cambiato. - Anziché
getItem()
, sostituiscicreateFragment()
nelle classi di adattatori basate su frammenti. Assicurati che il nuovo metodocreateFragment()
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:
- Modifica la superclasse in
RecyclerView.Adapter
per il paging nelle visualizzazioni o inFragmentStateAdapter
per il paging tra i frammenti. - Modifica i parametri del costruttore nelle classi di adattatori basate su frammenti.
- Esegui l'override di
getItemCount()
anziché digetCount()
. - Esegui l'override di
createFragment()
anziché digetItem()
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
- Esempi di ViewPager2 su GitHub
Video
- Turning the Page: Migrazione a ViewPager2 (Android Dev Summit '19)