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 поддерживает листание справа налево. Перелистывание справа налево включается автоматически в зависимости от региональных настроек, но вы также можете включить его вручную для элемента 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().
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; } }
Рефакторинг интерфейсов TabLayout
В ViewPager2 году в интеграцию с TabLayout будут внесены изменения. Если вы используете ViewPager с объектом TabLayout для показа горизонтальных вкладок навигации, вам нужно провести рефакторинг объекта TabLayout для интеграции с ViewPager2.
TabLayout больше не зависит от ViewPager2 и теперь доступен в составе компонентов Material. Чтобы использовать ее, добавьте в файл build.gradle соответствующую зависимость:
Яркий
implementation "com.google.android.material:material:1.1.0-beta01"
Kotlin
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. Для интеграции с ViewPager в TabLayout используется собственный метод setupWithViewPager(), а для интеграции с ViewPager2 требуется экземпляр TabLayoutMediator.
Объект TabLayoutMediator также обрабатывает задачу создания заголовков страниц для объекта TabLayout, что означает, что классу адаптера не нужно переопределять 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(); } ... }
Поддержка вложенных прокручиваемых элементов
ViewPager2 не поддерживает вложенные представления прокрутки, если у них та же ориентация, что и у содержащего их объекта ViewPager2. Например, прокрутка не будет работать для вертикального представления прокрутки внутри вертикально ориентированного объекта ViewPager2.
Чтобы поддерживать прокрутку внутри объекта ViewPager2 с той же ориентацией, необходимо вызвать метод requestDisallowInterceptTouchEvent() для объекта ViewPager2, когда вы ожидаете прокрутку вложенного элемента. В примере вложенной прокрутки ViewPager2 показан один из способов решения этой проблемы с помощью универсального специального макета-оболочки.
Дополнительные ресурсы
Дополнительную информацию о ViewPager2 можно найти в следующих ресурсах.
Образцы
- Примеры ViewPager2 на GitHub
Видео
- Переход на ViewPager2 (Саммит для разработчиков Android '19)