Jetpack Compose для ленивых

Про Jetpack Compose на сегодняшний день слышал, пожалуй, каждый android-разработчик. Некоторым уже удалось «затащить его в прод», кто-то пробовал его в своих пет-проектах, а кто-то до сих пор сомневается в его целесообразности. Ведь на первый взгляд мы имеем все тоже самое:  заменили на Text (),  Box () очень похож на   и др. Единственное, что сразу произвело впечатление — новые Lazy-списки, которые являются заменой привычного RecyclerView и позволяют писать меньше кода. Но, опять же, скопировать и подправить адаптер под очередной экран это уже привычное действие и оно не занимает много времени, а само действие отработано до автоматизма. К числу сомневающихся и, возможно совсем немного lazy, можно было отнести и меня. Но несколько задач, отличных от рутинного перекрашивания кнопок, заставили меня пересмотреть свое отношение к Compose. 

Случай первый. Дано: макет в фигме следующего вида

29adb8f247763a175713986029403d8c.jpg

Если у вас, как и у меня в свое время, появились сомнения в увиденном, то скорее вы все поняли правильно. Необходимо реализовать таблицу с горизонтальным скроллом, при этом первая ячейка должна быть жестко фиксирована. Зачем? «Увидели, понравилось, хотим себе». 

Как такое делать я не знал. Stackoverflow выдал несколько решений, от которых я начал нервничать. В одном из них была реализация через 

. Когда про него вспоминали последний раз? Другие решения были на базе RecyclerView (уже лучше) — кастомные LayoutManager или декораторы. Но главная причина, по которой я отверг эти решения, — ячейки имели статические размеры, а в моем случае высота зависит от текста в первой колонке. Желания прокидывать размеры и пересчитывать высоты ячеек в списке у меня не было, и я снова вернулся к гуглу.

На этом этапе я решил не игнорировать ссылки, которые вели на статьи с Jetpack Compose. Оказывается в LazyColumn/LazyRow помимо item-элементов были добавлены (пока еще Experimental) stickyHeaders (https://developer.android.com/jetpack/compose/lists#sticky-headers). 

Собственно это та штука, которая «прилипает» к верху экрана при скролле. Данная находка открыла во мне второе дыхание. Я посчитал, что для реализации задуманного будет удобно ответ сервера представить ввиде нескольких списков, по списку на каждый столбец.

Тогда начало нашей Compose-функции (назовем ее по-простому DataTable) будет выглядеть так:

13155c90ca8ea9ae7a7b0ed215bbff13.jpg

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

6bc3d68f661db36ab49dd723b98b41ea.jpg

Время строить таблицу:

(я Lazy, а еще и Sticky!)

(я Lazy, а еще и Sticky!)

2c57ae5f5f9dd462a7e1a23bbc24f5e4.jpg

Используется LazyRow, так как нужен именно горизонтальный скролл. В stickyHeader задаем фиксированный столбец. В itemsIndexed будем отрисовывать остальные столбцы с «галочками». Если запустить наш код на данном этапе, то экран будет выглядеть вот так:

e44dd9b81125e62d74775ecb2cecd336.jpg

Некрасиво.

Как видно, изначальная проблема с высотами ячеек при таком подходе все равно остается, поэтому поищем способ ее решения. У Modifier в доступных функциях есть колбэк onGloballyPositioned. Он возвращает координаты элемента на экране после его отрисовки и через них мы будем находить высоту. Для «прокидывания» высот в другие ячейки я использовал mutableState:

8befed767e785f13cc5a329983af9585.jpg

Ключ — индекс элемента (можно позволить, поскольку кол-во ячеек во всех столбцах одинаково), значение — высота.

Конечный код LazyRow элемента будет иметь вид:  

eb08bf58ebb629a270efa1a0a5e770b0.jpg

Первым создается stickyHeader. После заполнения ячеек контентом, высчитываются высоты и сохраняются в mutableState. 

После отрисовываются столбцы с «галочками». Из mutableState берутся высоты. В верху столбца (для index == 0) ставится картинка.

Запускаем, получаем такой экран, радуемся результату:

Вот так ленивый список сэкономил кучу времени ленивому разработчику и дал мощный пинокзаряд мотивации для изучения Jetpack Compose.

Посмотреть как работает можно тут:  https://play.google.com/store/apps/details? id=su.art.spbrealty&hl=ru 

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

50ec8a968ef088c20a4b299fb6da5e0f.jpg

В stickyHeader можно поместить любую @Composable — функцию, а значит подход из первого случая будет работать и здесь. Список будет иметь вид:

89f2ae0b1070298374bb444a5a477b01.jpg

Сначала размещаем элементы, которые будут скрываться под тулбаром, затем stickyHeader, после — все остальное.

По итогу имеем такой экран:

Посмотреть можно здесь :  https://play.google.com/store/search? q=мой%20теле2&c=apps&hl=ru

Случай третий. Кнопка. В данном случае было необходимо разработать экран объявления. При создании макета дизайнеры вдохновлялись Авито, в частности поведением кнопки «написать автору объявления»:

b03dbadabe2e0c5408a17d9ea92bdea6.jpg

Длинный экран со скроллом. У кнопки «Написать» есть свое место на этом экране. Но если пользователь не дошел до этого места,   то кнопка должна быть видна поверх контента. 

Первой мыслью было поискать в документации stickyFooter, поскольку это как stickyHeader, но внизу. Но, к сожалению, разработчики Google не посчитали нужным такой элемент. И тут у меня появилась безумная идея. 

Что будет, если у LazyColumn задать параметр reverseLayout = true? Будет ли stickyHeader «липнуть» к низу экрана? Как оказалось — будет. Дело за малым: переворачиваем порядок в списке задом наперед и получаем желаемый вид экрана:

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

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

Статью подготовил Илья Кубышкин, Android- разработчик в e-legion

© Habrahabr.ru