Migracja z ViewPager do ViewPager2

ViewPager2 to ulepszona wersja biblioteki ViewPager, która oferuje rozszerzona funkcjonalność i rozwiązuje typowe problemy z korzystaniem z ViewPager. Jeśli Twoja aplikacja używa już ViewPager, na tej stronie znajdziesz więcej informacji – przenoszonym do ViewPager2.

Jeśli chcesz używać w aplikacji ViewPager2, a obecnie jej nie używasz ViewPager, przeczytaj Przesuwaj między fragmentami za pomocą ViewPager2 i Tworzenie widoków przesuwanych za pomocą: za pomocą ViewPager2. i informacjami o nich.

Zalety migracji do ViewPager2

Głównym powodem migracji jest to, że usługa ViewPager2 otrzymuje aktywną , a ViewPager nie. Jednak ViewPager2 oferuje też i inne wyjątkowe zalety.

Obsługa orientacji pionowej

Oprócz tradycyjnej strony w poziomie funkcja ViewPager2 obsługuje podział strony w pionie stronicowanie. Możesz włączyć podział na strony w pionie dla elementu ViewPager2, ustawiając jego Atrybut android:orientation:

<androidx.viewpager2.widget.ViewPager2
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"
    android:orientation="vertical" />

Możesz też ustawić ten atrybut automatycznie za pomocą setOrientation(), .

Obsługa tekstu od prawej do lewej

ViewPager2 obsługuje stronicowanie od prawej do lewej (RTL). stronicowanie RTL jest włączone automatycznie w razie potrzeby na podstawie języka, ale możesz też ręcznie włączyć stronicowanie od prawej do lewej dla elementu ViewPager2, ustawiając jego Atrybut android:layoutDirection:

<androidx.viewpager2.widget.ViewPager2
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"
    android:layoutDirection="rtl" />

Możesz też ustawić ten atrybut automatycznie za pomocą setLayoutDirection(), .

Modyfikowalne kolekcje fragmentów

ViewPager2 obsługuje stronicowanie za pomocą możliwego do modyfikowania zbioru fragmentów, połączenia notifyDatasetChanged(). , aby zaktualizować UI, gdy zmieni się bazowa kolekcja.

Oznacza to, że aplikacja może dynamicznie modyfikować zbieranie fragmentów na stronie w środowisku wykonawczym, a ViewPager2 prawidłowo wyświetli zmodyfikowaną kolekcję.

Diffutil

ViewPager2 korzysta z platformy RecyclerView, co oznacza, że ma dostęp do narzędzie DiffUtil zajęcia. przynosi to liczne korzyści, a przede wszystkim oznacza, że ViewPager2 obiekty natywnie korzystają z animacji zmian w zbiorze danych z zajęć RecyclerView.

Migracja aplikacji do ViewPager2

Aby zaktualizować obiekty typu ViewPager w aplikacji do stanu ViewPager2, wykonaj te czynności:

Zaktualizuj pliki układu XML

Najpierw zamień elementy ViewPager w plikach układu XML na Elementy 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" />

Aktualizowanie klas adaptera

Korzystając z interfejsu ViewPager, trzeba było rozszerzyć klasę adaptera, która dostarcza nowe strony do obiektu. W zależności od przypadku użycia używana jest funkcja ViewPager trzy różne klasy abstrakcyjne. Funkcja ViewPager2 używa tylko 2 klas abstrakcyjnych.

W przypadku każdego konwertowanego obiektu ViewPager na obiekt ViewPager2 zaktualizuj klasę adaptera, aby rozszerzyć odpowiednią klasę abstrakcyjną w ten sposób:

Parametry konstruktora

Klasy adapterów oparte na fragmentach dziedziczące dane z klasy FragmentPagerAdapter lub FragmentStatePagerAdapter zawsze akceptuje pojedynczy obiekt FragmentManager jako parametru konstruktora. Jeśli przedłużysz FragmentStateAdapter o ViewPager2, dostępne są następujące opcje konstruktora :

  • Obiekt FragmentActivity lub obiekt Fragment, w którym Znajduje się w nim obiekt ViewPager2. W większości przypadków jest to lepsza opcja.
  • Obiekt FragmentManager i Lifecycle.

