[Разработка мобильных приложений, Разработка под Android] Делаем код в адаптере чище с помощью MergeAdapter
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Надоели перегруженные и сложные адаптеры в вашем проекте, напоминающие картинку ниже? Каждый раз, при добавлении нового типа ячейки хочется переписать адаптер для RecyclerView, чтобы код читался проще? Есть множество подходов, чаще всего рекомендуется использовать подход delegate adapter или, например библиотеку для динамического создания списков с различными типами view как groupie о работе с которой вы можете ознакомиться в этой статье. Но сегодня расскажем о новом классе, который поможет инкапсулировать логику вашего адаптера для разных ячеек тем самым соответствовать принципам SOLID.
MergeAdapter — новый класс, появившейся в recyclerview:1.2.0-alpha02, который позволит объединить несколько адаптеров для отображения в едином RecyclerView. Это позволит инкапсулировать логику для каждой ячейки в своём адаптере, и позволит переиспользовать её в будущем.
Проблема
Начнем с примера. Предположим, у нас есть задача отобразить ленту с двумя типами данных — текст с описанием и картинка. Код в методе onCreateViewHolder в самом распространённом случае будет выглядеть так:
override fun onCreateViewHolder(
parent: ViewGroup, viewType: Int
): RecyclerView.ViewHolder? {
val holder: RecyclerView.ViewHolder
val inflater = LayoutInflater.from(parent.context)
when (viewType) {
TEXT_VIEW_TYPE -> {
holder = TextViewHolder(
inflater.inflate(R.layout.text_item, parent, false)
)
}
IMAGE_VIEW_TYPE -> {
holder = ImageViewHolder(
inflater.inflate(R.layout.image_item, parent, false),
imageClickListener
)
}
else -> {
throw IllegalArgumentException(
"Can't create view holder from view type $viewType"
)
}
}
return holder
}
Чем это плохо? Минус такой реализации в нарушении принципов DRY и SOLID (single responsibility и open closed). Чтобы в этом убедиться, достаточно добавить два требования: ввести новый тип данных (чекбокс) и еще одну ленту, где будут только чекбоксы и картинки.
Перед нами встает выбор — использовать этот же адаптер для второй ленты или создать новый? Независимо от решения, которое мы выберем, нам придется менять код (об одном и том же, но в разных местах). Надо будет добавить новый VIEW_TYPE, новый ViewHolder и отредактировать методы: getItemViewType(), onCreateViewHolder() и onBindViewHolder().
Если мы решим оставить один адаптер, то на этом изменения закончатся. Но если в будущем новые типы данных с новой логикой будут добавляться только во вторую ленту, первая будет иметь лишний функционал, и ее тоже нужно будет тестировать, хотя она не изменялась.
Если решим создать новый адаптер, то будет просто масса дублирующего кода.
Решение
Новый класс MergeAdapter, позволяет комбинировать разные адаптеры для разного вида ячеек. Например, очень частый кейс использования — отображать spinner на время загрузки данных в ленте, а если, вдруг произошла ошибка загрузки — отображать ячейку с ошибкой в конце ленты.
Решением данной задачи, может быть использование MergeAdapter Предположим, у нас есть 3 адаптера:
val firstAdapter: FirstAdapter = …
val secondAdapter: SecondAdapter = …
val thirdAdapter: ThirdAdapter = …val mergeAdapter = MergeAdapter(firstAdapter, secondAdapter, thirdAdapter)
recyclerView.adapter = mergeAdapter
RecyclerView отобразит элементы каждого адаптера последовательно, в том же порядке, в котором переданы в конструктор. Разные адаптеры позволяют разделить логику для разных ячеек в списке. Например, если необходимо добавить заголовок к списку, не нужно реализовывать эту логику в адаптере, который отвечает за отображение главного контента в списке, можно разделить адаптеры, для разных видов ячеек. Такой подход помогает инкапсулировать логику и переиспользовать её в будущем для разных экранов.
Отображение загрузки в заголовке или внизу списка.
Для отображения статуса загрузки вверху или внизу списка нужно добавить адаптеры соответственно:
val mergeAdapter = MergeAdapter(headerAdapter, listAdapter, footerAdapter)
recyclerView.adapter = mergeAdapter
Верхняя ячейка и нижняя используют тот же layout, ViewHolder и UI логику (отображение статуса загрузки и скрытие). Вообще, достаточно было бы использовать 2 экземпляра одного и того же адаптера для верха и низа списка. Пример можно посмотреть тут или тут
Если кратко, то вот таким простым способом вы можете улучшить код вашего проекта, если вы используете сложный адаптер с различными типами ячеек.
Понравилась статья? Не забудьте присоединиться к нам в Telegram, а на платформе AndroidSchool.ru публикуются полезные материалы для Android-разработчика и современные туториалы.
===========
Источник:
habr.com
===========
Похожие новости:
- [Разработка мобильных приложений, Разработка под Android] Создание сложного списка элементов за 20 минут в Android на базе Groupie
- [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 + чистая архитектура: разбираем на примере
Теги для поиска: #_razrabotka_mobilnyh_prilozhenij (Разработка мобильных приложений), #_razrabotka_pod_android (Разработка под Android), #_android_recyclerview_mergeadapter, #_razrabotka_mobilnyh_prilozhenij (
Разработка мобильных приложений
), #_razrabotka_pod_android (
Разработка под Android
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 01:09
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Надоели перегруженные и сложные адаптеры в вашем проекте, напоминающие картинку ниже? Каждый раз, при добавлении нового типа ячейки хочется переписать адаптер для RecyclerView, чтобы код читался проще? Есть множество подходов, чаще всего рекомендуется использовать подход delegate adapter или, например библиотеку для динамического создания списков с различными типами view как groupie о работе с которой вы можете ознакомиться в этой статье. Но сегодня расскажем о новом классе, который поможет инкапсулировать логику вашего адаптера для разных ячеек тем самым соответствовать принципам SOLID. MergeAdapter — новый класс, появившейся в recyclerview:1.2.0-alpha02, который позволит объединить несколько адаптеров для отображения в едином RecyclerView. Это позволит инкапсулировать логику для каждой ячейки в своём адаптере, и позволит переиспользовать её в будущем. Проблема Начнем с примера. Предположим, у нас есть задача отобразить ленту с двумя типами данных — текст с описанием и картинка. Код в методе onCreateViewHolder в самом распространённом случае будет выглядеть так: override fun onCreateViewHolder(
parent: ViewGroup, viewType: Int ): RecyclerView.ViewHolder? { val holder: RecyclerView.ViewHolder val inflater = LayoutInflater.from(parent.context) when (viewType) { TEXT_VIEW_TYPE -> { holder = TextViewHolder( inflater.inflate(R.layout.text_item, parent, false) ) } IMAGE_VIEW_TYPE -> { holder = ImageViewHolder( inflater.inflate(R.layout.image_item, parent, false), imageClickListener ) } else -> { throw IllegalArgumentException( "Can't create view holder from view type $viewType" ) } } return holder } Чем это плохо? Минус такой реализации в нарушении принципов DRY и SOLID (single responsibility и open closed). Чтобы в этом убедиться, достаточно добавить два требования: ввести новый тип данных (чекбокс) и еще одну ленту, где будут только чекбоксы и картинки. Перед нами встает выбор — использовать этот же адаптер для второй ленты или создать новый? Независимо от решения, которое мы выберем, нам придется менять код (об одном и том же, но в разных местах). Надо будет добавить новый VIEW_TYPE, новый ViewHolder и отредактировать методы: getItemViewType(), onCreateViewHolder() и onBindViewHolder(). Если мы решим оставить один адаптер, то на этом изменения закончатся. Но если в будущем новые типы данных с новой логикой будут добавляться только во вторую ленту, первая будет иметь лишний функционал, и ее тоже нужно будет тестировать, хотя она не изменялась. Если решим создать новый адаптер, то будет просто масса дублирующего кода. Решение Новый класс MergeAdapter, позволяет комбинировать разные адаптеры для разного вида ячеек. Например, очень частый кейс использования — отображать spinner на время загрузки данных в ленте, а если, вдруг произошла ошибка загрузки — отображать ячейку с ошибкой в конце ленты. Решением данной задачи, может быть использование MergeAdapter Предположим, у нас есть 3 адаптера: val firstAdapter: FirstAdapter = …
val secondAdapter: SecondAdapter = … val thirdAdapter: ThirdAdapter = …val mergeAdapter = MergeAdapter(firstAdapter, secondAdapter, thirdAdapter) recyclerView.adapter = mergeAdapter RecyclerView отобразит элементы каждого адаптера последовательно, в том же порядке, в котором переданы в конструктор. Разные адаптеры позволяют разделить логику для разных ячеек в списке. Например, если необходимо добавить заголовок к списку, не нужно реализовывать эту логику в адаптере, который отвечает за отображение главного контента в списке, можно разделить адаптеры, для разных видов ячеек. Такой подход помогает инкапсулировать логику и переиспользовать её в будущем для разных экранов. Отображение загрузки в заголовке или внизу списка. Для отображения статуса загрузки вверху или внизу списка нужно добавить адаптеры соответственно: val mergeAdapter = MergeAdapter(headerAdapter, listAdapter, footerAdapter)
recyclerView.adapter = mergeAdapter Верхняя ячейка и нижняя используют тот же layout, ViewHolder и UI логику (отображение статуса загрузки и скрытие). Вообще, достаточно было бы использовать 2 экземпляра одного и того же адаптера для верха и низа списка. Пример можно посмотреть тут или тут Если кратко, то вот таким простым способом вы можете улучшить код вашего проекта, если вы используете сложный адаптер с различными типами ячеек. Понравилась статья? Не забудьте присоединиться к нам в Telegram, а на платформе AndroidSchool.ru публикуются полезные материалы для Android-разработчика и современные туториалы. =========== Источник: habr.com =========== Похожие новости:
Разработка мобильных приложений ), #_razrabotka_pod_android ( Разработка под Android ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 01:09
Часовой пояс: UTC + 5