Android viewpager with views

ViewPager

В Android есть компонент ViewFlipper, который позволяет прокручивать экран влево-вправо. Но он имеет ряд недостатков. ViewFlipper не позволяет с лёгкостью добиться эффекта привязки, когда экраны двигаются вместе с пальцем. Также нужен механизм автоматической доводки экранов в ту или иную сторону.

Google предложила другой вариант — использовать компонент ViewPager, который входит в состав Support Package. В новых проектах необходимая библиотека добавляется автоматически, поэтому мы сразу можем попробовать написать демонстрационный пример.

Так как компонент относится к пакету совместимости, то используемые фрагменты должны относиться к классу android.support.v4.app.Fragment, даже если вы собираетесь писать приложение для новых устройств, которые поддерживают стандартные фрагменты. На данный момент библиотека переписывается на AndroidX, поэтому я буду по возможности заменять старые примеры. Все названия классов и методов остаются, меняется только имя пакета.

ViewPager относится к категории ViewGroup и схож по работе с компонентами на основе AdapterView (ListView и Gallery). На панели инструментов Android Studio компонент можно найти в разделе Containers. При использовании ViewPager в разметке используйте полное имя класса. Старый вариант.

Данные для отображения компонент берёт из адаптеров:

  • PagerAdapter (фрагменты не обязательны)
  • FragmentPagerAdapter (если используется мало фрагментов)
  • FragmentStatePagerAdapter (если много фрагментов)

Вам нужно унаследовать класс от нужного адаптера и реализовать свои методы. Добавление и удаление экранов реализуется с помощью методов instantiateItem() и destroyItem() соответственно. Элементы View для отображения можно создавать прямо в адаптере. Такой подход хорош тем, что ViewPager можно настраивать так, чтобы в адаптере не хранились все экраны сразу. По умолчанию адаптер хранит текущий экран, и по одному слева и справа от него. Это может сэкономить память, если содержание экранов слишком сложное.

Когда пользователь переключается на другой фрагмент, то вызывается метод onDestroyView(), а не onDestroy().

FragmentPagerAdapter

Рассмотрим простой пример с минимальным количеством кода для первого знакомства. Создадим новый проект. В activity_main.xml добавим ViewPager из примера выше.

Подготовим разметки для фрагментов.

Сначала создадим ещё один файл разметки res/layout/fragment_1.xml:

И ещё один файл res/layout/fragment_2.xml:

Создадим новый класс для первого фрагмента Fragment1:

Класс для второго фрагмента Fragment2:

Фрагменты подготовлены. Теперь в основном классе MainActivity создадим адаптер на основе FragmentPagerAdapter и напишем остальной код:

В методе getCount() следует возвращать количество страниц. В методе getItem() по номеру позиции нужно вернуть фрагмент.

Если у нас имеется три экрана, то может быть удобно сразу показать второй экран, чтобы можно было прокручивать или влево к первому экрану или вправо к третьему экрану. Это достигается вызовом метода setCurrentItem(1).

В нашем примере мне было лень создавать третий фрагмент, поэтому второй фрагмент используется два раза. В качестве фрагмента по умолчанию я установил первый фрагмент. Запустите пример и подвигайте экраны приложения вправо-влево.

Сам принцип теперь понятен? Нужно подготовить фрагменты и через адаптер сменять их.

PageTransformer

Если хочется добавить анимации при смене фрагментов, то воспользуйтесь специальным классом PageTransformer. Попробуем менять прозрачность фрагмента.

Класс PageTransformer заслуживает отдельной статьи.

PagerTitleStrip

Рассмотрим другой вариант и изучим другие возможности. Для примера будем использовать всего один фрагмент, содержимое которого будем менять динамически. А сверху добавим специальную полоску с заголовками.

Изменим разметку для фрагмента (fragment_1.xml):

Создадим два строковых массива в ресурсах (res/values/strings.xml):

Теперь подготовим класс для фрагмента CatFragment:

При создании фрагмент извлекает данные из объекта Bundle и использует их для заполнения текстов и картинки.

Создадим класс для адаптера в отдельном файле для удобства с помощью мастера создания класса, указав в качестве суперкласса FragmentPagerAdapter.

У адаптера должен быть пустой конструктор и два обязательных метода getItem() и getCount(). Немного переделаем конструктор, чтобы он использовал контекст, необходимый для извлечения данных из ресурсов.

