Von ViewPager zu ViewPager2 migrieren

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:

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- oder Fragment-Objekt, in dem sich das ViewPager2-Objekt befindet. In den meisten Fällen ist dies die bessere Option.
  • Ein FragmentManager-Objekt und ein Lifecycle-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 von getCount(). Abgesehen vom Namen ist diese Methode unverändert.
  • Überschreiben Sie in fragmentbasierten Adapterklassen createFragment() anstelle von getItem(). Achten Sie darauf, dass die neue createFragment()-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:

  1. Ändere die Basisklasse in RecyclerView.Adapter, um durch Ansichten zu blättern, oder in FragmentStateAdapter, um durch Fragmente zu blättern.
  2. Ändern Sie die Konstruktorparameter in fragmentbasierten Adapterklassen.
  3. Überschreiben Sie getItemCount() anstelle von getCount().
  4. In fragmentbasierten Adapterklassen wird createFragment() anstelle von getItem() ü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