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)