Обычно новым фреймворкам не хватает хороших примеров использования, туториалов и инструментов. Но не в случае с Jetpack Compose: параллельно с разработкой фреймворка Google развивал репозиторий, богатый на примеры использования. Вырисовывался набор полезных утилит, переиспользуемых виджетов и прочих удобств для разработки.
Так из репозитория с примерами вырос Accompanist — набор библиотек для Jetpack Compose, которые можно добавить в свой проект как зависимость. В статье рассмотрим подробнее, какой инструментарий он предоставляет.
![](https://habrastorage.org/getpro/habr/upload_files/93d/889/acc/93d889acc3b4999908ddcd1226238099.png)
AppCompat Theme Adapter
В легаси-проекте тема может быть достаточно большой, иметь большое поддерево тем и кастомных атрибутов. В легаси-проектах обычно это AppCompat тема, в то время как Compose использует MaterialTheme. Поэтому нужен «мост» между двумя темами.
В составе Accompanist есть AppCompatTheme — удобная обёртка, которая перенесёт тему из мира легаси и XML в мир Compose. Исходники простые: их можно дополнить или сделать по образу свою реализацию, если в теме много кастомных параметров.
![](https://habrastorage.org/getpro/habr/upload_files/670/748/d0e/670748d0eea40ef15009f9bbc9882fdc.png)
Если проект уже использует Material Design Theme, то, согласно, документации, надо использовать MDC-Android Compose Theme Adapter из Material Design Components.
SystemUiController
SystemUiController — удобная обёртка над WindowInsetsControllerCompat. Помогает из кода управлять свойствами системных баров: видимостью и цветом. Незаменимая вещь, если вы поддерживаете в проекте API от версии 21 (Android 5.0).
![](https://habrastorage.org/getpro/habr/upload_files/6c1/bb5/761/6c1bb5761d47ba7c479e78a60fb9c4e8.gif)
Insets
Insets — обёртка над WindowInsetsCompat и набор расширений для совместимости с Compose. Поможет подписаться на изменения Inset’ов.
![](https://habrastorage.org/getpro/habr/upload_files/af0/e08/ce6/af0e08ce69be7d707a025ddbd78472a0.gif)
Кроме параметров из WindowInsetsCompat, можно использовать расширения для Modifier.
Для работы с padding’ами:
![](https://habrastorage.org/getpro/habr/upload_files/c1a/e3c/c11/c1ae3cc1100895ee78c16c9b6987d1f9.png)
Для работы с размерами:
![](https://habrastorage.org/getpro/habr/upload_files/26e/d49/59c/26ed4959ce82981e221aeea9199ad0c8.png)
Permissions
Работа с опасными разрешениями требует некоторых усилий. Нужно:
Запросить разрешение у пользователя.
С помощью кастомного диалога (view, snackbar и других) объяснить пользователю, зачем нужно разрешение.
Правильно обработать ответ пользователя.
Предоставить возможность перейти в настройки приложения, чтобы пользователь мог принять решение, какие доступы дать.
Разработчики Accompanist проделали большую работу, чтобы работать с разрешениями было удобно. И что самое классное, они сделали запрос разрешений через ActivityResultLauncher: никакого больше переопределения onActivityResult в Activity, и теперь явно прописывается контракт для запроса разрешения.
API достаточно простое.
![](https://habrastorage.org/getpro/habr/upload_files/92a/0f3/796/92a0f37961bf855d9414823fb1679634.png)
permissionState — состояние с параметрами разрешения. Кроме boolean флагов, нужен для запроса разрешения.
permissionNotGrantedContent — контент для состояния, когда нет разрешения.
permissionNotAvailableContent — контент для состояния, когда разрешение нельзя запросить. Тогда пользователя надо направить в настройки приложения.
content — контент для состояния, когда пользователь дал разрешение.
![Пара скриншотов с примерами работы из семпла Пара скриншотов с примерами работы из семпла](https://habrastorage.org/getpro/habr/upload_files/4be/3fa/1d1/4be3fa1d1daa26da1504509e3108ec90.png)
FlowLayout
FlexBoxLayout — контейнер для гибкой вёрстки. С ним удобно располагать элементы по горизонтали или вертикали: например, чтобы отобразить коллекцию элементов. Он сам рассчитает размеры и поймёт, как лучше перенести элемент на следующую строку или столбец.
![](https://habrastorage.org/getpro/habr/upload_files/5a5/674/7ac/5a56747ac09831ff8a33d155e014ee23.gif)
FlexBox для Android — не родной контейнер для вёрстки. Первую версию FlexBoxLayout для CSS представили в 2009 году. До мира Android-разработки он добрался сильно позже: в 2016 году вышла первая alpha версия. С того времени вышло немало релизов, самый свежий — 3.0.0.
Также вышел FlexBoxLayoutManager для RecyclerView. Вещь удобная, и она не могла не появиться в Compose. Только назвали контейнер по-другому: FlowLayout. Название не менее лаконичное — и так же точно передаёт суть его работы.
![](https://habrastorage.org/getpro/habr/upload_files/339/5c5/dfa/3395c5dfa8d6066cbbe0d15291c83bcc.png)
Как и его предки, имеет такие же параметры для вёрстки. Поэтому если вы с ними уже встречались ранее, то и в Compose верстать с его помощью будет несложно.
SwipeRefreshLayout
SwipeRefreshLayout — один из самых известных и используемых контейнеров в Android разработке.
Использовать его просто: в XML необходимо контейнер с контентом обернуть в SwipeRefreshLayout и в коде реализовать интерфейс OnRefreshListener — например, чтобы начать перезагрузку контента на экране. Вроде всё хорошо, больше ничего и не надо.
Трудности начинаются, если разработчику нужно кастомизировать внешний вид лоадера. Возможности кастомизации ограничены — несмотря на громоздкую реализацию (~1200 строк).
«Из коробки» можно изменить цвет стрелки, размер и появление индикатора. Если вам необходимо изменить цвет индикатора или (о боже) «а давайте как на iOS», то придётся искать стороннюю библиотеку или писать самостоятельно. Но и то, и другое занимает время и несёт риски.
Accompanist помогает решить эти проблемы.
![](https://habrastorage.org/getpro/habr/upload_files/b78/bcc/b87/b78bccb8790fb56da6dd73896d710ca8.gif)
В Accompanist отсутствует XML, но принцип использования не изменился: контент всё так же оборачиваем в SwipeRefresh и используем лямбду для действия при обновлении.
![](https://habrastorage.org/getpro/habr/upload_files/520/27b/f9d/52027bf9d2085c9ff690ff772cb65bb3.png)
Рассмотрим, что SwipeRefresh умеет «из коробки» и что с возможностью кастомизации.
![](https://habrastorage.org/getpro/habr/upload_files/d2b/da5/26f/d2bda526f698b0868746b4ae660474e9.png)
state — хранит состояние загрузки и прогресс анимации индикатора.
onRefresh — лямбда-триггер для старта, например, перезагрузки контента.
modifier — модификатор контейнера: размеров, отступов и так далее.
swipeEnabled — включение и выключение реакции контейнера на свайп.
refreshTriggerDistance задаёт расстояние до триггера анимации. Чем расстояние больше, тем позже индикатор отреагирует на свайп.
indicatorAlignment — аналог Gravity. Интересная функциональность, которая пригодится, скорее, при реализации кастомного индикатора.
indicatorPadding — простая кастомизация отступов индикатора. Можно сделать отступ с определённой стороны, можно равный со всех.
indicator — реализация индикатора. «Из коробки» есть стандартная реализация индикатора из material design. Можно модифицировать её или написать полностью свою.
clipIndicatorToPadding — флаг, позволяющий обрезать индикатор по отступу контента.
content — непосредственно сам контент внутри SwipeRefresh.
Примеры работы SwipeRefresh из семпла библиотеки Accompanist.
![](https://habrastorage.org/getpro/habr/upload_files/c59/64b/1b6/c5964b1b698ff4ef37d323d48fc55869.gif)
SwipeRefreshTweakedIndicatorSample
SwipeRefreshCustomIndicatorSample
SwipeRefresh уже «из коробки» предоставляет неплохие возможности кастомизации привычного Android-разработчикам SwipeRefreshLayout. Реализация контейнера и индикатора занимает примерно 600 строк.
Pager
На UI языке Pager — горизонтальный или вертикальный список, у которого каждый элемент занимает всю или почти всю область экрана. Ключевое отличие от обычного списка — Snap-эффект, то есть «доскрол» элемента.
![](https://habrastorage.org/getpro/habr/upload_files/dc2/23f/474/dc223f474ccfe060396a2a1bbc07c412.gif)
Pager, или ViewPager, имел несколько стадий развития как UI элемент в Android. Сначала был ViewPager, позже появился RecyclerView, и его приспособили под пейджинг. Сравнительно недавно появился ViewPager2, который под капотом использует тот же RecyclerView.
Давайте взглянем, как Pager может выглядеть на «языке» Compose. ViewPager, ViewPager2 и RecyclerView имеют общую черту: как UI-элементам для отображения коллекций, им нужен адаптер для заполнения данными. Pager на Compose этого лишён: взамен адаптера необходимо инициализировать PagerState. С него и начнём.
![](https://habrastorage.org/getpro/habr/upload_files/514/c17/b7e/514c17b7ef92f00d6394d2bb3f3ad547.png)
Параметров немного, а для инициализации обязательный всего один.
pageCount — количество элементов,
currentPage — текущая страница,
currentPageOffset — отступ страницы. Имеет значение от 0.0 до 1.0. Можно использовать для программного скролла страницы.
offscreenLimit — лимит страниц для подгрузки: количество подгружаемых элементов слева и справа от текущего элемента.
infiniteLoop — зацикленный скролл.
Сразу видно, какая функциональность «из коробки» самая крутая — конечно же, это зацикленный скролл. Сколько же было написано костылей для зацикливания ViewPager и RecyclerView :)
Accompanist предлагает две реализации для пейджера: HorizontalPager и VerticalPager. Если взглянуть в исходники, и то, и другое — просто обёртка на Compose функцией Pager. Разница только в инициализации параметра isVertical.
![](https://habrastorage.org/getpro/habr/upload_files/399/844/8b4/3998448b4d63c9154a84429b8fdbb68e.png)
Многие параметры могут быть знакомы по RecyclerView и ViewPager. Поэтому рассмотрим те, которые не встречаются у них «из коробки».
verticalAlignment — аналог Gravity. Используется для центровки элемента: например, чтобы элемент доскроливался не до центра, а до края контейнера.
horizontalAlignment — аналогично vercticalAligment, только для горизонтали.
flingBehavior — конфиг поведения скрола: настраивает анимацию, когда пользователь перестаёт скролить или бросает скрол. Эти параметры примечательны тем, что у ViewPager и RecyclerView они не настраиваются легко «из коробки». Для ViewPager придётся «шаманить» с параметрами адаптера и с самим ViewPager. У RecyclerView для доскрола используются наследники SnapHelper’а.
В коде всё выглядит достаточно просто: инициализируем PagerState и заполняем пейджер элементами.
![](https://habrastorage.org/getpro/habr/upload_files/fb9/478/c67/fb9478c67e9429bea6fdc7d101e374a5.png)
Часто пейджеры используются с индикаторами страниц, табами, а также трансформацией страниц при свайпе. И для этого у Accompanist’а тоже есть решение.
![](https://habrastorage.org/getpro/habr/upload_files/cbc/ee3/e15/cbcee3e15cb30d52e53182ae1e9a5895.gif)
Как я писал выше, состояние Pager’a хранится в PagerState. Для анимации индикаторов, трансформации страниц работы с табами его можно переиспользовать. Очень удобно: одно состояние для всех компонент — нет необходимости проставлять слушателей. Как бонус — получаем минимальную связность.
Coil
Coil (Coroutine Image Loader) — сравнительно новая библиотека: первая версия вышла в 2019 году. Написана на Kotlin и Coroutines — и в этом её преимущество перед Glide и Picasso. У неё простое и гибкое API, она легко интегрируется с проектами на Kotlin, а ещё умеет работать с Svg, Gif и видео.
До недавнего времени Accompanist тоже имел в арсенале интеграцию с Coil и Glide. С версии 0.15.0 этот инструментарий помечен как Deprecated: разработчики Coil добавили поддержку Compose и сделали это на основе Accompanist с небольшими изменениями. Поэтому далее код будет из примера работы Coil, а не Accompanist.
Чтобы понять, как происходит интеграция Coil и Compose, рассмотрим параметры элемента ImageView в Compose. Почти все могут быть знакомы Android-разработчику.
![](https://habrastorage.org/getpro/habr/upload_files/193/df5/f25/193df5f25b27651e43e90dd558fcaa24.png)
painter — сущность для отрисовки графики. Это может быть цвет, Bitmap или Vector.
contentDescription — cпециальный параметр для работы с Accessibility. Вы же хороший разработчик и не ставите туда null? :)
modifier — модификатор для выставления параметров элемента: размера, отступов и так далее.
alignment — аналог Gravity.
contentScale — аналог ScaleType из ImageView.
alpha — прозрачность для отрисовки painter.
colorFilter — цветовой фильтр для отрисовки painter.
Вся магия происходит в painter — точнее, в наследниках абстрактного класса Painter. Собственно, Coil для интеграции предоставляет свои реализации Painter’a.
CrossFadePainter отрисовывает cross fade переход между двумя Painter’ами.
![CrossFadePainter CrossFadePainter](https://habrastorage.org/getpro/habr/upload_files/93e/465/37d/93e46537de61cd8025477fcf2446a56b.png)
DrawablePainter — cамый простой представитель Painter’ов из библиотеки, но не менее полезный. Умеет отображать Drawable и AnimatableDrawable.
![DrawablePainter DrawablePainter](https://habrastorage.org/getpro/habr/upload_files/39e/1a2/eea/39e1a2eeac762e6fa476e602f650ba60.png)
ImagePainter умеет больше всех. Поддерживает загрузку String, Uri ("android.resource", "content", "file", "http", and "https"), HttpUrl, File, DrawableRes, Drawable, Bitmap.
ImageRequest и ImageLoader — базовые классы из Coil.
С помощью ImageRequest настраиваются параметры загрузки картинки: ссылка, листенеры, политика кеширования и декодинга.
ImageLoader запускает ImageRequest, содержит настройки для работы с изображениями. Как правило он настраивается один раз и переиспользуется во всём приложении.
![ImagePainter ImagePainter](https://habrastorage.org/getpro/habr/upload_files/42f/e90/104/42fe9010445101e2b35a3597587d61d2.png)
PlaceHolder
Skeleton-загрузчики уже давно стали стандартом для многих платформ. Во время загрузки пользователь видит псевдовёрстку экрана: по размеру и форме фигур он может предположить, где будет текст, где — фото, а где — кнопка. Это создаёт у пользователя позитивные впечатления.
Accompanist предлагает несколько реализаций placeholder’ов для создания skeleton загрузчиков:
Базовая — placeholder без анимаций.
Fade — placeholder c fade-анимацией.
Shimmer — placeholder c анимацией мерцания.
![](https://habrastorage.org/getpro/habr/upload_files/b4d/385/b28/b4d385b2896fed1391cd60d0f507eb2c.gif)
Accompanist — крутая поддержка для разработчиков на старте внедрения Compose
У Accompanist ещё не вышла версия 1.0, а инструментарий библиотеки уже достаточно богатый и поможет обойти кучу граблей.
Стоит заметить, что Accompanist — не единственная библиотека, которая поможет в поддержке и интеграции с Compose. Уже есть интеграция Compose c Hilt, Rx, Jetpack Navigation, Jetpack Paging. В Lottie 4.0.0 тоже добавили поддержку Compose.
quaer
Лучше бы усилия направили на качество и хорошую документацию, а не на количество.
Что толку от всех этих библиотек, если всё кривое и косое? Чуть не каждая попытка задейстовать что-то на Андроиде приводит к поиску обхода проблем и багов.
ozh-dev Автор
Да можно согласиться с вами, библиотека всё-таки версии 0.15 и до 1.0 далеко.
А вы тут документацию смотрели - https://google.github.io/accompanist/? Тут вроде по всем зависимостям библиотеки достаточно полно рассказывается.
Я считаю что баги не избежны сейчас, библиотека очень активно развивается. Вовремя написания статьи были задперекейчены зависимости для Coil и Glide, а через 4 часа после выхода статьи, вышла новая версия библиотеки.
quaer
Речь в принципе об экосистеме поверх которой всё это навёрнуто. Обёртка поверх обёртки. Почему бы не направить усилия на улучшение базы поверх которой всё это наворачивается? Вот буквально недавний пример: понадобился InputTextLayout. Казалось бы, куда стандартней? И тут же пришлось лезть искать почему бордюр рисуется поперёк текста метки. Пока пришлось убрать, потому что решения не нашлось.