Обычно новым фреймворкам не хватает хороших примеров использования, туториалов и инструментов. Но не в случае с Jetpack Compose: параллельно с разработкой фреймворка Google развивал репозиторий, богатый на примеры использования. Вырисовывался набор полезных утилит, переиспользуемых виджетов и прочих удобств для разработки. 

Так из репозитория с примерами вырос Accompanist — набор библиотек для Jetpack Compose, которые можно добавить в свой проект как зависимость. В статье рассмотрим подробнее, какой инструментарий он предоставляет.

AppCompat Theme Adapter

В легаси-проекте тема может быть достаточно большой, иметь большое поддерево тем и кастомных атрибутов. В легаси-проектах обычно это AppCompat тема, в то время как Compose использует MaterialTheme. Поэтому нужен «мост» между двумя темами.

В составе Accompanist есть AppCompatTheme — удобная обёртка, которая перенесёт тему из мира легаси и XML в мир Compose. Исходники простые: их можно дополнить или сделать по образу свою реализацию, если в теме много кастомных параметров.

Если проект уже использует Material Design Theme, то, согласно, документации, надо использовать MDC-Android Compose Theme Adapter из Material Design Components.

SystemUiController

SystemUiController — удобная обёртка над WindowInsetsControllerCompat. Помогает из кода управлять свойствами системных баров: видимостью и цветом. Незаменимая вещь, если вы поддерживаете в проекте API от версии 21 (Android 5.0).

Insets

Insets — обёртка над WindowInsetsCompat и набор расширений для совместимости с Compose. Поможет подписаться на изменения Inset’ов.

Кроме параметров из WindowInsetsCompat, можно использовать расширения для Modifier.

Для работы с padding’ами:

Для работы с размерами:

Permissions

Работа с опасными разрешениями требует некоторых усилий. Нужно:

  • Запросить разрешение у пользователя.

  • С помощью кастомного диалога (view, snackbar и других) объяснить пользователю, зачем нужно разрешение.

  • Правильно обработать ответ пользователя.

  • Предоставить возможность перейти в настройки приложения, чтобы пользователь мог принять решение, какие доступы дать.

Разработчики Accompanist проделали большую работу, чтобы работать с разрешениями было удобно. И что самое классное, они сделали запрос разрешений через ActivityResultLauncher: никакого больше переопределения onActivityResult в Activity, и теперь явно прописывается контракт для запроса разрешения.

API достаточно простое.

permissionState — состояние с параметрами разрешения. Кроме boolean флагов, нужен для запроса разрешения.

permissionNotGrantedContent — контент для состояния, когда нет разрешения.

permissionNotAvailableContent — контент для состояния, когда разрешение нельзя запросить. Тогда пользователя надо направить в настройки приложения.

content — контент для состояния, когда пользователь дал разрешение.

Пара скриншотов с примерами работы из семпла
Пара скриншотов с примерами работы из семпла

Request Permission Sample

FlowLayout

FlexBoxLayout — контейнер для гибкой вёрстки. С ним удобно располагать элементы по горизонтали или вертикали: например, чтобы отобразить коллекцию элементов. Он сам рассчитает размеры и поймёт, как лучше перенести элемент на следующую строку или столбец.

FlexBox для Android — не родной контейнер для вёрстки. Первую версию FlexBoxLayout для CSS представили в 2009 году. До мира Android-разработки он добрался сильно позже: в 2016 году вышла первая alpha версия. С того времени вышло немало релизов, самый свежий — 3.0.0. 

Также вышел FlexBoxLayoutManager для RecyclerView. Вещь удобная, и она не могла не появиться в Compose. Только назвали контейнер по-другому: FlowLayout. Название не менее лаконичное — и так же точно передаёт суть его работы.

Как и его предки, имеет такие же параметры для вёрстки. Поэтому если вы с ними уже встречались ранее, то и в Compose верстать с его помощью будет несложно.

SwipeRefreshLayout

SwipeRefreshLayout — один из самых известных и используемых контейнеров в Android разработке.