Klasy adapterów oparte na widoku, które dziedziczą bezpośrednio z RecyclerView.Adapter. nie wymagają parametru konstruktora.

Metody zastępowania

Klasy adaptera muszą też zastąpić różne metody obiektu ViewPager2 niż w przypadku ViewPager:

  • Zamiast getCount() zastąp getItemCount(). Oprócz nazwy ta metoda pozostaje bez zmian.
  • Zamiast getItem() zastąp wartość createFragment() opartą na fragmentach klas adapterów. Zadbaj o to, aby nowa metoda createFragment() zawsze była dostępna dostarcza nowe wystąpienie fragmentu przy każdym wywołaniu funkcji zamiast przez ponowne wykorzystanie instancji.
.

Podsumowanie

Podsumowując, aby przekonwertować klasę adaptera ViewPager do użytku z ViewPager2, musisz wprowadzić następujące zmiany:

  1. Zmień klasę nadrzędną na RecyclerView.Adapter na potrzeby stronicowania widoków lub FragmentStateAdapter do stronicowania we fragmentach.
  2. Zmień parametry konstruktora w klasach adaptacyjnych opartych na fragmentach.
  3. Zastąp getItemCount() zamiast getCount().
  4. Zastąp createFragment() zamiast getItem() w adapterze opartym na fragmentach zajęcia.

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;
    }
}

Refaktoryzacja interfejsów TabLayout

ViewPager2 wprowadza zmiany w integracji z TabLayout. Jeśli obecnie używasz ViewPager z obiektem TabLayout do wyświetlania w poziomie kart nawigacji, musisz dokonać refaktoryzacji obiektu TabLayout dla integrację z ViewPager2.

Usługa TabLayout została odłączona od usługi ViewPager2 i jest teraz dostępna w ramach Komponenty materiałowe. Oznacza to, że aby jej użyć, musisz dodać do niej odpowiednią zależność od pliku build.gradle:

Odlotowe

implementation "com.google.android.material:material:1.1.0-beta01"

Kotlin

implementation("com.google.android.material:material:1.1.0-beta01")

Musisz też zmienić lokalizację elementu TabLayout w hierarchii pliku układu XML. W przypadku funkcji ViewPager element TabLayout jest deklarowany jako element podrzędny elementu ViewPager; ale dzięki ViewPager2, elementowi TabLayout jest zadeklarowana bezpośrednio nad elementem ViewPager2 na tym samym poziomie:

<!-- 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>

Na koniec musisz zaktualizować kod, który dołącza obiekt TabLayout do ViewPager obiekt. TabLayout korzysta z własnego setupWithViewPager() do integracji z ViewPager, wymaga TabLayoutMediator instancję do integracji z ViewPager2.

Obiekt TabLayoutMediator służy też do generowania tytułów stron. dla obiektu TabLayout, co oznacza, że klasa adaptera nie musi zastąp 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();
    }
    ...
}

Obsługa zagnieżdżonych elementów przewijanych.

ViewPager2 nie obsługuje natywnie zagnieżdżonych widoków przewijania w przypadkach, gdy widok przewijania ma tę samą orientację co obiekt ViewPager2, który zawiera . Na przykład przewijanie nie zadziała w przypadku widoku przewijania w pionie w zorientowany pionowo obiekt ViewPager2.

Aby obsługiwać widok przewijania w obiekcie ViewPager2 o tej samej orientacji, Musisz zadzwonić requestDisallowInterceptTouchEvent() na obiekcie ViewPager2, gdy spodziewany jest przewinięcie umieszczonego elementu. Zagnieżdżone przewijanie ViewPager2 przykład przedstawia jeden sposób rozwiązania tego problemu przy użyciu uniwersalnego niestandardowego układu kodu.

Dodatkowe materiały

Aby dowiedzieć się więcej o usłudze ViewPager2, zapoznaj się z tymi dodatkowymi materiałami.

Próbki

Filmy