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.
// 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()
}
// 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
:
implementation "com.google.android.material:material:1.1.0-beta01"
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()
:
// 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()
}
...
}
// 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)