- Introduction to Android Nested RecyclerView
- Prerequisites
- Setting Up
- What we will look at
- Laying out UI components
- The parent RecyclerView
- The parent RecyclerView items
- Child RecyclerView layout items
- Setting up Model classes
- Setting up Adapter classes
- About the author
- Want to learn more about the EngEd Program?
- Реализация списка с заголовком, футером и пагинацией в Андроид
- RecyclerView
- Настройка Gradle
- Добавление RecyclerView в XML представление
- Привязка XML с классом JAVA
- RecyclerView ItemDecoration
- RecyclerView Adapter
- Пагинация
- О RecyclerView и выделении элементов
- Содержание
- 1. Немного о ViewHolder’ах
- 2. Вкратце о RecyclerView
- 3. Выделяем элементы
- 4. Заключение + Бонус
Introduction to Android Nested RecyclerView
February 9, 2021
RecyclerView is a container widget used to display large data sets that change dynamically. The RecyclerView widget manages the display and handling of items in a list. It provides Layout Managers to position these items. This way, you can create customized layout managers for RecyclerView containers.
We can use a RecyclerView inside another RecyclerView. We refer to this as nested RecyclerView. It is an instance where one RecyclerView widget is the parent to another RecyclerView widget. A good example where a nested RecyclerView widget is implemented includes the Google Play Store.
Applications such as eCommerce, music apps, and movie apps such as Netflix uses this concept.
In this case, the application would have one parent or the outer RecyclerView widget, which is scrollable vertically. The parent RecyclerView host a child or inner RecyclerView, that is scrollable horizontally.
A RecyclerView widget recycles UI components. It creates components that allow users to scroll through the list. As the user scrolls down the screen, RecyclerView changes the UI components to the data they represent.
With such increased UI complexity, you might need to use a nested RecyclerView. This guide will discuss and implement a nested RecyclerView using Android studio.
Prerequisites
To follow along with this guide, prior knowledge of Android RecyclerView would be helpful.
Setting Up
This guide will use Java in Android studio to create a nested RecyclerView application. Go ahead and start a new Android studio project. RecyclerView uses a library implementation «androidx.recyclerview:recyclerview:1.1.0» .
The library is pre-added on project creation, so there is no need to include the library in your gradle files.
What we will look at
We will be using a movie app scenario to implement a nested RecyclerView concept. We will use a movie template UI on the home screen. We’ll have a vertically scrolling list (parent RecyclerView) and a horizontal carousel as the child RecyclerView.
Laying out UI components
This application will have three XML files. We’ll implement them from the top-down, starting with the parent to the child UI components.
The parent RecyclerView
As we said, we have an outer and inner RecyclerView. To start with, we will create a parent RecyclerView inside the main activity XML file. In this case, activity_main.xml . This will be the containing layout of the application. Go ahead and add the following Recycler View widget code into it.
The parent RecyclerView items
An item layout defines the layout of the individual row that we will show in the RecyclerView list. This layout will hold any item that is a child of the primary (parent) RecyclerView. This includes the child RecyclerView. Create an XML file, name it parent_recyclerview_items.xml .
Below is the XML code to be implemented.
Child RecyclerView layout items
We have designed the parent View and the items that belong to it, including the child RecyclerView. We need to add the items to the child RecyclerView. These are the items that will be scrollable horizontally in each child RecyclerView widget that we add. Create an XML file, name it child_recyclerview_items.xml , and include the layout below.
Setting up Model classes
These are Java classes that utilize the necessary parameters for each item in the RecyclerView. This consists of getters and setters method declaration. A model class contains a collection of data/objects that we want to show in the RecyclerView.
The application will have two model classes.
- Model one: contains information about the child items.
This data class stores the Movie image and its name. The model contains one text view for the title and one image as the movie hero image.
- Model two: contains information about the parent items.
This is a text view for the movie category. The parent item also consists of an array list of the child items. We will go over how to bind them using a RecyclerView adapter holder.
Setting up Adapter classes
Adapters are used to set and pass data that will be displayed in the respective RecyclerView items. The adapter manages the collection of items (defined in an individual layout) and ViewHolder objects. ViewHolder defines the individual views (widgets) in the layout and populate the components into an individual row layout. An onBindViewHolder class will bind the views with their respective data objects.
Technically, the data could come from a database or a remote server if the information to be passed to the recycling view is hosted remotely. To keep this app straight to the point, we will use dummy/locally available information and then pass them to the respective RecyclerView Adapters.
Let’s populate this data into the respective views. We will have two view adapters for each of the RecyclerView widgets. Create the two adapters.
- Adapter one: ChildRecyclerViewAdapter.java . This adapter will hold the information that belongs to the child widget.
We have passed the movie name and hero image to the respective item views as delegated in the ViewModel class. Our main focus is a nested RecyclerView. The data passed to ChildRecyclerViewAdapter will be an item of ParentRecyclerViewAdapter . The ViewHolder of the child adapter will be explicitly accessible without a binding view. Go ahead and create a parent adapter. Call it ParentRecyclerViewAdapter
- Adapter two: ParentRecyclerViewAdapter . The adapter will hold the information to be displayed in the parent layout, including the child RecyclerView.
As you can see, we are binding the information of the ChildRecyclerViewAdapter to the ParentRecyclerViewAdapter . We have set different data categories. In this case, each category contains its distinct data. The ArrayList binds to the parent RecyclerView. In the onBindViewHolder , we will set up the child RecyclerView by:
- Initializing and setting the LinearLayoutManager (create an instance of the layout manager) for the child RecyclerView. Here we specify that this view will be HORIZONTAL .
- Invoke the layout fixed size. This tells the layout not to move around the screen. It stabilizes the layout screen movements.
- Pass ArrayList of the child items to the respective ParentViewHolder as category.setText(currentItem.movieCategory()) of the parent RecyclerView. Each category will inflate with respective array list items consisting of a movie name and a hero image. Make sure to check the content of this project on GitHub to get all the necessary image resources for each movieCategory .
- Associate the child Adapter and the RecyclerView. This informs the RecyclerView which adapter it should work with.
Let’s set the necessary categories to display the movies to the main RecyclerView layout.
Initialize the parent adapter layout manager and inflate the view data into the application’s main RecyclerView by associating it with the ParentRecyclerViewAdapter .
Everything is now set. Run the application to test if everything is working.
I hope this guide helped you understand and implement nested RecyclerView within your application context. For more reference, check the code on GitHub.
Peer Review Contributions by: Odhiambo Paul
About the author
Joseph Chege is an undergraduate student taking a Bachelor in Business Information Technology, a 4th-year student at Dedan Kimathi University of Technology. Joseph is fluent in Android Mobile Application Development and has a lot of passion for back-end development.
Want to learn more about the EngEd Program?
Discover Section’s community-generated pool of resources from the next generation of engineers.
Источник
Реализация списка с заголовком, футером и пагинацией в Андроид
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 и выделении элементов
Содержание
1. Немного о ViewHolder’ах
До выхода в свет Android SDK 5.0 Lollipop для отображения списков и таблиц использовались виджеты ListView и GridView. Общей рекомендацией при работе с этим виджетом было использование паттерна ViewHolder. Суть паттерна заключается в том, что для каждого элемента списка создаётся объект, хранящий ссылки на отдельные вьюхи внутри элемента. Таким образом, приходится выполнять достаточно дорогой вызов findViewById(int) только один раз при создании элемента.
Пример типичного ViewHolder’а прямиком из руководств гугла:
Cсылка на такой холдер для каждого элемента сохраняется в корневом layout’е, используя метод setTag(int, Object) (с моей точки зрения тот ещё костыль).
2. Вкратце о RecyclerView
К выходу Android SDK 5.0 Lollipop разработчиков Google наконец-то озарило, что два вышеперечисленных виджета морально устарели и нужно бы заменить их на нечто более стильное, модное и молодёжное. Было принято решение не переделывать старые виджеты, а написать новый. Так и появился на свет RecyclerView. Так в чём же его отличия, спросите вы?
Я приведу вкратце основные, а для более полного их раскрытия советую к ознакомлению вот эту статью на хабре. Итак:
- Сам виджет больше не берёт на себя обязанность по размещению элементов. Для этого появились LayoutManager’ы.
- Паттерн ViewHolder стал обязательным. Причём виджет научился заново использовать уже созданные ViewHolder’ы и удалять уже не используемые (отсюда и название), что благоприятно сказывается на быстродействии и размере используемой памяти.
- Новый, удобный способ работы с анимацией.
Я попробовал его и виджет оставил у меня противоречивые впечатления. С одной стороны, да, здорово, что теперь использование ViewHolder’а является обязательным, работает вроде тоже быстрей, памяти жрёт меньше. С другой стороны, есть проблемы со сложностью и недоделанностью виджета.
Что я понимаю под сложностью? Если что-то не работало в ListView (или работало не так как задумано) всегда можно было залезть в исходники, разобраться, в чём ошибка, исправить её, подоткнуть костылей тут и там и всё начинало работать. RecyclerView гораздо сложнее в плане логики работы, мозг сломаешь, пока разберёшься. Я пытался, но забросил, уж слишком много времени и сил для этого нужно.
Вторая проблема — банальное отсутствие функционала, присутствовавшего в ListView и GridView. За примерами далеко ходить не надо — стандартный функционал выделения элементов (дальнейшая тема этой статьи), отступы между элементами. Раньше, чтобы добавить всё это, нужно было написать буквально пару строчек кода, теперь на это уйдут уже десятки строк. Есть анимации, но только для добавления/удаления/редактирования элемента. Если же вы хотите, например, анимировать частичное изменение элемента, то к вам в дверь уже стучится птица обломинго. Виджет не поддерживает анимацию части элемента, и если анимировать элемент извне (из адаптера, например), то лучше этого не делать — подобные манипуляции оставляют элементы виджета (те самые ViewHolder’ы) в неопределённом состоянии, что приводит к совершенно фантастическому поведению вашего списка.
Резюмируя — если у вас в проекте используются старые виджеты и вы не используете анимации, то лучше пока оставить всё как есть и дождаться, когда виджет наполнят отсутствующим функционалом. Если же вы хотите простые анимации и при этом взаимодействие пользователя с виджетом подразумевается простое — попробуйте RecyclerView, вдруг понравиться.
3. Выделяем элементы
Итак, перейдём к главному — к технической части статьи. Поговорим о том, как выделять элементы в RecyclerView. Сразу оговорюсь — идея реализации почерпнута из замечательной серии статей Билла Филлипса про RecyclerView (ссылки в конце), так что всё нижеследующее можно считать вольным кратким пересказом.
В ListView для выделения элементов использовался метод setChoiceMode(int), RecyclerView же понятия не имеет, что элементы могут выделяться, поэтому мы должны научить этому наш адаптер.
Схема такая:
На диаграмме я схематично обозначил связи между объектами. Пунктирные стрелки — ссылки, остальные — вызовы методов. Зелёным я обозначил объекты, которые непосредственно реализуют логику выделения.
Принцип работы получается следующий:
- ViewHolderWrapper устанавливает себя в качестве ClickListener’а для корневой вьюхи ViewHolder’а и начинает получать события onClick и onLongClick. В зависимости от реализации он может просто проксировать эти события в HolderClickObservable (ViewHolderClickWrapper), либо, исходя из текущего статуса SelectionHelper’а выделять элемент вызовом setItemSelected(ViewHolder, boolean) (ViewHolderMultiSelectionWrapper).
- SelectionHelper сохраняет информацию о выделенных элементах и оповещает слушателей (SelectionObserver) об изменении выделения.
- Слушатель (в нашем случае адаптер) отвечает за визуальное отображение выделения элемента, а также взаимодействия с ним (на диаграмме вызов startActionMode у Activity).
В самом адаптере необходимо сделать следующие изменения:
1. Создать SelectionHelper и зарегистрировать слушателей (в данном случае сам адаптер, но это может быть и Activity, например)
2. Обернуть создаваемые ViewHolder’ы во ViewHolderWrapper нужного типа.Метод wrapSelectable(ViewHolder) у SelectionHelper’а:
3. Не забывать прицеплять наши ViewHolder’ы к SelectionHelper’у в методе onBindViewHolder(ViewHolder, int) нашего адаптера!
Это нужно по причине того, что пока нет другого способа получить от RecyclerView список используемых в настоящий момент ViewHolder’ов. Если не вести их учёт, при необходимости обновить отрисовку выделения у всех выбранных элементов (пользователь закрыл ActionMode, например), SelectionHelper просто не сможет этого сделать. Вьюхи останутся выглядеть выделенными, когда по факту таковыми не будут.
Вы спросите — «А почему бы просто не запоминать выделяемые ViewHolder’ы в методе setItemSelected(ViewHolder, boolean)?». Тут как раз сказывается особенность RecyclerView — он использует заново уже созданные ViewHolder’ы.
Выглядит это примерно так:
- Открываем приложение. На экране 10 элементов — 10 ViewHolder’ов создано для них.
- Запускаем ActionMode, начинаем выделять элементы — 1,2,3.
- Прокручиваем вьюху вниз, видим элементы с 10 по 20. Думаете, что теперь в памяти висит 20 ViewHolder’ов? Как бы ни так! Для части данных RecyclerView создаст новые ViewHolder’ы, а для другой заново использует уже имеющиеся. Причём неизвестно в каком порядке.
- Теперь если мы прокрутим вьюху обратно вверх, часть из наших 10 ViewHolder’ов будет уничтожена, вместо них будут созданы новые. Оставшаяся часть будет использована заново и совершенно не обязательно для тех же позиций.
- Отменяем ActionMode. SelectionHelper должен раскидать слушателям уведомления о сменившемся выделении на элементах с указанием ViewHolder’а для каждого элемента, но он уже не владеет актуальными данными, все Holder’ы поменялись!
В результате это приведёт к тому, что часть элементов останется отображаться с выделением.
И здесь становится очевидным ещё один важный момент — нельзя сохранять строгие ссылки (strong reference) на ViewHolder’ы! Они могут быть удалены из RecyclerView в зависимости от фазы Луны и желания левой пятки Ларри Пейджа. В этом случае, если мы будем хранить строгие ссылки на них, случится утечка памяти. Поэтому для хранения ссылок в ViewHolderWrapper и WeakHolderTracker используются только WeakReference.
4. Также важно не забыть в onBindViewHolder(ViewHolder, int) визуально отобразить выделение если оно есть (если нет — не забыть убрать!). Вы же помните, что для не выделенного элемента может быть использован ViewHolder, ранее использовавшийся для не выделенного и наоборот?
У меня это реализовано следующим образом:
4.1. SelectableRecyclerViewAdapter.onBindViewHolder(ViewHolder, int)
4.2. layout-файл для элемента
CheckableAutofitHeightFrameLayout добавляет к FrameLayout всего 2 вещи: во-первых, он всегда квадратный (смотри onMeasure(int, int)) и, во-вторых, добавляет к DrawableStates (те самые, которые используются в xml) состояние state_checked. В результате, для отображения выделения у такого layout’а можно использовать StateListDrawable на вроде этого:и все детали отображения уползают в xml-ки, в Java только нужно установить соответствующие состояния.
5. Передать событие onSelectableChanged(boolean) в Activity и запустить ActionMode:
Как вы видите, при запуске ActionMode, она регистрирует себя как SelectionObserver. Таким образом, можно обновлять количество выделенных элементов в заголовке. Не забудьте вызвать unregisterSelectionObserver(SelectionObserver) при закрытии!
4. Заключение + Бонус
Кажется, с выделением разобрались. Весь исходный код также можно посмотреть на GitHub.
В заключение вкратце приведу ещё несколько фишек для работы с RecyclerView, которые вы можете найти в примере.
1. Если не нужно выделять элементы, а нужно просто обрабатывать нажатия, вместо ViewHolderMultiSelectionWrapper оборачивайте элементы в ViewHolderClickWrapper методом wrapClickable(ViewHolder). Сам адаптер в таком случае будет выглядеть примерно так:
Виджет подбирает ширину столбцов в зависимости от параметра columnWidth. Важный момент: если доступная ширина 330 пикселей, а мы передадим желаемую ширину 100, в итоге в таблице будет 3 столбца по 110 пикселей и элементы будут этой ширины. Именно поэтому я также сделал CheckableAutofitHeightFrameLayout автоматически изменяющим свою высоту в зависимости от ширины.
3. Для добавления отступов между элементами можно выставить paddingTop/Left у RecyclerView и marginRight/Bottom у элементов, однако это выглядит как костыль. Рекомендуемым способом является добавление ItemDecoration к RecyclerView. В примере можно найти несколько. Для добавления отступов к обычному GridLayoutManager (под «обычным» я имею ввиду GridLayoutManager со стандартным SpanSizeLookup, в нём каждый элемент занимает 1 span) можно использовать
Источник