ViewPager2 ist eine verbesserte Version der ViewPager-Bibliothek, die erweiterte Funktionen bietet und gängige Probleme bei der Verwendung von ViewPager behebt.
Wenn Ihre App bereits ViewPager verwendet, lesen Sie diese Seite, um mehr über die Migration zu ViewPager2 zu erfahren.
Wenn du ViewPager2 in deiner App verwenden möchtest und derzeit nicht ViewPager verwendest, findest du weitere Informationen unter Mit ViewPager2 zwischen Fragmenten wechseln und Wischansichten mit Tabs mit ViewPager2 erstellen.
Vorteile der Migration zu ViewPager2
Der Hauptgrund für die Migration ist, dass ViewPager2 aktiv weiterentwickelt wird, ViewPager jedoch nicht. ViewPager2 bietet jedoch auch einige andere spezifische Vorteile.
Unterstützung für vertikale Ausrichtung
ViewPager2 unterstützt neben dem herkömmlichen horizontalen Paging auch vertikales Paging. Sie können das vertikale Paging für ein ViewPager2-Element aktivieren, indem Sie das Attribut android:orientation festlegen:
<androidx.viewpager2.widget.ViewPager2
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:orientation="vertical" />
Sie können dieses Attribut auch programmatisch mit der Methode setOrientation() festlegen.
Unterstützung für linksläufige Sprachen
ViewPager2 unterstützt das Blättern von rechts nach links (RTL). Die RTL-Seitenaufteilung wird automatisch aktiviert, wenn dies aufgrund der Spracheinstellung sinnvoll ist. Sie können die RTL-Seitenaufteilung aber auch manuell für ein ViewPager2-Element aktivieren, indem Sie das Attribut android:layoutDirection festlegen:
<androidx.viewpager2.widget.ViewPager2
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layoutDirection="rtl" />
Sie können dieses Attribut auch programmatisch mit der Methode setLayoutDirection() festlegen.
Änderbare Fragmentsammlungen
ViewPager2 unterstützt das seitenweise Durchblättern einer änderbaren Sammlung von Fragmenten und ruft notifyDatasetChanged() auf, um die Benutzeroberfläche zu aktualisieren, wenn sich die zugrunde liegende Sammlung ändert.
Das bedeutet, dass Ihre App die Fragmentsammlung zur Laufzeit dynamisch ändern kann und ViewPager2 die geänderte Sammlung korrekt anzeigt.
DiffUtil
ViewPager2 basiert auf RecyclerView und hat daher Zugriff auf die Dienstprogrammklasse DiffUtil. Das hat mehrere Vorteile. Vor allem können ViewPager2-Objekte die Animationen für Dataset-Änderungen aus der RecyclerView-Klasse nativ nutzen.
Anwendung zu ViewPager2 migrieren
So aktualisieren Sie ViewPager-Objekte in Ihrer App auf ViewPager2:
XML-Layoutdateien aktualisieren
Ersetzen Sie zuerst die ViewPager-Elemente in Ihren XML-Layoutdateien durch ViewPager2-Elemente:
<!-- 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" />
Adapterklassen aktualisieren
Wenn Sie ViewPager verwendet haben, mussten Sie die Adapterklasse erweitern, die dem Objekt neue Seiten bereitgestellt hat. Je nach Anwendungsfall hat ViewPager drei verschiedene abstrakte Klassen verwendet. ViewPager2 verwendet nur zwei abstrakte Klassen.
Aktualisieren Sie für jedes ViewPager-Objekt, das Sie in ein ViewPager2-Objekt konvertieren, die Adapterklasse, um die entsprechende abstrakte Klasse so zu erweitern:
- Wenn
ViewPagerPagerAdapterverwendet hat, um durch Ansichten zu blättern, verwenden SieRecyclerView.AdaptermitViewPager2. - Wenn
ViewPagerFragmentPagerAdapterverwendet hat, um durch eine kleine, feste Anzahl von Fragmenten zu blättern, verwenden SieFragmentStateAdaptermitViewPager2. - Wenn
ViewPagerFragmentStatePagerAdapterverwendet, um durch eine große oder unbekannte Anzahl von Fragmenten zu blättern, verwenden SieFragmentStateAdaptermitViewPager2.
Konstruktorparameter
Fragmentbasierte Adapterklassen, die von FragmentPagerAdapter oder FragmentStatePagerAdapter erben, akzeptieren immer ein einzelnes FragmentManager-Objekt als Konstruktorparameter. Wenn Sie FragmentStateAdapter für eine ViewPager2-Adapterklasse erweitern, haben Sie stattdessen die folgenden Optionen für Konstruktorparameter:
- Das
FragmentActivity- oderFragment-Objekt, in dem sich dasViewPager2-Objekt befindet. In den meisten Fällen ist dies die bessere Option. - Ein
FragmentManager-Objekt und einLifecycle-Objekt.
Ansichtsbasierte Adapterklassen, die direkt von RecyclerView.Adapter erben, benötigen keinen Konstruktorparameter.
Methoden überschreiben
Außerdem müssen Ihre Adapterklassen für ViewPager2 andere Methoden überschreiben als für ViewPager:
- Überschreiben Sie
getItemCount()anstelle vongetCount(). Abgesehen vom Namen ist diese Methode unverändert. - Überschreiben Sie in fragmentbasierten Adapterklassen
createFragment()anstelle vongetItem(). Achten Sie darauf, dass die neuecreateFragment()-Methode bei jedem Aufruf der Funktion eine neue Fragmentinstanz bereitstellt, anstatt Instanzen wiederzuverwenden.
Zusammenfassung
Zusammenfassend müssen Sie die folgenden Änderungen vornehmen, um eine ViewPager-Adapterklasse für die Verwendung mit ViewPager2 zu konvertieren:
- Ändere die Basisklasse in
RecyclerView.Adapter, um durch Ansichten zu blättern, oder inFragmentStateAdapter, um durch Fragmente zu blättern. - Ändern Sie die Konstruktorparameter in fragmentbasierten Adapterklassen.
- Überschreiben Sie
getItemCount()anstelle vongetCount(). - In fragmentbasierten Adapterklassen wird
createFragment()anstelle vongetItem()überschrieben.
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; } }
TabLayout-Schnittstellen refaktorieren
ViewPager2 führt Änderungen an der Integration von TabLayout ein. Wenn Sie derzeit ein ViewPager mit einem TabLayout-Objekt verwenden, um horizontale Tabs für die Navigation anzuzeigen, müssen Sie das TabLayout-Objekt für die Integration in ViewPager2 refaktorieren.
TabLayout wurde von ViewPager2 entkoppelt und ist jetzt als Teil von Material-Komponenten verfügbar. Wenn Sie sie verwenden möchten, müssen Sie die entsprechende Abhängigkeit in Ihre build.gradle-Datei einfügen:
Groovy
implementation "com.google.android.material:material:1.1.0-beta01"
Kotlin
implementation("com.google.android.material:material:1.1.0-beta01")
Außerdem müssen Sie die Position des TabLayout-Elements in der Hierarchie der XML-Layoutdatei ändern. Bei ViewPager wird das TabLayout-Element als untergeordnetes Element des ViewPager-Elements deklariert. Bei ViewPager2 wird das TabLayout-Element direkt über dem ViewPager2-Element auf derselben Ebene deklariert:
<!-- 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>
Schließlich müssen Sie den Code aktualisieren, mit dem das TabLayout-Objekt an das ViewPager-Objekt angehängt wird. TabLayout verwendet zwar eine eigene setupWithViewPager()-Methode für die Einbindung in ViewPager, benötigt aber eine TabLayoutMediator-Instanz für die Einbindung in ViewPager2.
Das Objekt TabLayoutMediator übernimmt auch die Aufgabe, Seitentitel für das Objekt TabLayout zu generieren. Das bedeutet, dass die Adapterklasse getPageTitle() nicht überschreiben muss:
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(); } ... }
Unterstützung verschachtelter scrollbarer Elemente
ViewPager2 unterstützt verschachtelte Scrollansichten nicht nativ, wenn die Scrollansicht dieselbe Ausrichtung wie das ViewPager2-Objekt hat, in dem sie enthalten ist. Das Scrollen würde beispielsweise bei einer vertikalen Scrollansicht in einem vertikal ausgerichteten ViewPager2-Objekt nicht funktionieren.
Wenn Sie eine Scrollansicht in einem ViewPager2-Objekt mit derselben Ausrichtung unterstützen möchten, müssen Sie requestDisallowInterceptTouchEvent() für das ViewPager2-Objekt aufrufen, wenn Sie stattdessen das verschachtelte Element scrollen möchten. Das Beispiel für verschachteltes Scrollen mit ViewPager2 zeigt eine Möglichkeit, dieses Problem mit einem vielseitigen benutzerdefinierten Wrapper-Layout zu lösen.
Zusätzliche Ressourcen
Weitere Informationen zu ViewPager2 finden Sie in den folgenden zusätzlichen Ressourcen.
Videos
- Turning the Page: Migrating to ViewPager2 (Android Dev Summit '19)