Для подсчёта количества экранов используется свойство массива length. А для формирования содержимого экрана мы помещаем нужные данные в объект Bundle, которые затем будут переданы фрагменту. Данные находятся в трёх массивах: короткий текст, длинный текст и ссылка на изображение в ресурсах.

Метод getPageTitle() позволяет задать заголовок для фрагмента. Сам заголовок мы зададим в следующем шаге.

Приготовления почти закончены. Изменим разметку главной активности, чтобы у отдельного экрана появились заголовки. Для этого добавляется тег PagerTitleStrip:

Если вы хотите видеть заголовок внизу фрагмента, то используйте android:layout_gravity=»bottom».

Загружаем ViewPager и подключаем адаптер.

Пример на планшете в альбомной ориентации:

Свойства PagerTitleStrip можно изменить программно, например, поменять размер и цвет заголовков.

Читайте также:  Все виды прошивок для андроид

Тег PagerTitleStrip можно заменить на PagerTabStrip. В этом случае заголовке станут ещё и кнопками-вкладками. Ничего переписывать не придётся. Вы по-прежнему можете перелистывать страницы пальцами или нажимать на заголовки.

У ViewPager есть обработчик событий OnPageChangeListener с тремя методами:

Например, метод onPageSelected() позволяет получить номер текущего экрана при пролистывании (отсчёт от 0).

Метод onPageScrolled() даёт представление о текущем значении скрола при пролистывании.

