Управление версиями плиток

На устройствах Wear OS плитки визуализируются двумя ключевыми компонентами с независимым управлением версиями. Чтобы плитки приложений работали правильно на всех устройствах, важно понимать эту базовую архитектуру.

  • Библиотеки, связанные с плиткой Jetpack : эти библиотеки (включая Wear Tiles и Wear ProtoLayout) встроены в ваше приложение, и вы, как разработчик, контролируете их версии. Ваше приложение использует эти библиотеки для создания объекта TileBuilder.Tile (структуры данных, представляющей вашу плитку) в ответ на системный вызов onTileRequest() .
  • ProtoLayout Renderer: Этот системный компонент отвечает за рендеринг объекта Tile на дисплее и обработку пользовательских взаимодействий. Версия рендерера не контролируется разработчиком приложения и может различаться на разных устройствах, даже с идентичным оборудованием.

Внешний вид или поведение плитки может различаться в зависимости от версий библиотеки Jetpack Tiles вашего приложения и версии ProtoLayout Renderer на устройстве пользователя. Например, одно устройство может поддерживать вращение или отображение данных о частоте сердечных сокращений, а другое — нет.

В этом документе объясняется, как обеспечить совместимость вашего приложения с различными версиями библиотеки Tiles и ProtoLayout Renderer, а также как перейти на более поздние версии библиотеки Jetpack.

Рассмотрите совместимость

Чтобы создать плитку, которая будет корректно работать на различных устройствах, следует учитывать следующее.

Определить версию рендерера

  • Используйте метод getRendererSchemaVersion() объекта DeviceParameters , переданного в ваш метод onTileRequest() . Этот метод возвращает основные и второстепенные номера версий ProtoLayout Renderer на устройстве.
  • Затем вы можете использовать условную логику в реализации onTileRequest() чтобы адаптировать дизайн или поведение вашего Tile на основе обнаруженной версии рендерера.
    • Например, если определенная анимация не поддерживается, вместо нее можно отобразить статическое изображение.

Аннотация @RequiresSchemaVersion

  • Аннотация @RequiresSchemaVersion в методах ProtoLayout указывает минимальную версию схемы рендерера, необходимую для того, чтобы этот метод вел себя так, как описано в документации ( пример ).
    • Хотя вызов метода, требующего более высокой версии рендерера, чем доступна на устройстве, не приведет к сбою приложения, он может привести к тому, что контент не будет отображаться или функция будет проигнорирована.

Пример

override fun onTileRequest(
    requestParams: TileService.TileRequest
): ListenableFuture<Tile> {
    val rendererVersion =
        requestParams.deviceConfiguration.rendererSchemaVersion
    val tile = Tile.Builder()

    if (
        rendererVersion.major > 1 ||
            (rendererVersion.major == 1 && rendererVersion.minor >= 300)
    ) {
        // Use a feature supported in renderer version 1.300 or later
        tile.setTileTimeline(/* ... */ )
    } else {
        // Provide fallback content for older renderers
        tile.setTileTimeline(/* ... */ )
    }

    return Futures.immediateFuture(tile.build())
}

Тестирование с различными версиями рендерера

Чтобы протестировать плитки на разных версиях рендерера, разверните их на разных версиях эмулятора Wear OS. (На физических устройствах обновления рендерера ProtoLayout предоставляются через Play Store или системные обновления. Невозможно принудительно установить определенную версию рендерера.)

Функция предварительного просмотра плиток Android Studio использует рендерер, встроенный в библиотеку Jetpack ProtoLayout, от которой зависит ваш код, поэтому другой подход — использовать различные версии библиотеки Jetpack при тестировании плиток.

Переход на Tiles 1.5 / ProtoLayout 1.3 (Material 3 Expressive)

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

Jetpack Tiles 1.5 и Jetpack ProtoLayout 1.3 вводят несколько заметных улучшений и изменений. Они включают:

  • API-интерфейс типа Compose для описания пользовательского интерфейса.
  • Компоненты Material 3 Expressive , включая кнопку с обхватывающим нижний край и поддержку улучшенных визуальных эффектов: анимации Lottie, больше типов градиентов и новые стили дуговых линий. - Примечание: некоторые из этих функций можно использовать и без перехода на новый API.

