На устройствах Wear OS плитки отрисовываются двумя ключевыми компонентами с независимым управлением версиями. Чтобы плитки вашего приложения работали правильно на всех устройствах, важно понимать эту архитектуру.
- Библиотеки Jetpack, связанные с виджетами. Эти библиотеки (в том числе Wear Tiles и Wear ProtoLayout) встроены в ваше приложение, и вы как разработчик контролируете их версии. Ваше приложение использует эти библиотеки для создания объекта
TileBuilder.Tile(структуры данных, представляющей ваш тайл) в ответ на вызов системыonTileRequest(). - Визуализатор ProtoLayout. Этот системный компонент отвечает за отрисовку объекта
Tileна экране и обработку действий пользователя. Версия рендерера не контролируется разработчиком приложения и может различаться на разных устройствах, даже если у них одинаковое аппаратное обеспечение.
Внешний вид и поведение элемента могут различаться в зависимости от версии библиотеки Jetpack Tiles в вашем приложении и версии ProtoLayout Renderer на устройстве пользователя. Например, одно устройство может поддерживать поворот экрана или отображение данных о пульсе, а другое – нет.
В этом документе рассказывается, как сделать приложение совместимым с разными версиями библиотеки Tiles и отрисовщика ProtoLayout. Также в ней рассказывается, как перейти на более новые версии библиотеки Jetpack.
Проверьте совместимость
Чтобы создать элемент, который будет корректно работать на разных устройствах, учитывайте, что некоторые функции могут поддерживаться не везде. Это можно сделать двумя способами: обнаруживать возможности отрисовщика во время выполнения и предоставлять встроенные резервные варианты.
Как определить возможности отрисовщика
Вы можете динамически менять макет виджета в зависимости от функций, доступных на устройстве.
Как определить версию средства отрисовки
- Используйте метод
getRendererSchemaVersion()объектаDeviceParameters, переданного в методonTileRequest(). Этот метод возвращает номера основной и промежуточной версий ProtoLayout Renderer на устройстве. - Затем вы можете использовать условную логику в реализации
onTileRequest(), чтобы адаптировать дизайн или поведение элемента на основе обнаруженной версии отрисовщика.
Аннотация "@RequiresSchemaVersion"
- Пометка
@RequiresSchemaVersionрядом с методами ProtoLayout указывает минимальную версию схемы средства отрисовки, необходимую для того, чтобы метод работал так, как описано в документации (пример).- Если вызвать метод, для которого требуется более новая версия отрисовщика, чем доступна на устройстве, приложение не закроется, но контент может не отобразиться или функция будет проигнорирована.
Пример определения версии
val rendererVersion = requestParams.deviceConfiguration.rendererSchemaVersion val arcElement = // DashedArcLine has the annotation @RequiresSchemaVersion(major = 1, minor = 500) // and so is supported by renderer versions 1.500 and greater if ( rendererVersion.major > 1 || (rendererVersion.major == 1 && rendererVersion.minor >= 500) ) { // Use DashedArcLine if the renderer supports it … DashedArcLine.Builder() .setLength(degrees(270f)) .setThickness(8f) .setLinePattern( LayoutElementBuilders.DashedLinePattern.Builder() .setGapSize(8f) .setGapInterval(10f) .build() ) .build() } else { // … otherwise use ArcLine. ArcLine.Builder().setLength(degrees(270f)).setThickness(dp(8f)).build() }
Предоставляйте резервные варианты
В некоторых ресурсах можно задать резервный вариант непосредственно в конструкторе. Это часто проще, чем проверять версию проигрывателя, и является предпочтительным подходом, если доступно.
Часто статическое изображение используется в качестве резервного для анимации Lottie. Если устройство не поддерживает анимацию Lottie, вместо нее будет показано статичное изображение.
val lottieImage = ResourceBuilders.ImageResource.Builder() .setAndroidLottieResourceByResId( ResourceBuilders.AndroidLottieResourceByResId.Builder(R.raw.lottie) .setStartTrigger(createOnVisibleTrigger()) .build() ) // Fallback if lottie is not supported .setAndroidResourceByResId( ResourceBuilders.AndroidImageResourceByResId.Builder() .setResourceId(R.drawable.lottie_fallback) .build() ) .build()
Тестирование с разными версиями рендерера
Чтобы протестировать виджеты с разными версиями отрисовщика, разверните их в разных версиях эмулятора Wear OS. На физических устройствах обновления ProtoLayout Renderer поставляются через Google Play или системные обновления. Невозможно принудительно установить определенную версию отрисовщика.)
Функция предпросмотра плиток в Android Studio использует встроенный в библиотеку Jetpack ProtoLayout, от которой зависит ваш код, отрисовщик. Поэтому при тестировании плиток можно использовать разные версии библиотек Jetpack.
Переход на Tiles 1.5 / ProtoLayout 1.3 (Material 3 Expressive)
Обновите библиотеки Jetpack Tile, чтобы воспользоваться новыми функциями, в том числе изменениями в интерфейсе, которые позволяют легко интегрировать элементы в систему.
В 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, но мы настоятельно не рекомендуем этого делать, если в этом нет крайней необходимости (например, если в приложении много элементов, которые нельзя перенести одновременно). - Следуйте рекомендациям по использованию мозаики. Поскольку карточки имеют четкую структуру и шаблоны, используйте существующие образцы в качестве отправной точки для собственных дизайнов.
- Проверьте, как выглядит контент при разных размерах экрана и шрифта. Плитки часто содержат много информации, поэтому текст (особенно на кнопках) может не помещаться и обрезаться. Чтобы этого избежать, используйте готовые компоненты и не вносите значительных изменений. Проверьте работу функции с помощью предварительного просмотра в 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"
val protoLayoutVersion = "1.3.0"
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().
Сравнение функций макета M2.5 и M3
M2.5
fun myLayout( context: Context, deviceConfiguration: DeviceParametersBuilders.DeviceParameters ) = PrimaryLayout.Builder(deviceConfiguration) .setResponsiveContentInsetEnabled(true) .setContent( Text.Builder(context, "Hello World!") .setTypography(Typography.TYPOGRAPHY_BODY1) .build() ) .build()
M3
fun myLayout( context: Context, deviceConfiguration: DeviceParametersBuilders.DeviceParameters, ) = materialScope(context, deviceConfiguration) { primaryLayout(mainSlot = { text("Hello, World!".layoutString) }) }
Основные различия между этими двумя типами данных приведены в таблице ниже.
- Устранение строителей. Предыдущий шаблон создания компонентов Material UI заменен более декларативным синтаксисом, вдохновленным Compose. (Компоненты без интерфейса, такие как String, Color и Modifiers, также получают новые обертки Kotlin.)
- Стандартизированные функции инициализации и макета. В макетах M3 используются стандартизированные функции инициализации и структуры:
materialScope()иprimaryLayout(). Эти обязательные функции инициализируют среду M3 (темы, область действия компонента с помощьюmaterialScope) и определяют основной макет на основе слотов (с помощьюprimaryLayout). Обе функции должны вызываться ровно один раз для каждого макета.
Темы
В Material 3 внесены изменения в темы, в том числе добавлены динамические цвета и расширенный набор вариантов типографики и форм.
Цвет
Одной из ключевых особенностей Material 3 Expressive является динамическое оформление. Плитки, для которых включена эта функция (по умолчанию), будут отображаться в системной теме (доступность зависит от устройства и конфигурации пользователя).
Ещё одно изменение в M3 – увеличение количества токенов цвета с 4 до 29. Новые токены цветов можно найти в классе ColorScheme.
Параметры текста
Как и в M2.5, в M3 широко используются константы для размера шрифта. Указывать размер шрифта напрямую не рекомендуется. Эти константы находятся в классе Typography и предлагают более широкий диапазон выразительных вариантов.
Подробную информацию можно найти в документации по типографике.
Фигура
Большинство компонентов M3 могут различаться по форме и цвету.
textButton (в mainSlot) с формой full:
Та же кнопка textButton с фигурой small:
Компоненты
Компоненты 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 и поддерживают динамическое применение тем.
| M2.5 | M3 |
|---|---|
| Интерактивные элементы | |
Button или Chip |
|
| Текст | |
Text |
text() |
| Индикаторы прогресса | |
CircularProgressIndicator |
circularProgressIndicator() или segmentedCircularProgressIndicator() |
| Вид страницы "Обзор" | |
PrimaryLayout или EdgeContentLayout |
primaryLayout() |
| — | buttonGroup() |
| Изображения | |
| — | icon(), avatarImage() или backgroundImage() |
Клавиши-модификаторы
В M3 Modifiers, которые используются для оформления или дополнения компонента, больше похожи на Compose. Это изменение позволяет сократить количество стандартного кода, поскольку нужные внутренние типы создаются автоматически. (Это изменение не связано с использованием компонентов интерфейса M3. При необходимости вы можете использовать модификаторы в стиле конструктора из ProtoLayout 1.2 с компонентами интерфейса M3 и наоборот.)
M2.5
// Uses Builder-style modifier to set opacity fun myModifier(): ModifiersBuilders.Modifiers = ModifiersBuilders.Modifiers.Builder() .setOpacity(TypeBuilders.FloatProp.Builder(0.5F).build()) .build()
M3
// Uses Compose-like modifiers to set opacity fun myModifier(): LayoutModifier = LayoutModifier.opacity(0.5F)
Модификаторы можно создавать в любом стиле API. Кроме того, вы можете использовать функцию расширения toProtoLayoutModifiers(), чтобы преобразовать LayoutModifier в ModifiersBuilders.Modifier.
Вспомогательные функции
Хотя ProtoLayout 1.3 позволяет создавать многие компоненты интерфейса с помощью API, вдохновленного Compose, основные элементы макета, такие как строки и столбцы из LayoutElementBuilders, по-прежнему используют шаблон построителя. Чтобы устранить стилистические различия и обеспечить единообразие с новыми API компонентов M3, используйте вспомогательные функции.
Без помощников
primaryLayout( mainSlot = { 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 макетов виджетов находятся в пространстве имен androidx.wear.protolayout. Чтобы использовать новые API, выполните в коде следующие действия по переносу.
Обновление зависимостей
В файле сборки модуля приложения внесите следующие изменения:
Яркий
// Removeimplementation 'androidx.wear.tiles:tiles-material:version'// Include additional dependencies implementation "androidx.wear.protolayout:protolayout:1.4.2" implementation "androidx.wear.protolayout:protolayout-material:1.4.2" implementation "androidx.wear.protolayout:protolayout-expression:1.4.2" // Update implementation "androidx.wear.tiles:tiles:1.6.2"
Kotlin
// Removeimplementation("androidx.wear.tiles:tiles-material:version")// Include additional dependencies implementation("androidx.wear.protolayout:protolayout:1.4.2") implementation("androidx.wear.protolayout:protolayout-material:1.4.2") implementation("androidx.wear.protolayout:protolayout-expression:1.4.2") // Update implementation("androidx.wear.tiles:tiles:1.6.2")
Как обновить пространства имен
В файлах кода приложения на Kotlin и Java внесите следующие изменения: Вы также можете выполнить скрипт для переименования пространства имен.
- Заменить все импортированные объекты
androidx.wear.tiles.material.*наandroidx.wear.protolayout.material.*. Выполните этот шаг также для библиотекиandroidx.wear.tiles.material.layouts. Замените большинство других импортированных объектов
androidx.wear.tiles.*на объектandroidx.wear.protolayout.*.Импорт для
androidx.wear.tiles.EventBuilders,androidx.wear.tiles.RequestBuilders,androidx.wear.tiles.TileBuildersиandroidx.wear.tiles.TileServiceдолжен остаться прежним.Переименованы некоторые устаревшие методы из классов TileService и TileBuilder:
TileBuilders:getTimeline()доgetTileTimeline()иsetTimeline()доsetTileTimeline()TileService: значение "onResourcesRequest()" заменено на "onTileResourcesRequest()".RequestBuilders.TileRequest:getDeviceParameters()→getDeviceConfiguration(),setDeviceParameters()→setDeviceConfiguration(),getState()→getCurrentState()иsetState()→setCurrentState().