Использовать его просто: в XML необходимо контейнер с контентом обернуть в SwipeRefreshLayout и в коде реализовать интерфейс OnRefreshListener — например, чтобы начать перезагрузку контента на экране. Вроде всё хорошо, больше ничего и не надо. 

Трудности начинаются, если разработчику нужно кастомизировать внешний вид лоадера. Возможности кастомизации ограничены — несмотря на громоздкую реализацию (~1200 строк). 

«Из коробки» можно изменить цвет стрелки, размер и появление индикатора. Если вам необходимо изменить цвет индикатора или (о боже) «а давайте как на iOS», то придётся искать стороннюю библиотеку или писать самостоятельно. Но и то, и другое занимает время и несёт риски.

Accompanist помогает решить эти проблемы. 

В Accompanist отсутствует XML, но принцип использования не изменился: контент всё так же оборачиваем в SwipeRefresh и используем лямбду для действия при обновлении.

Рассмотрим, что SwipeRefresh умеет «из коробки» и что с возможностью кастомизации.

state — хранит состояние загрузки и прогресс анимации индикатора.

onRefresh — лямбда-триггер для старта, например, перезагрузки контента.

modifier — модификатор контейнера: размеров, отступов и так далее.

swipeEnabled — включение и выключение реакции контейнера на свайп.

refreshTriggerDistance задаёт расстояние до триггера анимации. Чем расстояние больше, тем позже индикатор отреагирует на свайп.

indicatorAlignment — аналог Gravity. Интересная функциональность, которая пригодится, скорее, при реализации кастомного индикатора.

indicatorPadding — простая кастомизация отступов индикатора. Можно сделать отступ с определённой стороны, можно равный со всех.

indicator — реализация индикатора. «Из коробки» есть стандартная реализация индикатора из material design. Можно модифицировать её или написать полностью свою.

clipIndicatorToPadding — флаг, позволяющий обрезать индикатор по отступу контента.

content — непосредственно сам контент внутри SwipeRefresh.

Примеры работы SwipeRefresh из семпла библиотеки Accompanist.

SwipeRefreshBasicSample

SwipeRefreshTweakedIndicatorSample

SwipeRefreshCustomIndicatorSample

SwipeRefresh уже «из коробки» предоставляет неплохие возможности кастомизации привычного Android-разработчикам SwipeRefreshLayout. Реализация контейнера и индикатора занимает примерно 600 строк.

Pager

На UI языке Pager — горизонтальный или вертикальный список, у которого каждый элемент занимает всю или почти всю область экрана. Ключевое отличие от обычного списка — Snap-эффект, то есть «доскрол» элемента.

Pager, или ViewPager, имел несколько стадий развития как UI элемент в Android. Сначала был ViewPager, позже появился RecyclerView, и его приспособили под пейджинг. Сравнительно недавно появился ViewPager2, который под капотом использует тот же RecyclerView. 

Давайте взглянем, как Pager может выглядеть на «языке» Compose. ViewPager, ViewPager2 и RecyclerView имеют общую черту: как UI-элементам для отображения коллекций, им нужен адаптер для заполнения данными. Pager на Compose этого лишён: взамен адаптера необходимо инициализировать PagerState. С него и начнём.

Параметров немного, а для инициализации обязательный всего один.

pageCount — количество элементов,

currentPage — текущая страница,

currentPageOffset — отступ страницы. Имеет значение от 0.0 до 1.0. Можно использовать для программного скролла страницы.

offscreenLimit — лимит страниц для подгрузки: количество подгружаемых элементов слева и справа от текущего элемента.

infiniteLoop — зацикленный скролл.

Сразу видно, какая функциональность «из коробки» самая крутая — конечно же, это зацикленный скролл. Сколько же было написано костылей для зацикливания ViewPager и RecyclerView :) 

Accompanist предлагает две реализации для пейджера: HorizontalPager и VerticalPager. Если взглянуть в исходники, и то, и другое — просто обёртка на Compose функцией  Pager. Разница только в инициализации параметра isVertical.

Многие параметры могут быть знакомы по RecyclerView и ViewPager. Поэтому рассмотрим те,  которые не встречаются у них «из коробки».