Рекомендации

  • Переносите все плитки одновременно. Избегайте смешивания версий плиток в вашем приложении. Хотя компоненты Material 3 находятся в отдельном артефакте ( androidx.wear.protolayout:protolayout-material3 ) — что делает технически возможным использование плиток M2.5 и M3 в одном приложении — мы настоятельно не рекомендуем использовать этот подход, если только это не является абсолютно необходимым (например, если в вашем приложении большое количество плиток, которые нельзя перенести все одновременно).
  • Примите руководство Tiles UX. Учитывая высокую структурированность и шаблонность плиток, используйте дизайны в существующих примерах в качестве отправных точек для собственных дизайнов.
  • Тестируйте на разных экранах и размерах шрифтов. Плитки часто содержат много информации, что делает текст (особенно при размещении на кнопках) подверженным переполнению и обрезке. Чтобы свести это к минимуму, используйте готовые компоненты и избегайте обширной настройки. Тестируйте с помощью функции предварительного просмотра плиток Android Studio , а также на нескольких реальных устройствах.

Процесс миграции

Обновление зависимостей

Сначала обновите файл build.gradle.kts . Обновите версии и измените зависимость protolayout-material на protolayout-material3 , как показано:

// In build.gradle.kts

//val tilesVersion = "1.4.1"
//val protoLayoutVersion = "1.2.1"

// Use these versions for M3.
val tilesVersion = "1.5.0-rc01"
val protoLayoutVersion = "1.3.0-rc01"

 dependencies {
     // Use to implement support for wear tiles
     implementation("androidx.wear.tiles:tiles:$tilesVersion")

     // Use to utilize standard components and layouts in your tiles
     implementation("androidx.wear.protolayout:protolayout:$protoLayoutVersion")

     // Use to utilize components and layouts with Material Design in your tiles
     // implementation("androidx.wear.protolayout:protolayout-material:$protoLayoutVersion")
     implementation("androidx.wear.protolayout:protolayout-material3:$protoLayoutVersion")

     // Use to include dynamic expressions in your tiles
     implementation("androidx.wear.protolayout:protolayout-expression:$protoLayoutVersion")

     // Use to preview wear tiles in your own app
     debugImplementation("androidx.wear.tiles:tiles-renderer:$tilesVersion")

     // Use to fetch tiles from a tile provider in your tests
     testImplementation("androidx.wear.tiles:tiles-testing:$tilesVersion")
 }

TileService остается практически неизменным

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

Главное исключение касается отслеживания активности плитки: если ваше приложение использует onTileEnterEvent() или onTileLeaveEvent() , вам следует перейти на onRecentInteractionEventsAsync() . Начиная с API 36, эти события будут пакетироваться.

Адаптируйте свой код генерации макета

В ProtoLayout 1.2 (M2.5) метод onTileRequest() возвращает TileBuilders.Tile . Этот объект содержал различные элементы, включая TimelineBuilders.Timeline , который в свою очередь содержал LayoutElement , описывающий пользовательский интерфейс плитки.

В ProtoLayout 1.3 (M3), хотя общая структура данных и поток не изменились, LayoutElement теперь создается с использованием подхода, вдохновленного Compose, с макетом, основанным на определенных слотах , которые (сверху вниз) являются titleSlot (обычно для основного заголовка или заголовка), mainSlot (для основного контента) и bottomSlot (часто для действий, таких как кнопка края или дополнительная информация, такая как короткий текст). Этот макет создается функцией primaryLayout() .

Макет плитки, показывающий mainSlot, titleSlot, bottomSlot
Рисунок 1. : Слоты плитки.
Сравнение функций макета M2.5 и M3

М2.5

fun myLayout(
    context: Context,
    deviceConfiguration: DeviceParametersBuilders.DeviceParameters
) =
    PrimaryLayout.Builder(deviceConfiguration)
        .setResponsiveContentInsetEnabled(true)
        .setContent(
            Text.Builder(context, "Hello World!")
                .setTypography(Typography.TYPOGRAPHY_BODY1)
                .setColor(argb(0xFFFFFFFF.toInt()))
                .build()
        )
        .build()

