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. Хотя TabLayout использует собственный метод setupWithViewPager() для интеграции с ViewPager, для интеграции с 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 (Android Dev Summit '19)