ViewPager ist eine der bekanntesten und am weitesten verbreiteten Komponenten der Android Support Library. Darauf werden die einfachsten Karussells, Onboardings und Schieberegler hergestellt. Im Februar 2019 veröffentlichte das AndroidX-Entwicklungsteam ViewPager2. Schauen wir uns an, was diese Voraussetzungen waren und welche Vorteile die aktualisierte Version der Komponente hat.

ViewPager 2
Zum Zeitpunkt des Schreibens des Beitrags (Juli 2019) ist eine Beta-Version von
ViewPager2 verfügbar. Dies bedeutet, dass die unten genannten Probleme behoben und die Funktionalität verbessert und erweitert werden können. Die Entwickler versprechen, in Zukunft Unterstützung für TabLayout hinzuzufügen (obwohl dies nur mit der ersten Version möglich ist), die Leistung des Adapters zu optimieren, viele kleinere Korrekturen vorzunehmen und die Dokumentation fertigzustellen.
Integration
Die Komponente wird nicht mit Standardpaketen geliefert, sondern separat angeschlossen. Fügen Sie dazu dem Abhängigkeitsblock im Gradle-Skript Ihres Moduls die folgende Zeile hinzu:
implementation "androidx.viewpager2:viewpager2:1.0.0-beta02"
Implementierung
Beginnen wir mit den guten Nachrichten: Der Übergang von der ersten zur zweiten Version ist so einfach wie möglich und läuft auf eine Änderung der Importe hinaus. Die gute alte Syntax wurde nicht berührt: Die Methode
getCurrentItem () gibt die aktuelle Seite zurück. Mit
ViewPager2.onPageChangeCallback können
Sie den Pager-
Status abonnieren. Der Adapter wird weiterhin über
setAdapter () installiert
.
Es lohnt sich, tiefer zu graben, da deutlich wird, dass der erste und der zweite Pager nichts gemeinsam haben, außer Schnittstellen. Die Vertrautheit mit der Implementierung der setAdapter () -Methode lässt keinen Zweifel offen:
public final void setAdapter(@Nullable Adapter adapter) { mRecyclerView.setAdapter(adapter); }
Ja, ViewPager2 ist nur ein Wrapper über
RecyclerView . Dies ist einerseits ein großes Plus, andererseits verursacht es Kopfschmerzen. Mit dem Aufkommen von
PagerSnapHelper wurde es möglich,
RecyclerView als Broschüre zu
tarnen . Diese Klasse ändert die Physik der Schriftrolle. Wenn der Benutzer seinen Finger
loslässt, berechnet
PagerSnapHelper , welches Listenelement der Mittellinie der Liste am nächsten liegt, und richtet es mit einer glatten Animation genau in der Mitte aus. Wenn der Wisch scharf genug war, scrollt die Liste zum nächsten Element, andernfalls kehrt die Animation in ihren ursprünglichen Zustand zurück.
new PagerSnapHelper().attachToRecyclerView(mRecyclerView);

