Connect with us

Разработка

Назад в будущее: превью Compose для представлений на основе XML

Compose Preview действительно можно использовать с XML-представлениями!

Опубликовано

/

     
     

Jetpack Compose изменил разработку пользовательского интерфейса благодаря декларативному подходу, упростив написание кода. Одна из его отличительных особенностей — предварительные просмотры Compose, которая позволяет разработчикам мгновенно увидеть, как Composable функции будут выглядеть на устройстве, без необходимости запускать приложение на устройстве или эмуляторе.

Это означает, что они демонстрируют не только статичный макет, но и состояние UI, полученное на основе логики пользовательского интерфейса в Composable, и даже могут воспроизводить анимацию, если она включена, среди прочих возможностей.

В отличие от этого, старые представления Android на основе XML позволяют предварительно просматривать макеты. Но они ограничиваются статичными макетами с содержимым, не представляющим никакой реальной логики или данных — они настолько просты, насколько это возможно.

Так что если некоторые части вашего приложения все еще полагаются на представления на основе XML, вы можете упустить всю мощь предварительных просмотров Compose… или нет?

Ведь Compose Preview действительно можно использовать с XML-представлениями!

Почему предварительный просмотр XML-макетов не работает

Представим, что у нас есть следующий RecyclerView ViewHolder и соответствующий ему XML-макет Preview.

Назад в будущее: превью Compose для представлений на основе XML

Разница между ними очень заметна. Это связано с тем, что ViewHolder включает UI-логику, которая не может быть представлена в статическом XML-представлении. Например:

  • Динамическая инъекция представлений: ViewHolder программно внедряет дополнительные представления, такие как радиокнопки (т.е. флаги языков).
  • Использование кастомных представлений: В состав входит пользовательское представление для отображения количества «слов для обучения», состоящее из двух TextView, видимость которых меняется во время выполнения, чтобы анимировать подсчет и уменьшение чисел. Хотя можно использовать ссылку на атрибут tools, т. е. tools: для предварительного просмотра данных, это не может имитировать поведение этого пользовательского представления во время выполнения.

При таком динамическом поведении предварительный просмотр XML-макета становится практически бесполезным.

Включение предварительного просмотра Compose для ваших XML-макетов

Рендеринг представления состоит из двух основных частей:

  1. Inflating: Представление, созданное путем «раздувания» связанного XML-макета. Для ViewHolder этот View передается в качестве аргумента в конструктор.
  2. Binding: Метод, передающий состояние пользовательского интерфейса для рендеринга в View или ViewHolder.
// 1. Inflating
val container = LayoutInflater
    .from(context)
    .inflate(R.layout.mylayout, parentViewGroup)

// Create the ViewHolder with the container
val viewHolder = MyViewHolder(
    container = container,
    // other arguments, e.g. listeners, here...
)

// 2. Binding
viewHolder.bind(uiState)

Когда дело доходит до предварительного просмотра ViewHolder в Compose Preview, нам сначала нужно получить доступ к его базовому представлению. Мы можем получить доступ к нему через свойство itemView ViewHolder.

Но как отобразить это представление в предварительном просмотре Compose? Решение кроется в композабл AndroidView. Если вы интегрировали Compose в существующее приложение на основе XML, вы, возможно, уже использовали AndroidView для обертывания существующих экземпляров пользовательских представлений и их интеграции в иерархию Compose.

Именно этот трюк мы будем использовать для отображения ViewHolder — и любого другого представления на основе XML — внутри Compose Preview.

Давайте применим это на примере предыдущего ViewHolder!

@Preview
@Composable
fun TrainingViewHolderPreview() {
    AndroidView(
        // You might prefer fillMaxheight or fillMaxSize
        // depending on the container's layout constraints
        modifier = Modifier.fillMaxWidth(), 
        factory = { context ->

            // 1. inflating
            val containerParent = FrameLayout(contextWrapper)
            val container = LayoutInflater
                .from(context)
                .inflate(R.layout.training_row, containerParent)

            val viewHolder = TrainingViewHolder(container = layout)

            // 2. Binding
            viewHolder.bind(
                 item = initialTrainingItem,
                 languageClickedListener = null // or a meaningful listener
            )
            .itemView
        },
    )
}

А вот как выглядит предварительный просмотр по сравнению с тем, что мы видим на устройстве:

Назад в будущее: превью Compose для представлений на основе XML

