- Реализация списка с заголовком, футером и пагинацией в Андроид
- RecyclerView
- Настройка Gradle
- Добавление RecyclerView в XML представление
- Привязка XML с классом JAVA
- RecyclerView ItemDecoration
- RecyclerView Adapter
- Пагинация
- RecyclerView
- Принцип работы
- Добавление RecyclerView в проект
- Библиотека
- Добавление в макет
- Макет элемента списка
- Адаптер и viewHolder
- Последние штрихи
- Создание RecyclerView с помощью шаблона
- Стандартные LayoutManager’ы
- LinearLayoutManager
- GridLayoutManager
- StaggeredGridLayoutManager
- Динамическое переключение
- SnapHelper
- Использование нескольких макетов для элементов RecyclerView
- ViewType
- Несколько списков в одном RecyclerView
- ConcatAdapter
- Использование ConcatAdapter . Обзор некоторых методов класса
- Пример с header’ом и footer’ом
- Полезные ссылки
Реализация списка с заголовком, футером и пагинацией в Андроид
RecyclerView
RecyclerView — это расширенная версия ListView с некоторыми улучшениями в производительности и с новыми функциями. Как следует из названия, RecyclerView перерабатывает или повторно использует представления элементов при прокрутке. В RecyclerView гораздо проще добавлять анимации по сравнению с ListView. В этом уроке мы разберем, как создать RecyclerView с заголовком, футером, разбиением на страницы и анимацией.
Настройка Gradle
Добавьте следующую зависимость в файл build.gradle:
Добавление RecyclerView в XML представление
После того, как проект будет синхронизирован, добавьте компонент RecyclerView в ваш макет:
Привязка XML с классом JAVA
Теперь в методе onCreate вашей активности добавьте следующий код:
Прежде чем идти дальше, давайте подробно рассмотрим приведенный выше код
- Layout Manager — Простыми словами, Layout Manager помогает нам определить структуру нашего RecyclerView. Есть три встроенных Layout Managers. Помимо этого, мы можем создать собственный пользовательский Layout Manager, чтобы удовлетворить наши требования.
- LinearLayoutManager показывает элементы в списке с вертикальной или горизонтальной прокруткой.
- GridLayoutManager показывает элементы в сетке.
- StaggeredGridLayoutManager показывает элементы в шахматной сетке.
RecyclerView ItemDecoration
ItemDecoration позволяет приложению добавлять специальный полосы и смещения к определенным представлениям элементов из набора данных адаптера. Это может быть полезно для рисования разделителей между элементами, выделениями, границами визуальной группировки и т. д. – developer.android.com
В этом примере мы будем использовать ItemDecoration для добавления отступов к каждому элементу.
В вышеприведенном классе мы устанавливаем отступы к нулевому элементу.
RecyclerView Adapter
Теперь давайте настроим адаптер ReeyclerView с заголовком и футером.
Пагинация
Теперь, когда адаптер готов, давайте посмотрим, как добавить пагинацию в список RecyclerView. Это довольно легко сделать и должно быть добавлено в onCreate после установки Adapter to Recycler-View.
Всякий раз, когда данные изменяются в mList, вызывайте функцию ниже, чтобы обновить адаптер RecyclerView и показать новые данные.
Надеюсь, что этот пост поможет вам получить общее представление о настройке RecyclerView с заголовком, подвалом и пагинацией.
Источник
RecyclerView
RecyclerView — компонент для отображения элементов списка, который является более продвинутой и гибкой версией ListView , но не является его родственником, а относится к семейству ViewGroup .
Принцип работы
Для отображения данных RecyclerView использует несколько компонентов:
- Объект RecyclerView , который нужно добавить в макет. Он заполняется элементами списка в зависимости от того, какой был установлен LayoutManager . Существуют стандартные LayoutManager ‘ы, например, LinearLayoutManager отображает элементы в виде списка, а GridLayoutManager — в виде сетки. Но можно создать и свой собственный LayoutManager .
- Элементы списка представлены в виде объектов viewHolder . Например, если список состоит из различных видов деревьев, то viewHolder — это конкретный вид дерева — сосна, яблоня, берёза и т.д. RecyclerView создает столько объектов viewHolder , сколько требуется для отображения на экране устройства и несколько про запас. Когда пользователь начинает прокручивать список, RecyclerView берёт те объекты viewHolder , которые ушли за пределы экрана и “привязывает” к ним новые данные.
- Объекты viewHolder управляются адаптером. Он создаёт объекты viewHolder и привязывает к ним информацию.
Добавление RecyclerView в проект
Библиотека
По умолчанию (при создании нового проекта) функциональность RecyclerView не доступна. Поэтому для начала нужно добавить соответствующую библиотеку. Для этого в файл build.gradle , который находится в папке app , добавьте одну из библиотек:
Версия support library уже вряд ли когда-нибудь изменится, так как её поддержку остановили. А вот за версией androidx нужно следить.
После того, как библиотека добавлена, обязательно нажмите на кнопку Sync Now, чтобы изменения вступили в силу.
Добавление в макет
Библиотека добавлена, а значит теперь мы можем обращаться к RecyclerView . Первым делом следует добавить его в макет. Он может быть добавлен как дочерний элемент другого компонента:
Либо может быть единственным (корневым) компонентом макета:
Далее для корректной работы RecyclerView требуется установить LayoutManager и адаптер. LayoutManager может быть установлен двумя способами. В макете:
Либо вместе с адаптером в классе фрагмента или активити:
При этом начиная с версии Android Studio 3.6 необязательно вызывать метод findViewById(), а можно напрямую обратится к компоненту из макета по его идентификатору (как в примере выше).
Макет элемента списка
Для элемента списка можно создать собственный макет. Например, у меня каждый элемент состоит из названия дерева и его краткого описания. Поэтому макет для элемента включает в себя два компонента TextView :
А можно воспользоваться стандартными макетами, к которым можно обращаться через android.R.layout.НАЗВАНИЕ_ИЗ_СПИСКА . Но на мой взгляд это вариант для ленивых или для любопытных.
Адаптер и viewHolder
Адаптер — это класс, который занимается передачей данных в список, созданием объектов viewHolder и их обновлением. Адаптер должен наследоваться от класса RecyclerView.Adapter .
ViewHolder — это тоже класс, объекты которого адаптер использует для хранения и визуализации элементов списка. ViewHolder должен наследоваться от класса RecyclerView.ViewHolder . Как правило этот класс располагают внутри адаптера.
В классе адаптера нужно обязательно переопределить 3 метода:
- onCreateViewHolder() — данный метод вызывается LayoutManager ‘ом, чтобы создать объекты viewHolder и передать им макет, по которому будут отображаться элементы списка.
- onBindViewHolder() — данный метод вызывается LayoutManager ‘ом, чтобы привязать к объекту viewHolder данные, которые он должен отображать.
- getItemCount() — возвращает общее количество элементов в списке.
А в классе ViewHolder требуется указать используемые компоненты разметки.
Последние штрихи
Теперь адаптер настроен и готов к использованию. Осталось только его подключить. Делается это в классе фрагмента или активити (в моём примере используется фрагмент).
Если какой-либо элемент списка изменился, то следует вызвать метод адаптера notifyItemChanged() и передать ему позицию элемента, которую требуется обновить. Вместо него можно использовать метод notifyDataSetChanged() , который будет обновлять полностью весь список, но из-за этого он является ресурсозатратным.
Создание RecyclerView с помощью шаблона
Есть возможность пропустить все вышеописанные шаги и воспользоваться стандартным шаблоном. Этот шаблон автоматически добавит в проект новый фрагмент с поддержкой RecyclerView . Это означает, что студия за вас создаст не только фрагмент, но и адаптер, ViewHolder , макет для элемента списка, а также код, который всё это подключает.
Кликните по любому файлу правой кнопкой мыши и в появившемся контекстном меню выберите: New > Fragment > Fragment (List).
Стандартные LayoutManager’ы
RecyclerView использует LayoutManager для того, чтобы расположить элементы списка на экране. При этом каждый LayoutManager позволяет расположить их по-своему.
Существует три стандартных LayoutManager ‘а:
- LinearLayoutManager — упорядочивает элементы в виде обычного вертикального или горизонтального списка.
- GridLayoutManager — размещает элементы в виде сетки одинакового размера.
- StaggeredGridLayoutManager — размещает элементы в виде неравномерной сетки: каждый столбец будет слегка смещён по сравнению с предыдущим.
Как правило этих вариантов достаточно для большинства ситуаций. Но если это не ваш случай, то можно создать свой собственный LayoutManager , расширив класс RecyclerView.LayoutManager .
LinearLayoutManager
По умолчанию LinearLayoutManager упорядочивает элементы в виде вертикального списка.
У данного класса есть другой конструктор, который позволяет явно задать ориентацию списка. Помимо контекста, ему требуется два параметра:
- Ориентация — задаётся с помощью констант HORIZONTAL и VERTICAL класса LinearLayoutManager .
- Булево значение: если передать true — список будет установлен в обратном порядке (начало будет в конце).
Эти параметры можно устанавливать с помощью специальных методов:
Либо с помощью специальных атрибутов в XML:
GridLayoutManager
Размещает элементы списка в виде сетки одинакового размера.
У класса GridLayoutManager есть два конструктора. Для использования первого конструктора необходимы два параметра: контекст и количество столбцов в сетке.
Для второго конструктора — четыре параметра:
- контекст;
- количество столбцов в сетке;
- ориентация списка — задаётся с помощью констант HORIZONTAL и VERTICAL класса LinearLayoutManager ;
- булево значение — если передать true — список будет установлен в обратном порядке (начало будет в конце).
Если задать горизонтальную ориентацию, то в списке будет столько рядов, сколько было задано вторым параметром (в данном примере = 3), а листаться, само собой, будет в бок.
То же самое можно задать с помощью XML атрибутов:
StaggeredGridLayoutManager
Размещает элементы в виде неравномерной сетки.
У класса StaggeredGridLayoutManager всего один конструктор с двумя параметрами:
- количество столбцов в сетке;
- ориентация списка — задаётся с помощью констант HORIZONTAL и VERTICAL класса StaggeredGridLayoutManager .
Если задать горизонтальную ориентацию, то в списке будет столько рядов, сколько было задано первым параметром (в данном примере = 3), а листаться, само собой, будет в бок.
То же самое можно задать с помощью XML атрибутов:
Динамическое переключение
Переключаться между LayoutManager ‘ами можно динамически. Например, при нажатии на кнопку:
SnapHelper
SnapHelper позволяет настроить “прилипание” элементов к определённой позиции в RecyclerView . Например, при пролистывании можно настроить прилипание таким образом, что первый видимый элемент будет сам прилипать к краю экрана или ближайший к центру элемент будет автоматически вставать в центр экрана.
Существует два стандартных класса для работы с прилипанием элементов: LinearSnapHelper и PagerSnapHelper .
LinearSnapHelper застовляет ближайший к центру элемент вставать в центр экрана. Допустим вы листаете список и в какой-то момент убрали пальцы от экрана. Список без вашего участия автоматически прокрутится и установит в центр экрана ближайший элемент.
PagerSnapHelper предназначен для полноэкранных элементов и ведёт себя как ViewPager .
Добавить себе в проект просто:
Если ни один вариант вас не устраивает, то создайте свою собственную реализацию этих классов и опишите в нёй необходимое поведение элементов при пролистывании списка.
Использование нескольких макетов для элементов RecyclerView
При отображении списка все его элементы выглядят одинаково и в большинстве случаев это оправдано и разработчика вполне устраивает. Тем не менее возникают ситуации, когда нужно один или целый ряд элементов выделять из остальных. Например:
- требуется добавить header или footer;
- отображение двух списков в одном RecyclerView ;
- выделение определённого элемента в списке.
ViewType
Одним из способов, который используется для выделения элементов в списке, является присвоение viewType каждому объекту viewHolder . ViewType — это произвольное цифровое значение от 0 и выше, которое необходимо для того, чтобы различать объекты viewHolder между собой. Например, если вам требуется добавить header и footer, при этом элементы списка должны выглядеть идентично, то у вас будет три viewType : для header’а, footer’а и элемента списка.
Для начала добавьте в папку res/layout три макета: для header’а, footer’а и элемента списка.
В классе адаптера создадим константы, которые будут хранить значения viewType .
Для удобства создадим базовый класс GenericViewHolder .
От него будут наследоваться три класса ViewHolder . Каждый из них отвечает за свой макет и привязку к нему данных.
Обратите внимание на класс ListItemViewHolder . В отличии от остальных он является внутренним (модификатор inner ), так как ему для привязки данных требуется обращаться к свойству trees своего внешнего класса. Из поступившего номера позиции вычитается единица, так как нулевая позиция занята header’ом и не будет сюда поступать.
Теперь возьмёмся за код самого адаптера. С помощью метода getItemViewType() зададим viewType каждому объекту viewHolder в зависимости от его позиции в списке. Первый и последний элемент списка — это header и footer. Если в списке 15 элементов, то позиция для footer’а будет 15 + 1, так как header всегда находится в нулевой позиции.
В методе onCreateViewHolder() создаём объект viewHolder в зависимости от viewType .
В методе onBindViewHolder() вызываем метод привязки данных bindView() , который переопределён во всех наших классах ViewHolder .
Метод getItemCount() должен возвращать количество элементов в RecyclerView . Поэтому следует учесть наличие header’а и footer’а.
Адаптер готов к использованию. Результат будет примерно таким:
Несколько списков в одном RecyclerView
У меня как-то возникала необходимость отображения двух списков на одном экране друг за другом. При этом при клике по элементу из первого списка он должен был переместиться во второй список и наоборот.
Первая появившаяся мысль — добавить на экран два RecyclerView . И это вполне себе работает. Но возникает ряд неудобств, одно из них — некорректная работа overScroll. Эффект overScroll визуально показывает, что вы дошли до конца или начала списка.
И если на экране два RecyclerView , то эффект overScroll появляется для каждого из них. Выглядит не очень красиво. Конечно можно overScroll эффект отключить, но тогда появляется неуверенность: “А дошел ли я до конца списка?”.
В общем нашлось решение, при котором один RecyclerView работает с двумя списками и завязано это всё на использовании viewType из примера выше.
Создаём два макета — один для элементов первого списка, второй — для элементов второго списка. У меня они отличаются только цветом фона — у элементов первого списка он белый, у второго — красный.
Константы для хранения значений viewType :
Также используем для удобства базовый класс GenericViewHolder .
От него будут наследоваться два класса ViewHolder : один для элементов первого списка, второй для элементов второго списка.
Каждый из них будет по своему реализовывать метод bindView() . Для элементов первого списка ничего рассчитывать не требуется, нужно просто привязать к объектам viewHolder данные по порядку.
Второй список должен отображаться сразу после первого. Так как номер позиции может быть любым числом от 0 до list1.size + list2.size , в методе bindView() класса SecondListItemViewHolder потребуется произвести расчеты.
Также обратите внимание что оба класса являются внутренними (модификатор inner ), так как им для привязки данных требуется обращаться к компонентам адаптера list1 и list2 .
Переходим к коду адаптера. В методе getItemViewType() нужно предусмотреть все возможные сценарии: когда в обоих списках есть элементы и когда в одном из списков нет элементов.
В методе onCreateViewHolder() создаём объект viewHolder в зависимости от viewType .
В методе onBindViewHolder() вызываем метод привязки данных bindView() , который переопределён во всех наших классах ViewHolder , а также вешаем слушателя.
Метод getItemCount() должен возвращать количество элементов в RecyclerView . Поэтому следует учесть наличие двух списков.
При клике по элементу из первого или второго списка будет вызван метод updateUi() , который отмечает, что по элементу кликнули и переносит его в другой список.
Адаптер готов к использованию. Результат будет примерно таким:
Необязательно делать списки динамическими, таким образом можно отображать и статические списки. И даже комбинировать с предыдущим примером — добавлять header (один или для всех списков) и footer.
ConcatAdapter
Несмотря на то, что все примеры, описанные в предыдущем разделе, вполне себе рабочие, в плане кода выглядят не очень хорошо. В основном из-за того, что в одном адаптере скапливается множество реализаций класса ViewHolder , а также логика их отображения. Если нам понадобится добавить или удалить какой-либо ViewHolder , то придётся переписывать класс адаптера и заново его тестировать.
По этой причине в recyclerview:1.2.0-alpha02 был добавлен новый класс MergeAdapter , который в версии recyclerview:1.2.0-alpha04 переименовали в ConcatAdapter .
ConcatAdapter позволяет отображать содержимое нескольких адаптеров в одном RecyclerView . То есть вместо накапливания множества реализаций класса ViewHolder в одном адаптере, мы можем создать для каждого ViewHolder ‘а свой адаптер, а потом объединить их все при помощи ConcatAdapter . Таким образом код станет более понятным и переиспользуемым, а если потребуется добавить в RecyclerView что-то новое — просто создадим новый адаптер.
Использование ConcatAdapter . Обзор некоторых методов класса
Передайте в конструктор ConcatAdapter все ваши адаптеры, которые нужно объединить, чтобы отображать их в одном RecyclerView .
Адаптеры будут отображаться на экране в том порядке, в котором были переданы в конструктор класса ConcatAdapter .
Если один из адаптеров должен несколько раз отображаться на экране, то создайте несколько объектов этого адаптера и передайте их все в конструктор класса ConcatAdapter .
Когда мы вызываем метод notifyDataSetChanged() в любом из адаптеров, ConcatAdapter тоже его вызывает.
У класса ConcatAdapter есть конструктор, который позволяет передавать список из адаптеров. На экране они будут отображаться в том порядке, в котором были добавлены в список.
Если вам нужно добавить один из адаптеров не сразу, а позже, то используйте метод addAdapter() . Этот метод добавляет адаптер в последнюю позицию, т.е. отображаться он будет после всех остальных.
Если же требуется добавить адаптер не последним, а в определённую позицию, то в метод addAdapter() передайте номер позиции и сам адаптер. Метод добавит адаптер в указанную позицию, а все остальные адаптеры сместятся.
Обратите внимание, что номер позиции не может быть больше количества адаптеров (отсчёт начинается с нуля). В примере у нас три адаптера, каждому из которых может быть присвоена позиция 0, 1 или 2. Если указать число выше, то вылетит ошибка.
Для удаления адаптера используется метод removeAdapter() .
Чтобы узнать сколько элементов объединил в себе ConcatAdapter вызовите метод itemCount . Количество элементов суммируется со всех добавленных адаптеров.
Можно получить список всех адаптеров, добавленных в ConcatAdapter . Для этого вызовите adapters , который возвращает MutableList со всеми адаптерами.
Обычно если в адаптере нам надо обратиться к какой-либо позиции, мы используем метод getAdapterPosition() класса ViewHolder . При работе с ConcatAdapter вместо getAdapterPosition() следует использовать getBindingAdapterPosition() .
Пример с header’ом и footer’ом
Возьмём пример, который был в разделе ViewType: требуется отобразить header, footer и список между ними. В таком случае у нас будет три адаптера: для header’а, footer’а и элементов списка. Можно использовать и два адаптера, если логика и внешний вид header’а и footer’а идентичны. Но для наглядности в своём примере я буду использовать три.
Для начала убедитесь, что в build.gradle добавлена нужная версия библиотеки recyclerView:
Можно использовать и версию 1.2.0-alpha02 , но учтите, что в этой версии ConcatAdapter ещё носит название MergeAdapter .
Создадим классы данных для header’а, footer’а и элементов списка (деревья).
Добавим макет для каждого компонента.
За отображение header’а будет отвечать HeaderAdapter .
Для отображения элементов списка создадим ListItemAdapter .
Ну и наконец адаптер для отображения footer’а.
Теперь осталось лишь объединить всё вместе в методе onCreate() — для активити или в методе onViewCreated() — для фрагмента. Для этого создадим по одному объекту каждого из адаптеров и передадим их классу ConcatAdapter() в том порядке, в котором они должны быть отражены на экране.
Если же в ConcatAdapter() передать footer сразу после header’а
то результат будет таким:
Полезные ссылки
Общие ссылки по теме:
Create a List with RecyclerView — гайд из официальной документации.
RecyclerView — документация по классу (androidx).
Recyclerview — Release Notes — информация о выходе новых версий.
Using the RecyclerView — гайд от codepath.
Кастомизация:
Having multiple lists in a single RecyclerView — гайд по использованию нескольких списков в одном RecyclerView .
Адаптеры:
ConcatAdapter — официальная документация.
Код:
RecyclerView — полный код всех примеров из данной статьи.
MergeAdapter-sample — пример реализации ConcatAdapter от Kotlin Android Open Source.
Concat Adapter Android Example — пример реализации ConcatAdapter от Mindorks.
Источник