ViewPager2
ist eine verbesserte Version der ViewPager
-Bibliothek, die erweiterte Funktionen bietet und häufige Probleme bei der Verwendung von ViewPager
behebt.
Wenn Ihre Anwendung bereits ViewPager
verwendet, finden Sie auf dieser Seite weitere Informationen zur Migration zu ViewPager2
.
Wenn Sie ViewPager2
in Ihrer App verwenden möchten und ViewPager
derzeit nicht nutzen, finden Sie 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
aktiven Entwicklungssupport erhält und ViewPager
nicht. Allerdings bietet ViewPager2
auch einige andere spezifische Vorteile.
Unterstützung für vertikale Ausrichtung
ViewPager2
unterstützt zusätzlich zum herkömmlichen horizontalen Paging vertikales Paging. Sie können vertikales Paging für ein ViewPager2
-Element aktivieren, indem Sie das entsprechende android:orientation
-Attribut festlegen:
<androidx.viewpager2.widget.ViewPager2
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:orientation="vertical" />
Mit der Methode setOrientation() kann dieses Attribut auch programmatisch festgelegt werden.
Unterstützung von rechts nach links
ViewPager2
unterstützt die Seitenausrichtung von rechts nach links (RTL). Je nach Sprache wird das RTL-Seitening automatisch aktiviert. Sie können es aber auch manuell für ein ViewPager2
-Element aktivieren. Dazu legen Sie das entsprechende android:layoutDirection
-Attribut fest:
<androidx.viewpager2.widget.ViewPager2
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layoutDirection="rtl" />
Mit der Methode setLayoutDirection() lässt sich dieses Attribut auch programmatisch festlegen.
Bearbeitbare Fragmentsammlungen
ViewPager2
unterstützt das Durchblättern einer änderbaren Sammlung von Fragmenten. Dabei wird notifyDatasetChanged()
aufgerufen, um die UI zu aktualisieren, wenn sich die zugrunde liegende Sammlung ändert.
Das bedeutet, dass Ihre Anwendung 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
. Dies bietet mehrere Vorteile, vor allem bedeutet es aber, dass ViewPager2
-Objekte die Animationen von Dataset-Änderungen aus der RecyclerView
-Klasse nativ nutzen.
App zu ViewPager2 migrieren
So aktualisierst du ViewPager
-Objekte in deiner 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
Bei der Verwendung von ViewPager
mussten Sie die Adapterklasse erweitern, über die neue Seiten für das Objekt bereitgestellt wurden. Je nach Anwendungsfall wurden bei 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 zu erweitern:
- Wenn
ViewPager
mitPagerAdapter
durch Ansichten blättern konnte, verwendeRecyclerView.Adapter
mitViewPager2
. - Wenn
ViewPager
mitFragmentPagerAdapter
durch eine kleine, feste Anzahl von Fragmenten blättern konnte, verwendeFragmentStateAdapter
mitViewPager2
. - Wenn
ViewPager
mitFragmentStatePagerAdapter
durch eine große oder unbekannte Anzahl an Fragmenten blättern konnte, verwendeFragmentStateAdapter
mitViewPager2
.
Konstruktorparameter
Fragmentbasierte Adapterklassen, die FragmentPagerAdapter
oder FragmentStatePagerAdapter
übernehmen, akzeptieren immer ein einzelnes FragmentManager
-Objekt als Konstruktorparameter. Wenn Sie FragmentStateAdapter
für eine ViewPager2
-Adapterklasse erweitern, stehen stattdessen die folgenden Optionen für Konstruktorparameter zur Verfügung:
- 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.
Für ansichtsbasierte Adapterklassen, die direkt von RecyclerView.Adapter
übernommen werden, ist kein Konstruktorparameter erforderlich.
Überschreibungsmethoden
Außerdem müssen Ihre Adapterklassen für ViewPager2
andere Methoden überschreiben als für ViewPager
:
- Überschreiben Sie
getItemCount()
anstelle vongetCount()
. Abgesehen vom Namen bleibt diese Methode unverändert. - Überschreiben Sie
createFragment()
in fragmentbasierten Adapterklassen anstelle vongetItem()
. Achten Sie darauf, dass die neue MethodecreateFragment()
bei jedem Aufruf der Funktion immer eine neue Fragmentinstanz bereitstellt, anstatt Instanzen zu wiederverwenden.
Zusammenfassung
Zusammenfassend lässt sich sagen, dass Sie folgende Änderungen vornehmen, um eine ViewPager
-Adapterklasse zur Verwendung mit ViewPager2
zu konvertieren:
- Ändern Sie die Basisklasse in
RecyclerView.Adapter
, um durch Ansichten blättern zu können, oder inFragmentStateAdapter
, um durch Fragmente zu blättern. - Ändern Sie die Konstruktorparameter in fragmentbasierten Adapterklassen.
getItemCount()
anstelle vongetCount()
überschreiben.- Überschreiben Sie
createFragment()
anstelle vongetItem()
in fragmentbasierten Adapterklassen.
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
Mit ViewPager2
wurden Änderungen an der TabLayout
-Integration vorgenommen. 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 der Material Components verfügbar. Das bedeutet, dass Sie die entsprechende Abhängigkeit zu Ihrer build.gradle
-Datei hinzufügen müssen, um sie verwenden zu können:
Groovig
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 Ihrer XML-Layoutdatei ändern. Bei ViewPager
wird das TabLayout
-Element als untergeordnetes Element des ViewPager
-Elements deklariert. Mit ViewPager2
wird das TabLayout
-Element jedoch auf derselben Ebene direkt über dem ViewPager2
-Element 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 für die Einbindung in ViewPager
eine eigene Methode setupWithViewPager()
. Für die Einbindung in ViewPager2
ist jedoch eine TabLayoutMediator
-Instanz erforderlich.
Das TabLayoutMediator
-Objekt übernimmt auch die Aufgabe, Seitentitel für das TabLayout
-Objekt 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(); } ... }
Verschachtelte scrollbare Elemente unterstützen
ViewPager2
unterstützt nativ keine verschachtelten Scroll-Ansichten, wenn die Scroll-Ansicht dieselbe Ausrichtung hat wie das ViewPager2
-Objekt, das sie enthält. Das Scrollen funktioniert beispielsweise nicht bei einer vertikalen Scrollansicht in einem vertikal ausgerichteten ViewPager2
-Objekt.
Um eine Scroll-Ansicht innerhalb eines ViewPager2
-Objekts mit derselben Ausrichtung zu ermöglichen, müssen Sie requestDisallowInterceptTouchEvent()
für das ViewPager2
-Objekt aufrufen, wenn Sie stattdessen durch das verschachtelte Element scrollen möchten. Das verschachtelte Scrolling-Beispiel von ViewPager2 zeigt eine Möglichkeit, dieses Problem mit einem vielseitigen benutzerdefinierten Wrapper-Layout zu lösen.
Weitere Informationen
Weitere Informationen zu ViewPager2
finden Sie in den folgenden zusätzlichen Ressourcen.
Produktproben
- ViewPager2-Beispiele auf GitHub
Videos
- Turning the Page: Migrating to ViewPager2 (Android Dev Summit '19)