М3

fun myLayout(
    context: Context,
    deviceConfiguration: DeviceParametersBuilders.DeviceParameters,
) =
    materialScope(context, deviceConfiguration) {
        primaryLayout(mainSlot = { text("Hello, World!".layoutString) })
    }

Чтобы подчеркнуть основные различия:

  1. Устранение Builders . Традиционный шаблон Builder для компонентов пользовательского интерфейса Material3 заменяется более декларативным синтаксисом, вдохновленным Compose. (Не-UI-компоненты, такие как String/Color/Modifiers, также получают новые оболочки Kotlin.)
  2. Стандартизированные функции инициализации и макета . Макеты M3 опираются на стандартизированные функции инициализации и структуры: materialScope() и primaryLayout() . Эти обязательные функции инициализируют среду M3 (темизацию, область действия компонента через materialScope ) и определяют первичный макет на основе слотов (через primaryLayout ). Обе должны быть вызваны ровно один раз для макета.

Тематика

Цвет

Отличительной особенностью Material 3 Expressive является «динамическое тематизирование»: плитки, которые включают эту функцию (включены по умолчанию), будут отображаться в теме, предоставленной системой (доступность зависит от устройства и конфигурации пользователя).

Еще одним изменением в M3 является расширение количества цветовых токенов, которое увеличилось с 4 до 29. Новые цветовые токены можно найти в классе ColorScheme .

Типографика

Подобно M2.5, M3 в значительной степени опирается на предопределенные константы размера шрифта — прямое указание размера шрифта не рекомендуется. Эти константы находятся в классе Typography и предлагают немного расширенный диапазон более выразительных опций.

Подробную информацию см. в документации по типографике .

Форма

Большинство компонентов M3 могут различаться по размеру, форме и цвету.

textButtonmainSlot ) с full формой:

Плитка с «полной» формой (более закругленные углы)
Рисунок 2. : Плитка с «полной» формой

Тот же текстовый элемент с small формой:

Плитка «маленькой» формы (менее закругленные углы)
Рисунок 3. : Плитка с «маленькой» формой

Компоненты

Компоненты M3 значительно более гибкие и настраиваемые, чем их аналоги M2.5. Там, где M2.5 часто требовал отдельных компонентов для различных визуальных обработок, M3 часто использует обобщенный, но в то же время высоконастраиваемый «базовый» компонент с хорошими значениями по умолчанию.

Этот принцип применяется к «корневому» макету. В M2.5 это был либо PrimaryLayout , либо EdgeContentLayout . В M3 после того, как установлен один MaterialScope верхнего уровня, вызывается функция primaryLayout() . Она возвращает корневой макет напрямую (не нужны построители) и принимает LayoutElements для нескольких «слотов», таких как titleSlot , mainSlot и bottomSlot . Эти слоты могут быть заполнены конкретными компонентами пользовательского интерфейса, такими как возвращаемые text() , button() или card() , или структурами макета, такими как Row или Column из LayoutElementBuilders .

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

М2.5 М3
Интерактивные элементы
Button или Chip
Текст
Text text()
Индикаторы прогресса
CircularProgressIndicator circularProgressIndicator() или segmentedCircularProgressIndicator()
Макет
PrimaryLayout или EdgeContentLayout primaryLayout()
buttonGroup()
Изображения
icon() , avatarImage() или backgroundImage()

Модификаторы

В M3 Modifiers , которые вы используете для декорирования или дополнения компонента, больше похожи на Compose. Это изменение может сократить шаблонный код за счет автоматического построения соответствующих внутренних типов. (Это изменение ортогонально использованию компонентов пользовательского интерфейса M3; при необходимости вы можете использовать модификаторы в стиле builder из ProtoLayout 1.2 с компонентами пользовательского интерфейса M3 и наоборот.)

М2.5

