[Разработка мобильных приложений, Разработка под Android] Создание сложного списка элементов за 20 минут в Android на базе Groupie
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Списки являются основным способом представления различного контента в мобильных приложениях. Будь то социальная сеть, приложение для чтения книг или интернет-магазин, в большинстве таких приложений встречаются списки с разными видами ячеек, разного уровня вложенности. Самый наглядный пример, известный любому Android-разработчику — это приложение Google Play, начальный экран которого является сложным списком с множеством вложенных элементов:
Естественно, чтобы поддержать такое разнообразие контента и при этом сохранить оптимальную производительность для такой задачи лучше всего использовать RecyclerView. Как создать список, поддерживающий разные виды ячеек, которые в свою очередь могут тоже содержать вложенные списки за 20 минут, я покажу на примере в этой статье.
В конце у вас получится вот такой список:
Итак, задача: создать список для отображения различного вида контента, при этом каждая категория, то есть ячейка списка может содержать неограниченное количество более мелких ячеек и иметь горизонтальный скрол. Звучит сложно? Если вы думаете что это сложная задача, над которой вам нужно будет работать всю неделю, то спешу вас обрадовать, мы сделаем такой список примерно на полчаса.
Подходов к решению такой задачи, множество, но суть решения одна — здесь необходимо использовать RecyclerView с различными типами ячеек, в которых также находится RecyclerView для возможности горизонтального скролла неограниченного количества ячеек. Можно использовать как стандартный подход, в котором необходимо будет создать adapter для каждого из списков, ViewHolders для разного типа ячеек и так далее. А можно использовать более быстрый подход без множества похожего кода на базе библиотеки Groupie
Groupie is a simple, flexible library for complex RecyclerView layouts.
Это простая и в тоже время мощная библиотека для упрощения построения списков на базе RecyclerView, которая заметно ускорит разработку сложных списков как в примерах выше.
Безусловно, все что мы видели можно сделать и без этой библиотеки, тем более совсем недавно в Android появился MergeAdapter (если будет интересно, то в следующей статье напишу и про него). Но так или иначе вы столкнётесь с недостатками стандартного подхода, описанного в той же статье. Поэтому, сегодня мы попробуем новый подход, ускоряющий разработку, избавляющий от написания бойлерплейт-кода и соответсвующий принципам SOLID.
Если кратко, то алгоритм действий выглядит следующим:
- Создаём проект. Добавляем нужные зависимости.
- Определяем нужные ячейки. Создаём layouts для отображения UI
- Соединяем ячейки с адаптером RecyclerView и наслаждаемся результатом.
Создание проекта и добавление библиотек.
Для создания списка как в примере на картинке нам понадобится 4 библиотеки: RecyclerView, CardView, Picasso (для отображения картинок) и Groupie. Добавим всё это в build.gradle(app):
implementation 'com.xwray:groupie:2.8.0'
implementation 'com.xwray:groupie-kotlin-android-extensions:2.8.0'
implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation 'com.squareup.picasso:picasso:2.71828'
implementation 'androidx.cardview:cardview:1.0.0'
Кроме этого, добавьте в build.gradle в блок android
androidExtensions {
experimental = true
}
Нажмите Sync Now для скачивания необходимых зависимостей.
Создание ячеек для отображения контента
Для отображения списка нам понадобится 3 типа ячеек:
- Общая ячейка — контейнер для отображения вложенного списка. Обозначена красным прямоугольником.
- Ячейка внутри основной ячейки для отображения информации о фильме. Такие ячейки выделены синим прямоугольником. Они находятся внутри основной ячейки в RecyclerView c горизонтальным скролом.
- Квадратная ячейка для отображения обложек игр. Выделена зелёным цветом.
Создание главной ячейки с вложенным RecyclerView
Вначале создадим общую ячейку с вложенным RecyclerView для отображения более мелких ячеек.
Вёрстка такой ячейки будет состоять из CardView с LinearLayout для отображения названия, описания и RecyclerView для отображения внутренних ячеек.
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_content_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:background="#FFFFFF"
app:cardCornerRadius="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFFFFF"
android:orientation="vertical">
<TextView
android:id="@+id/title_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:textSize="18sp"
android:textStyle="bold"
tools:text="Заголовок карточки" />
<TextView
android:id="@+id/description_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:textSize="12sp"
tools:text="Описание карточки" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/items_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="12dp"
android:layout_marginBottom="16dp"
android:orientation="horizontal"
android:visibility="visible"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:ignore="RtlSymmetry" />
</LinearLayout>
</androidx.cardview.widget.CardView>
Теперь создадим логику для отображения данных ячейки.
class MainCardContainer(
private val title: String? = "",
private val description: String? = "",
private val onClick: (url: String) -> Unit,
private val items: List<Item>
) : Item() {
override fun getLayout() = R.layout.item_card
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
viewHolder.title_text_view.text = title
viewHolder.description_text_view.text = description
viewHolder.items_container.adapter =
GroupAdapter<GroupieViewHolder>().apply { addAll(items) }
}
}
Каждая ячейка при использовании Groupie должна быть наследником от абстрактного класса Item. Для этого необходимо переопределить всего 2 метода getLayout() и bind(). То есть для создания ячейки вам нужно указать layout который будет использоваться для отображения UI и дописать логику формирования данных для этой ячейки и всё! Теперь не нужно писать однотипные адаптеры для разных ячеек или комбинировать множество разных типов ячеек в одном адаптере, нарушая принципы SOLID. Ну или выдумывать базовые классы для ячеек, только для того, чтобы можно было переиспользовать один и тот же адаптер. C Groupie для каждой ячейки вам необходимо создать свой класс, и описать в нем UI!
В данном пример мы будем использовать одну общую ячейку, которая на вход принимает название, описание и список других Item, то есть ячеек, которые и будут наполнять вложенный в эту ячейку RecyclerView. Самое интересное тут, пожалуй вот эта строчка:
viewHolder.items_container.adapter =
GroupAdapter<GroupieViewHolder>().apply { addAll(items) }
То есть для RecyclerView который внутри этой ячейки, необходимо добавить общий GroupAdapter и наполнить его ячейками, которые являются наследниками Item.
Общий контейнер готов, теперь осталось сверстать частные ячейки для каждого типа контента.
Их будет 2:
- Ячейка для фильма с названием фильма
- Квадратная ячейка с обложкой игры
Ячейка для фильма
Ячейка для фильма, также должна быть наследником Item и должна реализовать 2 метода:
class MovieItem(private val content: MovieContent) : Item() {
override fun getLayout() = R.layout.item_with_text
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
viewHolder.description.text = content.title
Picasso.get()
.load(content.url)
.into(viewHolder.image_preview)
}
}
Верстка достаточно простая и код можно посмотреть в проекте на GitHub.
Квадратная ячейка для отображения обложки игры
Эта ячейка тоже является достаточно простой, поэтому лучше посмотрите код проекта.
Все вместе. Соединяем все ячейки вместе
Для создания списка теперь нужно создать ячейки с контентом и передать их в адаптер RecyclerView. Для создания ячеек были созданы 2 метода getPopularMovies() и getPopularGames() которые возвращают ячейки типа Item.
private fun getPopularMovies(): Item {
return MainCardContainer(
"Список фильмов", "Лучшие фильмы", ::onItemClick,
listOf(
MovieItem(
MovieContent(
"Джокер",
"https://upload.wikimedia.....jpg"
)
),
MovieItem(
MovieContent(
"Бойцовский клуб",
"https://upload.wikimedia.org......jpg"
)
)
)
)
}
Каждый из методов возвращает 1 ячейку MainCardContainer — которой передаётся в качестве аргумента список ячеек уже с контентом для вложенного RecyclerView. Например, для ячейки которая отображает список фильмов нужно указать список ячеек MovieItem. Для второй ячейки, которая отображает список игр — мы создадим также метод, который создаст основную общую ячейку и передаст ячейки с играми.
В итоге создание списка теперь будет выглядеть так:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val movies = listOf(getPopularMovies(), getPopularGames())
items_container.adapter =
GroupAdapter<GroupieViewHolder>().apply { addAll(movies) }
}
Последняя строка как раз использует GroupAdapter в который мы можем передать любые ячейки, которые являются наследниками Item.
Ну вот и всё! Буквально за 20 минут мы создали сложный список для отображения различного типа контента с вложенным горизонтальным списком! Сравните такой подход с традиционным и сделайте выводы сами! Абсолютно точно, такой подход сэкономит вам время на разработку подобных UI — компонентов и избавит от кучи бесполезного кода. А в телеграм-канале @android_school_ru вы сможете найти ещё больше интересных туториалов и бесплатных мини-курсов.
===========
Источник:
habr.com
===========
Похожие новости:
- [Flutter, Конференции, Разработка мобильных приложений] Анонс вебинара «Flutter vs технология, на которой пишете вы: за чем будущее?»
- [Java, Разработка мобильных приложений, Разработка под Android, Kotlin, Gradle] Встраиваем карты от Huawei в Android приложение
- [Разработка под Android, Тестирование мобильных приложений, Kotlin] Паттерн PageObject в Kotlin для UI-тестирования Android (перевод)
- [Разработка под iOS, Разработка мобильных приложений, Xcode, Swift] Мой Covid-19 lockdown проект, или, как я полез в кастомный UICollectionViewLayout и получил ChatLayout
- [Gradle, Java, Kotlin, Разработка мобильных приложений, Разработка под Android] Встраиваем геолокацию от Huawei в Android приложение
- [Разработка мобильных приложений, Разработка под Android, Kotlin, Учебный процесс в IT] Android Academy Fundamentals: теперь прямо у тебя дома
- [Программирование, Разработка под Android] Избегайте внедрения внешних библиотек в свой проект
- [Разработка под iOS, Разработка мобильных приложений, Swift, Аналитика мобильных приложений] Автоматизация тестирования продуктовой аналитики в мобильных приложениях
- [Разработка мобильных приложений, Проектирование и рефакторинг, Dart, Flutter] Flutter + чистая архитектура: разбираем на примере
- [Разработка под Android] Автоматический Code Improvement при коммите в Android Studio
Теги для поиска: #_razrabotka_mobilnyh_prilozhenij (Разработка мобильных приложений), #_razrabotka_pod_android (Разработка под Android), #_android_development, #_recyclerview, #_groupie, #_mergeadapter, #_razrabotka_mobilnyh_prilozhenij (
Разработка мобильных приложений
), #_razrabotka_pod_android (
Разработка под Android
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 00:38
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Списки являются основным способом представления различного контента в мобильных приложениях. Будь то социальная сеть, приложение для чтения книг или интернет-магазин, в большинстве таких приложений встречаются списки с разными видами ячеек, разного уровня вложенности. Самый наглядный пример, известный любому Android-разработчику — это приложение Google Play, начальный экран которого является сложным списком с множеством вложенных элементов: Естественно, чтобы поддержать такое разнообразие контента и при этом сохранить оптимальную производительность для такой задачи лучше всего использовать RecyclerView. Как создать список, поддерживающий разные виды ячеек, которые в свою очередь могут тоже содержать вложенные списки за 20 минут, я покажу на примере в этой статье. В конце у вас получится вот такой список: Итак, задача: создать список для отображения различного вида контента, при этом каждая категория, то есть ячейка списка может содержать неограниченное количество более мелких ячеек и иметь горизонтальный скрол. Звучит сложно? Если вы думаете что это сложная задача, над которой вам нужно будет работать всю неделю, то спешу вас обрадовать, мы сделаем такой список примерно на полчаса. Подходов к решению такой задачи, множество, но суть решения одна — здесь необходимо использовать RecyclerView с различными типами ячеек, в которых также находится RecyclerView для возможности горизонтального скролла неограниченного количества ячеек. Можно использовать как стандартный подход, в котором необходимо будет создать adapter для каждого из списков, ViewHolders для разного типа ячеек и так далее. А можно использовать более быстрый подход без множества похожего кода на базе библиотеки Groupie Groupie is a simple, flexible library for complex RecyclerView layouts.
Безусловно, все что мы видели можно сделать и без этой библиотеки, тем более совсем недавно в Android появился MergeAdapter (если будет интересно, то в следующей статье напишу и про него). Но так или иначе вы столкнётесь с недостатками стандартного подхода, описанного в той же статье. Поэтому, сегодня мы попробуем новый подход, ускоряющий разработку, избавляющий от написания бойлерплейт-кода и соответсвующий принципам SOLID. Если кратко, то алгоритм действий выглядит следующим:
Создание проекта и добавление библиотек. Для создания списка как в примере на картинке нам понадобится 4 библиотеки: RecyclerView, CardView, Picasso (для отображения картинок) и Groupie. Добавим всё это в build.gradle(app): implementation 'com.xwray:groupie:2.8.0'
implementation 'com.xwray:groupie-kotlin-android-extensions:2.8.0' implementation "androidx.recyclerview:recyclerview:1.1.0" implementation 'com.squareup.picasso:picasso:2.71828' implementation 'androidx.cardview:cardview:1.0.0' Кроме этого, добавьте в build.gradle в блок android androidExtensions {
experimental = true } Нажмите Sync Now для скачивания необходимых зависимостей. Создание ячеек для отображения контента Для отображения списка нам понадобится 3 типа ячеек:
Создание главной ячейки с вложенным RecyclerView Вначале создадим общую ячейку с вложенным RecyclerView для отображения более мелких ячеек. Вёрстка такой ячейки будет состоять из CardView с LinearLayout для отображения названия, описания и RecyclerView для отображения внутренних ячеек. <?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main_content_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="8dp" android:background="#FFFFFF" app:cardCornerRadius="8dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#FFFFFF" android:orientation="vertical"> <TextView android:id="@+id/title_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:textSize="18sp" android:textStyle="bold" tools:text="Заголовок карточки" /> <TextView android:id="@+id/description_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginEnd="16dp" android:textSize="12sp" tools:text="Описание карточки" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/items_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="12dp" android:layout_marginTop="16dp" android:layout_marginEnd="12dp" android:layout_marginBottom="16dp" android:orientation="horizontal" android:visibility="visible" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" tools:ignore="RtlSymmetry" /> </LinearLayout> </androidx.cardview.widget.CardView> Теперь создадим логику для отображения данных ячейки. class MainCardContainer(
private val title: String? = "", private val description: String? = "", private val onClick: (url: String) -> Unit, private val items: List<Item> ) : Item() { override fun getLayout() = R.layout.item_card override fun bind(viewHolder: GroupieViewHolder, position: Int) { viewHolder.title_text_view.text = title viewHolder.description_text_view.text = description viewHolder.items_container.adapter = GroupAdapter<GroupieViewHolder>().apply { addAll(items) } } } Каждая ячейка при использовании Groupie должна быть наследником от абстрактного класса Item. Для этого необходимо переопределить всего 2 метода getLayout() и bind(). То есть для создания ячейки вам нужно указать layout который будет использоваться для отображения UI и дописать логику формирования данных для этой ячейки и всё! Теперь не нужно писать однотипные адаптеры для разных ячеек или комбинировать множество разных типов ячеек в одном адаптере, нарушая принципы SOLID. Ну или выдумывать базовые классы для ячеек, только для того, чтобы можно было переиспользовать один и тот же адаптер. C Groupie для каждой ячейки вам необходимо создать свой класс, и описать в нем UI! В данном пример мы будем использовать одну общую ячейку, которая на вход принимает название, описание и список других Item, то есть ячеек, которые и будут наполнять вложенный в эту ячейку RecyclerView. Самое интересное тут, пожалуй вот эта строчка: viewHolder.items_container.adapter =
GroupAdapter<GroupieViewHolder>().apply { addAll(items) } То есть для RecyclerView который внутри этой ячейки, необходимо добавить общий GroupAdapter и наполнить его ячейками, которые являются наследниками Item. Общий контейнер готов, теперь осталось сверстать частные ячейки для каждого типа контента. Их будет 2:
Ячейка для фильма Ячейка для фильма, также должна быть наследником Item и должна реализовать 2 метода: class MovieItem(private val content: MovieContent) : Item() {
override fun getLayout() = R.layout.item_with_text override fun bind(viewHolder: GroupieViewHolder, position: Int) { viewHolder.description.text = content.title Picasso.get() .load(content.url) .into(viewHolder.image_preview) } } Верстка достаточно простая и код можно посмотреть в проекте на GitHub. Квадратная ячейка для отображения обложки игры Эта ячейка тоже является достаточно простой, поэтому лучше посмотрите код проекта. Все вместе. Соединяем все ячейки вместе Для создания списка теперь нужно создать ячейки с контентом и передать их в адаптер RecyclerView. Для создания ячеек были созданы 2 метода getPopularMovies() и getPopularGames() которые возвращают ячейки типа Item. private fun getPopularMovies(): Item {
return MainCardContainer( "Список фильмов", "Лучшие фильмы", ::onItemClick, listOf( MovieItem( MovieContent( "Джокер", "https://upload.wikimedia.....jpg" ) ), MovieItem( MovieContent( "Бойцовский клуб", "https://upload.wikimedia.org......jpg" ) ) ) ) } Каждый из методов возвращает 1 ячейку MainCardContainer — которой передаётся в качестве аргумента список ячеек уже с контентом для вложенного RecyclerView. Например, для ячейки которая отображает список фильмов нужно указать список ячеек MovieItem. Для второй ячейки, которая отображает список игр — мы создадим также метод, который создаст основную общую ячейку и передаст ячейки с играми. В итоге создание списка теперь будет выглядеть так: override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val movies = listOf(getPopularMovies(), getPopularGames()) items_container.adapter = GroupAdapter<GroupieViewHolder>().apply { addAll(movies) } } Последняя строка как раз использует GroupAdapter в который мы можем передать любые ячейки, которые являются наследниками Item. Ну вот и всё! Буквально за 20 минут мы создали сложный список для отображения различного типа контента с вложенным горизонтальным списком! Сравните такой подход с традиционным и сделайте выводы сами! Абсолютно точно, такой подход сэкономит вам время на разработку подобных UI — компонентов и избавит от кучи бесполезного кода. А в телеграм-канале @android_school_ru вы сможете найти ещё больше интересных туториалов и бесплатных мини-курсов. =========== Источник: habr.com =========== Похожие новости:
Разработка мобильных приложений ), #_razrabotka_pod_android ( Разработка под Android ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 00:38
Часовой пояс: UTC + 5