ViewPager2
— это улучшенная версия библиотеки ViewPager
, которая предлагает расширенные функциональные возможности и устраняет распространенные трудности при использовании ViewPager
. Если ваше приложение уже использует ViewPager
, прочитайте эту страницу, чтобы узнать больше о переходе на ViewPager2
.
Если вы хотите использовать ViewPager2
в своем приложении и в настоящее время не используете ViewPager
, прочтите «Скольжение между фрагментами с помощью ViewPager2» и «Создание представлений пролистывания с вкладками с помощью ViewPager2» для получения дополнительной информации.
Преимущества перехода на ViewPager2
Основная причина миграции заключается в том, что ViewPager2
получает активную поддержку разработки, а ViewPager
— нет. Однако ViewPager2
также предлагает ряд других конкретных преимуществ.
Поддержка вертикальной ориентации
ViewPager2
поддерживает вертикальное разбиение на страницы в дополнение к традиционному горизонтальному разбиению на страницы. Вы можете включить вертикальное разбиение на страницы для элемента ViewPager2
, установив его атрибут android:orientation
:
<androidx.viewpager2.widget.ViewPager2
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:orientation="vertical" />
Вы также можете установить этот атрибут программно, используя метод setOrientation() .
Поддержка справа налево
ViewPager2
поддерживает разбивку по страницам справа налево (RTL). Разбиение по страницам RTL включается автоматически, где это необходимо, в зависимости от языкового стандарта, но вы также можете вручную включить разбиение по страницам RTL для элемента ViewPager2
, установив его атрибут android:layoutDirection
:
<androidx.viewpager2.widget.ViewPager2
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layoutDirection="rtl" />
Вы также можете установить этот атрибут программно, используя метод setLayoutDirection() .
Модифицируемые коллекции фрагментов
ViewPager2
поддерживает постраничную перелистывание изменяемой коллекции фрагментов, вызывая notifyDatasetChanged()
для обновления пользовательского интерфейса при изменении базовой коллекции.
Это означает, что ваше приложение может динамически изменять коллекцию фрагментов во время выполнения, и ViewPager2
будет правильно отображать измененную коллекцию.
DiffUtil
ViewPager2
построен на основе RecyclerView
, что означает, что он имеет доступ к служебному классу DiffUtil
. Это дает несколько преимуществ, но, прежде всего, это означает, что объекты ViewPager2
изначально используют преимущества анимации изменения набора данных из класса RecyclerView
.
Перенесите свое приложение на ViewPager2.
Выполните следующие действия, чтобы обновить объекты ViewPager
в вашем приложении до ViewPager2
:
Обновить файлы макета XML
Сначала замените элементы ViewPager
в файлах макета XML элементами 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" />
Обновить классы адаптеров
При использовании ViewPager
вам приходилось расширять класс адаптера, который предоставлял объекту новые страницы. В зависимости от варианта использования ViewPager
использовал три разных абстрактных класса. ViewPager2
использует только два абстрактных класса.
Для каждого объекта ViewPager
, который вы преобразуете в объект ViewPager2
, обновите класс адаптера, чтобы расширить соответствующий абстрактный класс следующим образом:
- Когда
ViewPager
использовалPagerAdapter
для постраничного просмотра представлений, используйтеRecyclerView.Adapter
сViewPager2
. - Когда
ViewPager
использовалFragmentPagerAdapter
для пролистывания небольшого фиксированного количества фрагментов, используйтеFragmentStateAdapter
сViewPager2
. - Когда
ViewPager
использовалFragmentStatePagerAdapter
для пролистывания большого или неизвестного количества фрагментов, используйтеFragmentStateAdapter
сViewPager2
.
Параметры конструктора
Классы адаптеров на основе фрагментов, наследуемые от FragmentPagerAdapter
или FragmentStatePagerAdapter
всегда принимают один объект FragmentManager
в качестве параметра конструктора. Когда вы расширяете FragmentStateAdapter
для класса адаптера ViewPager2
, вместо этого у вас есть следующие параметры для параметров конструктора:
- Объект
FragmentActivity
или объектFragment
, в котором находится объектViewPager2
. В большинстве случаев это лучший вариант. - Объект
FragmentManager
и объектLifecycle
.
Классы адаптеров на основе представлений, наследуемые непосредственно от RecyclerView.Adapter
, не требуют параметра конструктора.
Переопределить методы
Классы адаптеров также должны переопределить методы для ViewPager2
отличные от методов для ViewPager
:
- Вместо
getCount()
переопределитеgetItemCount()
. Помимо названия, этот метод не изменился. - Вместо
getItem()
переопределитеcreateFragment()
в классах адаптеров на основе фрагментов. Убедитесь, что ваш новый методcreateFragment()
всегда предоставляет новый экземпляр фрагмента каждый раз, когда вызывается функция, а не повторно использует экземпляры.
Краткое содержание
Таким образом, чтобы преобразовать класс адаптера ViewPager
для использования с ViewPager2
, необходимо внести следующие изменения:
- Измените суперкласс на
RecyclerView.Adapter
для постраничного просмотра представлений илиFragmentStateAdapter
для постраничного просмотра фрагментов. - Измените параметры конструктора в классах адаптеров на основе фрагментов.
- Переопределите
getItemCount()
вместоgetCount()
. - Переопределите
createFragment()
вместоgetItem()
в классах адаптеров на основе фрагментов.
Котлин
// 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; } }
Рефакторинг интерфейсов TabLayout.
ViewPager2
вносит изменения в интеграцию TabLayout
. Если вы в настоящее время используете ViewPager
с объектом TabLayout
для отображения горизонтальных вкладок для навигации, вам необходимо выполнить рефакторинг объекта TabLayout
для интеграции с ViewPager2
.
TabLayout
отделен от ViewPager2
и теперь доступен как часть компонентов Material . Это означает, что для его использования вам необходимо добавить соответствующую зависимость в ваш файл build.gradle
:
классный
implementation "com.google.android.material:material:1.1.0-beta01"
Котлин
implementation("com.google.android.material:material:1.1.0-beta01")
Вам также необходимо изменить расположение элемента TabLayout
в иерархии файла макета XML. При использовании ViewPager
элемент TabLayout
объявляется как дочерний элемент ViewPager
; но в случае с ViewPager2
элемент TabLayout
объявляется непосредственно над элементом ViewPager2
на том же уровне:
<!-- 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>
Наконец, необходимо обновить код, который присоединяет объект TabLayout
к объекту ViewPager
. Хотя TabLayout
использует собственный метод setupWithViewPager()
для интеграции с ViewPager
, для интеграции с ViewPager2
требуется экземпляр TabLayoutMediator
.
Объект TabLayoutMediator
также выполняет задачу создания заголовков страниц для объекта TabLayout
, а это означает, что классу адаптера не нужно переопределять 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(); } ... }
Поддержка вложенных прокручиваемых элементов.
ViewPager2
изначально не поддерживает вложенные представления прокрутки в тех случаях, когда представление прокрутки имеет ту же ориентацию, что и содержащий его объект ViewPager2
. Например, прокрутка не будет работать для представления вертикальной прокрутки внутри вертикально ориентированного объекта ViewPager2
.
Чтобы поддерживать представление прокрутки внутри объекта ViewPager2
с той же ориентацией, вы должны вызвать requestDisallowInterceptTouchEvent()
для объекта ViewPager2
, когда вместо этого вы планируете прокручивать вложенный элемент. Пример вложенной прокрутки ViewPager2 демонстрирует один из способов решения этой проблемы с помощью универсального пользовательского макета оболочки .
Дополнительные ресурсы
Дополнительные сведения о ViewPager2
см. в следующих дополнительных ресурсах.
Образцы
- Примеры ViewPager2 на GitHub
Видео
- Переворачивая страницу: переход на ViewPager2 (Android Dev Summit '19)