Stellen Sie bei Verwendung von PagerSnapHelper sicher, dass die Breite und Höhe der RecyclerView selbst sowie aller ViewHolders auf MATCH_PARENT festgelegt sind. Andernfalls ist das Verhalten von SnapHelper nicht vorhersehbar. Fehler können an völlig unerwarteten Stellen auftreten. All dies macht die Erstellung eines Karussells aus Elementen geringer Höhe ziemlich zeitaufwändig, obwohl dies möglich ist.
In Anbetracht all der oben genannten Punkte sieht das Widget im Layout folgendermaßen aus:
<androidx.viewpager2.widget.ViewPager2 android:id="@+id/main_pager" android:layout_width="match_parent" android:layout_height="match_parent" />
Im selben Paket wie
ViewPager2 finden wir auch die
ScrollEventAdapter- Klasse, mit deren Hilfe die
Syntaxkontinuität aufrechterhalten werden kann.
ScrollEventAdapter implementiert
RecyclerView.OnScrollListener und wandelt
Bildlaufereignisse in OnPageChangeCallback- Ereignisse um.
@Override public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { if (mAdapterState != STATE_IN_PROGRESS_MANUAL_DRAG && newState == RecyclerView.SCROLL_STATE_DRAGGING) { ... dispatchStateChanged(SCROLL_STATE_DRAGGING); return; } ... }
Jetzt wird
OnPageChangeCallback nicht durch eine Schnittstelle dargestellt, sondern durch eine abstrakte Klasse, mit der Sie nur die erforderlichen Methoden überschreiben können (in den meisten Fällen benötigen Sie nur o
nPageSelected (Int) , was funktioniert, wenn eine bestimmte Seite ausgewählt ist):
main_pager.registerOnPageChangeCallback( object : ViewPager2.OnPageChangeCallback() { override fun onPageSelected(position: Int) {
Eigenschaften
Bemerkenswert ist die Methode
setPageTransformer () , die
ViewPager2.PageTransformer als Parameter verwendet. Es legt einen
Rückruf für jedes Seitenauswahlereignis fest und dient dazu, eine eigene Animation für diese Seite festzulegen.
Rückruf erhält die
Ansicht der aktuellen Seite und deren Nummer als Eingabe. Das dieser Methode am nächsten liegende Analogon ist der
ItemAnimator von
RecyclerView .
In neuen Versionen der Bibliothek wurden zwei Implementierungen des Transformators hinzugefügt:
CompositePageTransformer und
MarginPageTransformer . Der erste ist für das Kombinieren von Transformatoren verantwortlich, um mehrere Transformationen gleichzeitig auf einen Pager anzuwenden, und der zweite für das Einrücken zwischen Seiten:

Darüber hinaus unterstützt das neue Widget Orientierungsänderungen: Durch einfaches Aufrufen der
setOrientation () -Methode können Sie Ihren Pager in eine vertikale Liste mit Wischen von oben nach unten verwandeln:
main_pager.setOrientation(ViewPager2.ORIENTATION_VERTICAL)
Dies geschieht erneut dank des Übergangs zur
RecyclerView : Unter der Haube wird eine Änderung der Ausrichtung des
LayoutManager aufgerufen , der für die Anzeige der Listenelemente verantwortlich ist. Es sollte beachtet werden, dass das Delegieren einer großen Anzahl von Aufgaben an andere Klassen der neuen Komponente zugute gekommen ist: Die Auflistung ist viel kompakter und lesbarer geworden.
Dies ist nicht das Ende des Spaßes. In einem Update erhielt
ViewPager2 Unterstützung für
ItemDecoration : eine
Hilfsklasse zum
Dekorieren der untergeordneten Ansicht . Dieser Mechanismus kann verwendet werden, um Trennzeichen zwischen Elementen, Rahmen und Zellenhervorhebungen zu zeichnen.
Es gibt bereits viele vorgefertigte Implementierungen von Dekorateuren, da diese seit vielen Jahren erfolgreich bei der Arbeit mit dem üblichen
RecyclerView eingesetzt werden . Alle Entwicklungen sind jetzt auf Pager anwendbar. Standardmäßig ist eine Standardimplementierung von Pager-Trennzeichen verfügbar:
main_pager.addItemDecoration( DividerItemDecoration(this, RecyclerView.HORIZONTAL) )
Zusammen mit dem nächsten Update im Mai 2019 fügte
ViewPager2 eine weitere wichtige Methode hinzu:
setOffscreenPageLimit (Int) . Er ist dafür verantwortlich, wie viele Elemente rechts und links von der Zentrale im Pager initialisiert werden. Obwohl
RecyclerView standardmäßig für das Zwischenspeichern und Anzeigen der
Ansicht verantwortlich ist, können Sie mit dieser Methode die gewünschte Anzahl der zu ladenden Elemente explizit festlegen.
Adapter
Der ideologische Nachfolger des ersten Pager-Adapters ist der
FragmentStateAdapter : Die Interaktionsschnittstellen und die Klassennamen sind nahezu identisch. Die Änderungen betrafen nur die Benennung einiger Methoden. Wenn früher die abstrakte Funktion
getItem (position) implementiert werden musste, um die gewünschte
Fragmentinstanz für die angegebene Position zurückzugeben, und diese Benennung auf zwei Arten interpretiert werden konnte, wurde diese Funktion jetzt in
createFragment (position) umbenannt . Die Gesamtzahl der Fragmente wird wie zuvor von der Funktion
getCount () bereitgestellt .
Von den wichtigen strukturellen Änderungen an der Schnittstelle sollte auch beachtet werden, dass der Adapter nun die Möglichkeit hat, den Lebenszyklus seiner Elemente zu steuern. Daher akzeptiert er zusammen mit dem
FragmentManager im Konstruktor ein
Lifecycle-Objekt , entweder eine
Aktivität oder ein
Fragment . Aus Sicherheitsgründen wurden die
Methoden saveState () und
restoreState () als endgültig deklariert und wegen Vererbung geschlossen.
Die
FragmentViewHolder- Klasse ist für das Speichern von Fragmenten in
RecyclerView verantwortlich . Die
onCreateViewHolder () -Methode von
FragmentStateAdapter ruft
FragmentViewHolder.create () auf .
static FragmentViewHolder create(ViewGroup parent) { FrameLayout container = new FrameLayout(parent.getContext()); container.setLayoutParams( new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) ); container.setId(ViewCompat.generateViewId()); container.setSaveEnabled(false); return new FragmentViewHolder(container); }
Wenn die
onBindViewHolder () -Methode
aufgerufen wird , werden der Bezeichner des Elements an der aktuellen Position und der
ViewHolder- Bezeichner
zugeordnet , um das Fragment weiter daran anzuhängen:
final long itemId = holder.getItemId(); final int viewHolderId = holder.getContainer().getId(); final Long boundItemId = itemForViewHolder(viewHolderId); ... mItemIdToViewHolder.put(itemId, viewHolderId); ensureFragment(position);
Wenn Sie einen Container aus dem
ViewHolder an die
View- Hierarchie
anhängen , wird eine
FragmentTransaction ausgeführt, die dem Container ein
Fragment hinzufügt:
void placeFragmentInViewHolder(@NonNull final FragmentViewHolder holder) { Fragment fragment = mFragments.get(holder.getItemId()); ... scheduleViewAttach(fragment, container); mFragmentManager.beginTransaction() .add(fragment, "f" + holder.getItemId()) .setMaxLifecycle(fragment, STARTED) .commitNow(); ... }
Somit ergeben sich zwei Verwendungszwecke von
ViewPager2 : durch Erben der Adapterklasse, entweder direkt von
RecyclerView.Adapter oder von
FragmentStateAdapter .
Sicherlich werden Sie eine Frage haben: Warum einen zweiten Pager mit Fragmenten und einem Adapter für diese verwenden, wenn es eine normal funktionierende erste Version gibt?
ViewPager ist bei der Arbeit mit großen dynamischen
Datenlisten alles andere als eine „Silberkugel“. Es eignet sich hervorragend zum Erstellen von Karussells mit statischen Bildern oder Bannern, aber paginierte Newsfeeds mit dem Laden von Werbepostings und Filtern bringen hart unterstützte und hässliche Monster hervor. Früher oder später werden Sie sicherlich auf den brennenden Wunsch stoßen, alles in
RecyclerView neu zu schreiben. Jetzt müssen Sie dies nicht mehr tun, da sich der Pager selbst in ihn verwandelt hat und seine leistungsstarken Funktionen für die Arbeit mit dynamischen Listen ausgeliehen hat, während sie in die übliche Syntax eingeschlossen wurden.
Das einzige, was
PagerAdapter uns bieten kann
, ist die
notifyDataSetChanged () -Methode, die den
ViewPager zwingt, alle gerenderten Listenelemente neu
zu zeichnen. Möglicherweise stellen Sie fest, dass uns niemand daran
hindert, eine Liste mit Positionen für vorhandene Elemente zu speichern und
POSITION_UNCHANGED von der Methode
getItemPosition () für diese zurückzugeben. Diese Lösung kann jedoch nicht als schön bezeichnet werden, sie ist ziemlich umständlich, außerdem ist es schwierig, diese Fälle zu erweitern, in denen sich die Elemente in der Liste ständig ändern und nicht nur nacheinander am Ende hinzugefügt werden.
FragmentStateAdapter verfügt über ein vollständiges Arsenal an
RecyclerView.Adapter- Methoden, sodass die Logik des Neuzeichnens von Elementen viel flexibler konfiguriert werden kann. Darüber hinaus können Sie zusammen mit dem
FragmentStateAdapter DiffUtil verwenden , mit dem Sie die Benachrichtigung über Änderungen fast vollständig automatisieren können.

Achtung! Damit die notify ... -Methoden ordnungsgemäß funktionieren (mit Ausnahme von notifyDataSetChanged ), sollten die Methoden getItemId (Int) und c ontainsItem (Long) neu definiert werden. Dies geschieht, weil die Standardimplementierung nur die Seitenzahl betrachtet und wenn Sie beispielsweise ein neues Element nach dem aktuellen hinzufügen, wird es nicht hinzugefügt, da getItemId unverändert bleibt. Ein Beispiel für das Überschreiben dieser beiden Methoden basierend auf einer Liste von Elementen vom Typ Int :
override fun getItemId(position: Int): Long { return items[position].toLong() } override fun containsItem(itemId: Long): Boolean { return items.contains(itemId.toInt()) }
Der Hauptgrund für das Erscheinen von
ViewPager2 ist die Zurückhaltung, das Rad neu zu erfinden. Einerseits ist das
AndroidX- Entwicklungsteam eindeutig bereit, den veralteten
ViewPager jetzt aufzugeben, und wird sicherlich nicht in die Erweiterung seiner Funktionalität investieren. Ja und warum? Schließlich weiß
RecyclerView bereits alles, was benötigt wird. Auf der anderen Seite wird das Entfernen und Beenden des Supports für eine derart weit verbreitete Komponente offensichtlich nicht zu einer Loyalität der Community führen.
Zusammenfassend:
ViewPager2 ist definitiv eine Aufmerksamkeit wert, obwohl es im Moment nicht ohne schwerwiegende Mängel ist.
Nachteile
- Feuchtigkeit und eine große Anzahl von Fehlern (entschuldbar für die Beta-Version);
- Nähe. RecyclerView ist ein privates Feld von ViewPager2 , das uns viele Möglichkeiten nimmt: Es ist unmöglich, Swipe-to- Dism oder Drag- and -Drop zu implementieren ( ItemTouchHelper stellt eine direkte Verbindung zu RecyclerView her ). Sie können ItemAnimator in keiner Weise neu definieren , nicht direkt auf LayoutManager zugreifen und RecycledViewPool verwenden . Mit der Veröffentlichung neuer Versionen der Komponente wächst jedoch die Anzahl der von RecyclerView geerbten Schnittstellenmethoden (z. B. ItemDecoration ), und wir können hoffen, die fehlenden Methoden in Zukunft hinzufügen zu können.
Vorteile
- Unterstützung für alle Vorteile von RecyclerView.Adapter : Kombinieren von Elementen unterschiedlichen Typs in einer Liste, Hinzufügen und Entfernen von Elementen direkt während des Wischens, animiertes Neuzeichnen des Inhalts der Liste beim Ändern;
- Unterstützung für das gesamte Spektrum der Benachrichtigungsmethoden und automatische Berechnung von Änderungen mit DiffUtil ;
- Einfacher Übergang aufgrund der Kontinuität der Syntax;
- Unterstützung für vertikale und horizontale Ausrichtung "out of the box";
- RTL- Unterstützung;
- Support ItemDecorator ;
- Unterstützung für das Scrollen von Software durch fakeScrollBy () ;
- Möglichkeit, die Anzahl der geladenen Elemente manuell festzulegen;
- Die Möglichkeit, eine der vorgefertigten Open-Source-Lösungen zu verwenden, um den Boilerplate-Code zu reduzieren, was beim Schreiben von benutzerdefiniertem RecyclerView.Adapter unvermeidlich ist. Zum Beispiel EasyAdapter .
Zusammenfassend möchte ich sagen, dass
ViewPager2 wirklich einen
genaueren Blick wert ist. Dies ist eine vielversprechende, erweiterbare und funktionale Lösung. Und obwohl es zu früh ist, ein
neues Widget in der Produktion zu starten, kann man mit Sicherheit sagen, dass es nach einer vollständigen Veröffentlichung seinen Vorfahren vollständig ersetzen kann und sollte.
Für diejenigen, die es wagen und entschlossen sind, die der Artikel zum Experimentieren inspiriert hat, erschien
PagerSnapHelper in der 28. Version der
Support-Bibliothek. Dies bedeutet, dass Sie es zusammen mit Ihrem
RecyclerView verwenden können ,
indem Sie ViewPager2 selbst erstellen .
Die Beispieloperation von
ViewPager2 und
FragmentStateAdapter .
Offizielle Versionshinweise ViewPager2