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:
- Gdy usługa
ViewPagerużywaPagerAdapterdo przechodzenia między odsłonami, użyj funkcjiRecyclerView.AdapterzViewPager2. - Gdy usługa
ViewPagerużyłaFragmentPagerAdapterdo przejścia do małej strony, stałą liczbę fragmentów, użyjFragmentStateAdapterzViewPager2. - Gdy użytkownik
ViewPagerużyłFragmentStatePagerAdapterdo przejścia do strony duża lub nieznana liczba fragmentów, użyjFragmentStateAdapterzViewPager2.
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
FragmentActivitylub obiektFragment, w którym Znajduje się w nim obiektViewPager2. W większości przypadków jest to lepsza opcja. - Obiekt
FragmentManageriLifecycle.
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ąpgetItemCount(). 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 metodacreateFragment()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:
- Zmień klasę nadrzędną na
RecyclerView.Adapterna potrzeby stronicowania widoków lubFragmentStateAdapterdo stronicowania we fragmentach. - Zmień parametry konstruktora w klasach adaptacyjnych opartych na fragmentach.
- Zastąp
getItemCount()zamiastgetCount(). - Zastąp
createFragment()zamiastgetItem()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
- Przykłady ViewPager2 w GitHubie
Filmy
- Turning the Page: Migracja do ViewPager2 (Android Dev Summit 2019)