Pull to refresh

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

Level of difficultyMedium
Reading time4 min
Views5.9K

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

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

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

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

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

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

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

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

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

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

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

Некрасиво.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Tags:
Hubs:
Total votes 10: ↑10 and ↓0+11
Comments8

Articles