verticalAlignment — аналог Gravity. Используется для центровки элемента: например, чтобы элемент доскроливался не до центра, а до края контейнера.

horizontalAlignment — аналогично vercticalAligment, только для горизонтали.

flingBehavior — конфиг поведения скрола: настраивает анимацию, когда пользователь перестаёт скролить или бросает скрол. Эти параметры примечательны тем, что у ViewPager и RecyclerView они не настраиваются легко «из коробки». Для ViewPager придётся «шаманить» с параметрами адаптера и с самим ViewPager. У RecyclerView для доскрола используются наследники SnapHelper’а.

В коде всё выглядит достаточно просто: инициализируем PagerState и заполняем пейджер элементами.

Часто пейджеры используются с индикаторами страниц, табами, а также трансформацией страниц при свайпе. И для этого у Accompanist’а тоже есть решение.

Как я писал выше, состояние 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-разработчику.

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

DrawablePainter — cамый простой представитель Painter’ов из библиотеки, но не менее полезный. Умеет отображать Drawable и AnimatableDrawable.

DrawablePainter
DrawablePainter

ImagePainter умеет больше всех. Поддерживает загрузку String, Uri ("android.resource", "content", "file", "http", and "https"), HttpUrl, File, DrawableRes, Drawable, Bitmap. 

ImageRequest и ImageLoader — базовые классы из Coil.

  • С помощью ImageRequest настраиваются параметры загрузки картинки: ссылка, листенеры, политика кеширования и декодинга.

  • ImageLoader запускает ImageRequest, содержит настройки для работы с изображениями. Как правило он настраивается один раз и переиспользуется во всём приложении.

ImagePainter
ImagePainter

PlaceHolder

Skeleton-загрузчики уже давно стали стандартом для многих платформ. Во время загрузки пользователь видит псевдовёрстку экрана: по размеру и форме фигур он может предположить, где будет текст, где — фото, а где — кнопка. Это создаёт у пользователя позитивные впечатления.

Accompanist предлагает несколько реализаций placeholder’ов для создания skeleton загрузчиков: 

  • Базовая — placeholder без анимаций. 

  • Fade — placeholder c fade-анимацией. 

  • Shimmer — placeholder c анимацией мерцания.

Accompanist — крутая поддержка для разработчиков на старте внедрения Compose

У Accompanist ещё не вышла версия 1.0, а инструментарий библиотеки уже достаточно богатый и поможет обойти кучу граблей. 

Стоит заметить, что Accompanist — не единственная библиотека, которая поможет в поддержке и интеграции с Compose. Уже есть интеграция Compose c Hilt, Rx, Jetpack Navigation, Jetpack Paging. В Lottie 4.0.0 тоже добавили поддержку Compose.

Комментарии (3)


  1. quaer
    09.08.2021 01:28

    Лучше бы усилия направили на качество и хорошую документацию, а не на количество.

    Что толку от всех этих библиотек, если всё кривое и косое? Чуть не каждая попытка задейстовать что-то на Андроиде приводит к поиску обхода проблем и багов.


    1. ozh-dev Автор
      09.08.2021 10:11

      Да можно согласиться с вами, библиотека всё-таки версии 0.15 и до 1.0 далеко.
      А вы тут документацию смотрели - https://google.github.io/accompanist/? Тут вроде по всем зависимостям библиотеки достаточно полно рассказывается.
      Я считаю что баги не избежны сейчас, библиотека очень активно развивается. Вовремя написания статьи были задперекейчены зависимости для Coil и Glide, а через 4 часа после выхода статьи, вышла новая версия библиотеки.


      1. quaer
        09.08.2021 13:45

        Речь в принципе об экосистеме поверх которой всё это навёрнуто. Обёртка поверх обёртки. Почему бы не направить усилия на улучшение базы поверх которой всё это наворачивается? Вот буквально недавний пример: понадобился InputTextLayout. Казалось бы, куда стандартней? И тут же пришлось лезть искать почему бордюр рисуется поперёк текста метки. Пока пришлось убрать, потому что решения не нашлось.