// A Builder-style modifier to set the opacity of an element to 0.5
fun myModifier(): ModifiersBuilders.Modifiers =
    ModifiersBuilders.Modifiers.Builder()
        .setOpacity(TypeBuilders.FloatProp.Builder(0.5F).build())
        .build()

М3

// The equivalent Compose-like modifier is much simpler
fun myModifier(): LayoutModifier = LayoutModifier.opacity(0.5F)

Вы можете создавать модификаторы, используя любой из стилей API, а также можете использовать функцию расширения toProtoLayoutModifiers() для преобразования LayoutModifier в ModifiersBuilders.Modifier .

Вспомогательные функции

В то время как ProtoLayout 1.3 позволяет выражать многие компоненты пользовательского интерфейса с помощью API, вдохновленного Compose, основополагающие элементы макета, такие как строки и столбцы из LayoutElementBuilders продолжают использовать шаблон строителя. Чтобы преодолеть этот стилистический разрыв и обеспечить согласованность с новыми API компонентов M3, рассмотрите возможность использования вспомогательных функций.

Без помощников

primaryLayout(
    mainSlot = {
        LayoutElementBuilders.Column.Builder()
            .setWidth(expand())
            .setHeight(expand())
            .addContent(text("A".layoutString))
            .addContent(text("B".layoutString))
            .addContent(text("C".layoutString))
            .build()
    }
)

С помощниками

// Function literal with receiver helper function
fun column(builder: Column.Builder.() -> Unit) =
    Column.Builder().apply(builder).build()

primaryLayout(
    mainSlot = {
        column {
            setWidth(expand())
            setHeight(expand())
            addContent(text("A".layoutString))
            addContent(text("B".layoutString))
            addContent(text("C".layoutString))
        }
    }
)

Переход на Tiles 1.2 / ProtoLayout 1.0

Начиная с версии 1.2 большинство API макетов Tiles находятся в пространстве имен androidx.wear.protolayout . Чтобы использовать последние API, выполните следующие шаги миграции в вашем коде.

Обновление зависимостей

В файле сборки модуля вашего приложения внесите следующие изменения:

Круто

  // Remove
  implementation 'androidx.wear.tiles:tiles-material:version'

  // Include additional dependencies
  implementation "androidx.wear.protolayout:protolayout:1.2.1"
  implementation "androidx.wear.protolayout:protolayout-material:1.2.1"
  implementation "androidx.wear.protolayout:protolayout-expression:1.2.1"

  // Update
  implementation "androidx.wear.tiles:tiles:1.4.1"

Котлин

  // Remove
  implementation("androidx.wear.tiles:tiles-material:version")

  // Include additional dependencies
  implementation("androidx.wear.protolayout:protolayout:1.2.1")
  implementation("androidx.wear.protolayout:protolayout-material:1.2.1")
  implementation("androidx.wear.protolayout:protolayout-expression:1.2.1")

  // Update
  implementation("androidx.wear.tiles:tiles:1.4.1")

Обновление пространств имен

В файлах кода вашего приложения на основе Kotlin и Java сделайте следующие обновления. В качестве альтернативы вы можете выполнить этот скрипт переименования пространства имен .

  1. Замените все импорты androidx.wear.tiles.material.* на androidx.wear.protolayout.material.* . Выполните этот шаг также для библиотеки androidx.wear.tiles.material.layouts .
  2. Замените большинство других импортов androidx.wear.tiles.* на androidx.wear.protolayout.* .

    Импорт для androidx.wear.tiles.EventBuilders , androidx.wear.tiles.RequestBuilders , androidx.wear.tiles.TileBuilders и androidx.wear.tiles.TileService должен остаться прежним.

  3. Переименуйте несколько устаревших методов из классов TileService и TileBuilder:

    1. TileBuilders : getTimeline() для getTileTimeline() и setTimeline() для setTileTimeline()
    2. TileService : onResourcesRequest() к onTileResourcesRequest()
    3. RequestBuilders.TileRequest : getDeviceParameters() для getDeviceConfiguration() , setDeviceParameters() для setDeviceConfiguration() , getState() для getCurrentState() и setState() для setCurrentState()