Как мы видим, предварительный просмотр не полностью соответствует тому, что мы видим на устройстве. Это связано с тем, что реализация нашего viewHolder.bind() запускает некоторые анимации, и, похоже, предварительный просмотр отражает их состояние до того, как они будут полностью выполнены.

Вот тут-то и пригодится интерактивный режим предварительного просмотра (Preview Interactive Mode).

В большинстве случаев Compose Preview отображаются очень точно, в том числе и при использовании представлений на основе XML. Это исключительный случай, связанный с анимацией, запускаемой при создании ViewHolder.

Интерактивный режим предварительного просмотра

Запуск режима Preview Interaction позволяет нам быстро пронаблюдать полную анимацию, выполняемую в viewHolder.bind():

Назад в будущее: превью Compose для представлений на основе XML

Тем не менее, он все еще не идентичен тому, что мы видим на реальном устройстве: британский флаг должен быть отключен, чтобы соответствовать «6 словам для обучения» (5 русских, 1 немецкое). Это связано с тем, что он запускается в Android Studio, которая использует Layoutlib для рендеринга Composable.

Layoutlib — это кастомная версия фреймворка Android, специально разработанная для работы вне Android-устройств. Его цель — обеспечить предварительный просмотр макета в Android Studio, который очень похож на рендеринг на устройстве, хотя это может быть и не точное совпадение.

При скриншот тестах этого превью с помощью различных библиотек, что можно сделать с помощью ComposablePreviewScanner, результаты получаются разными:

  1. Paparazzi генерирует скриншот, отражающий то, что мы видим в интерактивном режиме Preview, что неточно.
  2. Инструмент тестирования скриншотов Compose Preview Screenshot Test сбоит при раздувании ViewHolder, о чем я сообщал в этом issue. Однако ожидается, что в обычных условиях он будет отображать те же результаты, которые мы видим в Preview. Это связано с тем, что он также основан на Layoutlib, как и Paparazzi.
  3. Roborazzi, который основан на JVM, а также все библиотеки скриншот-тестирования на основе инструментария, такие как Dropshots, Shot или Android-Testify, генерируют точные скриншоты, идентичные тому, что отображается на устройстве. Ни одна из них не использует Layoutlib.

Вы можете проверить это самостоятельно в репозитории Github Android Screenshot Testing Playground в модуле :recyclerviewscreen-previews.

Таким образом, похоже, что это ошибка, связанная с Layoutlib.

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

И тут на помощь приходит опция Run Preview.

Run Preview

Эта опция запускает предварительный просмотр на эмуляторе или физическом устройстве, и это очень быстро, если эмулятор уже запущен.

Поскольку превью запускается на эмуляторе или устройстве, для рендеринга превью используется настоящий Android Framework, а не Layoutlib. Как следствие, это наиболее точный вариант проверки того, как отображается код в ваших превью.

Назад в будущее: превью Compose для представлений на основе XML

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

Однако у этого способа есть недостаток: он отображает конфигурацию (т. е. локаль, режим пользовательского интерфейса, размер шрифта и т.д.) устройства, а не Preview.

Однако это все равно эффективнее, чем устанавливать приложение и переходить к экрану, на котором находится компонент для предварительного просмотра.

Заключение

Мы рассмотрели, как Compose Preview могут рендерить макеты на основе XML, продемонстрировав несколько преимуществ по сравнению с традиционными XML-превью:

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

Мы также рассмотрели, как эффективно использовать интерактивный режим Compose Preview и опции Run Preview, их недостатки, а также то, как рендеринг в Preview соотносится с файлами скриншотов, генерируемыми наиболее популярными библиотеками скриншот-тестирования, в зависимости от того, основаны они на Layoutlib или нет.

Использование Compose Preview с представлениями на основе XML открывает новые возможности. Благодаря таким библиотекам, как ComposablePreviewScanner, мы можем автоматически генерировать скриншот-тесты из Preview и запускать их с помощью выбранной нами библиотеки тестирования скриншотов. Это позволяет нам:

Эти инструменты не только экономят время, но и помогают нам внедрить лучшие практики разработки UI.

Примеры кода из этой статьи вы можете найти в репозитории Android Screenshot Testing Playground.

Источник

Если вы нашли опечатку - выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.

Наши партнеры:

LEGALBET

Мобильные приложения для ставок на спорт
Хорошие новости

Telegram

Популярное

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: