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
ViewPager
używaPagerAdapter
do przechodzenia między odsłonami, użyj funkcjiRecyclerView.Adapter
zViewPager2
. - Gdy usługa
ViewPager
użyłaFragmentPagerAdapter
do przejścia do małej strony, stałą liczbę fragmentów, użyjFragmentStateAdapter
zViewPager2
. - Gdy użytkownik
ViewPager
użyłFragmentStatePagerAdapter
do przejścia do strony duża lub nieznana liczba fragmentów, użyjFragmentStateAdapter
zViewPager2
.
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 obiektFragment
, w którym Znajduje się w nim obiektViewPager2
. W większości przypadków jest to lepsza opcja. - Obiekt
FragmentManager
iLifecycle
.
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.Adapter
na potrzeby stronicowania widoków lubFragmentStateAdapter
do 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)