Метод onPageScrollStateChanged() позволяет узнать о состоянии, в котором находится скрол (SCROLL_STATE_IDLE – экраны не листаются, SCROLL_STATE_DRAGGING – пользователь «тащит» страницу, SCROLL_STATE_SETTLING – палец пользователя «доводит» страницу до конца.

Сейчас данный слушатель помечен как устаревший.

Адаптер FragmentStatePagerAdapter

Это старый вариант примера, когда компонент только появился. Я не стал делать ревизию, оставляю вам для самостоятельного изучения.

Адаптер FragmentPagerAdapter держит все фрагменты в памяти, а FragmentStatePagerAdapter создаёт их заново по мере необходимости. В этом их принципиальное отличие. Напишем пример и добавим поддержку кнопки Back.

Создадим разметку для отдельной страницы (pager.xml):

Создадим класс фрагмента с минимальным кодом

Разметку активности оставим минимальной:

Код для активности. Тут всё уже знакомо.

Заключение

В Android Studio в некоторых шаблонах встречается реализация ViewPager из коробки. Недавно Гугл представила бета-версию ViewPager2, которая должна заменить старый компонент.

Источник

Android working with ViewPager2, TabLayout and Page Transformers

As we all know, the Android team is constantly releasing new updates and improvements to the Android Framework. One of the components that received major updates is ViewPager2. ViewPager2 is the replacement of ViewPager and got few performance improvements and additional functionalities. In order to use ViewPager2, you should consider using androidx artifacts in your project.

In my earlier tutorial, I have explained Building Intro Sliders to your App using ViewPager. In this article, we are going to try the same but using ViewPager2. We’ll also go through the additional interesting functionalities provided by ViewPager2.

DEMO

Whats’s New in ViewPager2

  • ViewPager2 is improvised version of ViewPager that provides additional functionalities and addresses common issues faced in ViewPager.
  • ViewPager2 is built on top of RecyclerView. So you will have great advantages that a RecyclerView has. For example, you can take advantage of DiffUtils to efficiently calculate the difference between data sets and update the ViewPager with animations.
  • ViewPager2 supports vertical orientation. Earlier vertical swiping is done by customizing the ViewPager.
  • ViewPager2 has right-to-left(RTL) support and this will be enabled automatically based on app locale.
  • When working with a collection of Fragments, calling notifyDatasetChanged() will update the UI properly when an underlying Fragment changes its UI.
  • ViewPager2 supports page transformations i.e you can provide animations when switching between pages. You can also write your own custom page transformation.
  • PagerAdapter is replaced by RecyclerView.Adapter as its based on RecyclerView.
  • FragmentStatePagerAdapter is replaced by FragmentStateAdapter.

To get started with ViewPager2, add the dependency to app/build.gradle.

1. ViewPager with Static Views

Usually, ViewPager is used to achieve a tabbed view by combining the TabLayout and collection of Fragments. But if you want to have static views without the Fragment class, you can do it using RecyclerView adapter class.

Below is an example of implementing Intro Slides using ViewPager2. The designs are taken from my older example How to Build Intro Slider for your App but the implementation part varies due to changes in ViewPager2 and in adapter class.

Add the ViewPager2 to your layout file. You can see namespace is uses androidx package androidx.viewpager2.widget.ViewPager2.

This creates horizontally swipeable views that are created using static XML layouts. The full code of this example can be found here.

2. ViewPager with Tabs & Fragments

If you want to create swipeable views with Tabs, you can combine ViewPager2, TabLayout and Fragments.

To use TabLayout, add Material Components to your package. This makes the material TabLayout available in your project.

Add TabLayout and ViewPage2 to your layout.

Create required test Fragment classes MoviesFragment, EventsFragment and TicketsFragment.

Finally, create an adapter class that provides Fragments to ViewPager. Here we can see ViewPagerFragmentAdapter extends FragmentStateAdapter.

3. ViewPager2 Orientation

In a few scenarios, you might want to provide vertical swiping instead of traditional horizontal swiping. To enable vertical swiping, add android:orientation to ViewPager2 element.

Vertical orientation can also be enabled programmatically by calling setOrientation() method.

4. ViewPager Transformers

Another great feature of ViewPager2 is, page transformations i.e the page transition animation from one page to another. Traditionally, we see a sliding animation between two screens. This animation can be customized by providing page transformers to ViewPage2.

Читайте также:  Где находится папка кэш андроид

To set a custom transformation, use setPageTransformer() method. Below example creates Flip animation between pages.

This creates beautiful vertical flip animation when swiping the pages.

Below are a set of ViewPager transitions that I have collected from different sources (All the credits goes to original authors).

Hi there! I am Founder at androidhive and programming enthusiast. My skills includes Android, iOS, PHP, Ruby on Rails and lot more. If you have any idea that you would want me to develop? Let’s talk: [email protected]

Источник

ViewPager 2 — новая функциональность в старой обертке

ViewPager — один из самых известных и широко используемых компонентов Android Support Library. Все простейшие карусели, онбординги и слайдеры в мобильных приложениях для Android выполнены именно на нем. В феврале 2019 года команда разработки AndroidX выпустила ViewPager2. Давайте разберемся, какие у этого были предпосылки и какими преимуществами обладает обновленная версия компонента.

ViewPager 2

На момент написания поста (июль 2019) доступна бета-версия ViewPager2, а это значит, что нижеупомянутые проблемы могут быть исправлены, а функциональность доработана и расширена. Разработчики обещают в будущем добавить поддержку TabLayout (пока он умеет работать только с первой версией), оптимизировать производительность адаптера, внести множество мелких исправлений и доработать документацию.

Интеграция

Компонент не поставляется со стандартными пакетами, а подключается отдельно. Для этого необходимо добавить в блок dependencies в gradle-скрипте вашего модуля следующую строку:

Реализация

Начнём с хороших новостей: переход с первой на вторую версию максимально прост и сводится к смене импортов. Старый добрый синтаксис не тронули: текущую страницу возвращает метод getCurrentItem(), подписаться на изменение состояния пейджера позволяет ViewPager2.onPageChangeCallback, адаптер по-прежнему устанавливается через setAdapter().

Стоит копнуть глубже, как становится понятно, что у первого и второго пейджеров нет ничего общего кроме интерфейсов. Знакомство с реализацией метода setAdapter() не оставляет почвы для сомнений:

Да, ViewPager2 является всего лишь обёрткой над RecyclerView. С одной стороны, это большой плюс, с другой же — добавляет головной боли. Замаскировать RecyclerView под «листалку» стало возможно с появлением PagerSnapHelper. Этот класс меняет физику скролла. Когда пользователь отпускает палец, PagerSnapHelper рассчитывает, какой элемент списка находится ближе всего к осевой линии списка, и с плавной анимацией выравнивает его точно по центру. Таким образом, если свайп был достаточно резким, список пролистывается к следующему элементу, в противном случае — с анимацией возвращается в исходное состояние.

При использовании PagerSnapHelper убедитесь, что ширина и высота самого RecyclerView, а также всех его ViewHolder’ов задана равной MATCH_PARENT. В противном случае поведение SnapHelper будет непредсказуемым, могут возникнуть баги в совершенно неожиданных местах. Всё это делает создание карусели из элементов небольшой высоты довольно трудоёмким, хотя и возможным.

Учитывая все вышесказанное, в верстке виджет будет выглядеть следующим образом:

В одном пакете с ViewPager2 мы также можем найти класс ScrollEventAdapter, помогающий сохранить преемственность синтаксиса. ScrollEventAdapter реализует RecyclerView.OnScrollListener и трансформирует события скролла в события OnPageChangeCallback.

Теперь OnPageChangeCallback представлен не интерфейсом, а абстрактным классом, что позволяет переопределять только необходимые методы (в большинстве случаев вам понадобится только onPageSelected(Int), срабатывающий при выборе определенной страницы):

Особенности

Достоин упоминания и метод setPageTransformer(), в качестве параметра принимающий ViewPager2.PageTransformer. Он устанавливает callback на каждое событие выбора страницы и служит для задания собственной анимации этой страницы. Callback получает на вход View текущей страницы и её номер. Ближайшим аналогом этого метода является ItemAnimator из RecyclerView.

В новых версиях библиотеки были добавлены две реализации трансформера:

CompositePageTransformer и MarginPageTransformer. Первый отвечает за то, чтобы комбинировать трансформеры, чтобы применять сразу несколько трансформаций к одному пейджеру, а второй — для проставления отступов между страницами:

Кроме того, новый виджет поддерживает смену ориентации: простым вызовом метода setOrientation() вы можете превратить свой пейджер в вертикальный список со свайпам сверху вниз:

Происходит это вновь благодаря переходу на RecyclerView: под капотом вызывается смена ориентации LayoutManager, который отвечает за отображение элементов списка. Хочется отметить, что делегирование большого количества задач другим классам пошло новому компоненту на пользу: его листинг стал гораздо более компактным и читаемым.

На этом интересное не заканчивается. В одном из обновлений ViewPager2 получил поддержку ItemDecoration: класса-помощника для декорирования дочерних View. Этот механизм может быть использован для рисования разделителей между элементами, границ, подсветки ячеек.

Готовых реализаций декораторов уже очень много, ведь они уже много лет успешно применяются при работе с обычным RecyclerView. Все наработки теперь применимы и к пейджерам. «Из коробки» доступна стандартная реализация разделителей элементов пейджера:

Вместе с очередным апдейтом в мае 2019 года ViewPager2 был добавлен еще один важный метод: setOffscreenPageLimit(Int). Он отвечает за то, сколько элементов справа и слева от центрального будет инициализироваться в пейджере. Хотя по-умолчанию за кэширование и показ View отвечает RecyclerView, с помощью этого метода вы можете явно задать желаемое количество подгружаемых элементов.

Читайте также:  Merge into current android studio

Адаптер

Идейным наследником адаптера первого пейджера является FragmentStateAdapter: интерфейсы взаимодействия и нейминг классов почти не различаются. Изменения коснулись разве что именования некоторых методов. Если раньше нужно было реализовать абстрактную функцию getItem(position), чтобы вернуть нужный экземпляр Fragment для заданной позиции, и это именование могло трактоваться двояко, то сейчас эта функция переименована в createFragment(position). Общее количество фрагментов как и раньше поставляется функцией getCount().

Из важных структурных изменений интерфейса следует также отметить, что адаптер теперь имеет возможность управлять жизненным циклом своих элементов, поэтому вместе с FragmentManager’ом в конструкторе он принимает Lifecycle-объект, либо Activity или Fragment. Из-за этого, для безопасности методы saveState() и restoreState() были объявлены финальными, и закрыты для наследования.
За хранение фрагментов внутри RecyclerView отвечает класс FragmentViewHolder. Метод onCreateViewHolder() из FragmentStateAdapter вызывает FragmentViewHolder.create().

При вызове метода onBindViewHolder(), происходит связывание идентификатора элемента на текущей позиции и идентификатора ViewHolder, для дальнейшего присоединения к нему фрагмента:

И, наконец, при присоединении контейнера из ViewHolder к иерархии View, выполняется FragmentTransaction, добавляющий Fragment в контейнер:

Таким образом, вырисовывается два варианта использования ViewPager2: через наследование класса-адаптера либо напрямую от RecyclerView.Adapter, либо от FragmentStateAdapter.

Наверняка у вас возникнет вопрос: зачем же использовать второй пейджер с Fragment’ами и адаптером для них, когда есть нормально функционирующая первая версия? ViewPager — далеко не «серебряная пуля» при работе с большими динамическими списками данных. Он отлично подходит для создания каруселей со статичным набором картинок или баннеров, но вот пагинируемые новостные ленты с подгрузкой рекламных постов, фильтрацией рождают трудно-поддерживаемых и уродливых монстров. Рано или поздно вы обязательно столкнётесь со жгучим желанием переписать всё на RecyclerView. Теперь вам не придётся этого делать, потому что пейджер сам превратился в него, позаимствовав его мощные возможности по работе с динамическими списками, обернув их при этом в привычный синтаксис.

Единственное, что нам может предложить PagerAdapter — метод notifyDataSetChanged(), принуждающий ViewPager перерисовывать все отрисованные элементы списка. Вы можете резонно заметить, что никто не мешает нам хранить список позиций для существующих элементов и возвращать для них POSITION_UNCHANGED из метода getItemPosition(), всё так. Однако это решение нельзя назвать красивым, оно довольно громоздкое, к тому же, трудно расширяемое на те случаи, когда элементы в списке постоянно изменяются, а не только последовательно добавляются в конец. FragmentStateAdapter же обладает полным арсеналом методов RecyclerView.Adapter, поэтому логику перерисовки элементов можно настроить гораздо более гибко. Более того, вкупе с FragmentStateAdapter можно использовать DiffUtil, который позволяет почти полностью автоматизировать работу по уведомлению об изменениях.

Внимание! Для корректной работы методов notify… (кроме notifyDataSetChanged) следует переопределить методы getItemId(Int) и containsItem(Long). Делается это потому, что реализация по-умолчанию смотрит только на номер страницы, и если вы, например, добавите новый элемент после текущего, он не будет добавлен, так как getItemId останется неизменным. Пример переопределения этих двух методов на основе списка элементов типа Int:

Основной причиной появления ViewPager2 является нежелание изобретать велосипед. С одной стороны, команда разработки AndroidX явно готова отказаться от морально устаревшего ViewPager уже сейчас и уж точно не собирается вкладываться в расширение его функциональности. Да и зачем? Ведь RecyclerView уже и так умеет всё, что нужно. С другой стороны, удаление и прекращение поддержки настолько широко используемого компонента явно не добавит лояльности со стороны сообщества.

Подведём итоги: ViewPager2 определённо достоин внимания, хотя на данный момент и не лишён серьёзных недостатков.

Минусы

Плюсы

  • Поддержка всех преимуществ RecyclerView.Adapter: комбинирование элементов разного типа в одном списке, добавление и удаление элементов прямо во время свайпа, анимированная перерисовка содержимого списка при изменении;
  • Поддержка всего спектра notify… методов и автоматического вычисления изменений с помощью DiffUtil;
  • Простота перехода за счёт преемственности синтаксиса;
  • Поддержка вертикальной и горизонтальной ориентации «из коробки»;
  • Поддержка RTL;
  • Поддержка ItemDecorator;
  • Поддержка программного скролла посредством fakeScrollBy();
  • Возможность вручную выставить количество подгружаемых элементов;
  • Возможность использования любого из готовых open-source решений для сокращения boilerplate-кода, неизбежного, при написании кастомного RecyclerView.Adapter. Например, EasyAdapter.

В качестве резюме, хочется сказать, что к ViewPager2 действительно стоит присмотреться. Это многообещающее, расширяемое и функциональное решение. И хотя пускать новый виджет в продакшн ещё рано, можно с уверенностью сказать, что после полноценного релиза он может и должен полностью вытеснить своего прародителя.

Для тех смелых и решительных, кого статья вдохновила на эксперименты, в 28-ой версии Support Library появился PagerSnapHelper, а это значит, что вы можете использовать его вместе с вашим RecyclerView, воссоздав ViewPager2 своими руками.

Семпл работы ViewPager2 и FragmentStateAdapter.

Источник

Оцените статью