功能块版本控制

在 Wear OS 设备上,功能块由两个具有独立版本控制的关键组件呈现。为确保您的应用功能块在所有设备上正常运行,请务必了解此底层架构。

  • 与 Jetpack 功能块相关的库:这些库(包括 Wear 功能块和 Wear ProtoLayout)会嵌入到您的应用中,并且您(作为开发者)可以控制其版本。您的应用会使用这些库来构建 TileBuilder.Tile 对象(表示功能块的数据结构),以响应系统的 onTileRequest() 调用。
  • ProtoLayout 渲染程序:此系统组件负责在显示屏上渲染 Tile 对象并处理用户互动。渲染程序的版本不受应用开发者控制,并且可能会因设备而异,即使是硬件相同的设备也是如此。

功能块的外观或行为可能会因应用的 Jetpack Tiles 库版本以及用户设备上的 ProtoLayout Renderer 版本而异。例如,一台设备可能支持旋转或显示心率数据,而另一台设备可能不支持。

本文档介绍了如何确保您的应用与不同版本的功能块库和 ProtoLayout 渲染程序兼容,以及如何迁移到更高版本的 Jetpack 库。

考虑兼容性

如需创建在各种设备上都能正常运行的功能块,您应考虑以下事项。

检测渲染程序版本

  • 使用传递给 onTileRequest() 方法的 DeviceParameters 对象的 getRendererSchemaVersion() 方法。此方法会返回设备上 ProtoLayout Renderer 的主要版本号和次要版本号。
  • 然后,您可以在 onTileRequest() 实现中使用条件逻辑,根据检测到的渲染程序版本调整功能块的设计或行为。
    • 例如,如果系统不支持特定动画,您可以改为显示静态图片。

@RequiresSchemaVersion 注解

  • ProtoLayout 方法上的 @RequiresSchemaVersion 注解表示该方法按文档中所述的方式运行所需的最小渲染程序架构版本(示例)。
    • 虽然调用需要高于设备上可用版本的渲染程序版本的方法不会导致应用崩溃,但可能会导致内容无法显示或系统忽略相应功能。

示例

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 Renderer 更新由 Play 商店或系统更新提供。无法强制安装特定的渲染程序版本。)

Android Studio 的功能块预览功能会使用嵌入在代码依赖的 Jetpack ProtoLayout 库中的渲染程序,因此另一种方法是在测试功能块时依赖于不同的 Jetpack 库版本。

迁移到功能块 1.5 / ProtoLayout 1.3(Material 3 Expressive)

更新您的 Jetpack 功能块库,以利用最新的增强功能,包括界面更改,以便功能块与系统无缝集成。

Jetpack Tiles 1.5 和 Jetpack ProtoLayout 1.3 引入了多项值得注意的改进和变更。其中包括:

  • 用于描述界面的类似 Compose 的 API。
  • Material 3 富有表现力组件,包括底部紧贴边缘的按钮,以及对增强型视觉效果的支持: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-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 实现(包括任何资源加载机制)应该只需进行最少的修改或无需修改。

主要例外情况涉及功能块 activity 跟踪:如果您的应用使用 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 布局函数的比较

M2.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()

M3

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

如需突出显示主要区别,请执行以下操作:

  1. 移除 Builder。Material3 界面组件的传统构建器模式已被更具声明性的 Compose 语法取代。(String/Color/Modifier 等非界面组件也获得了新的 Kotlin 封装容器。)
  2. 标准化初始化和布局函数。M3 布局依赖于标准化初始化和结构函数:materialScope()primaryLayout()。这些必需函数会初始化 M3 环境(主题设置、通过 materialScope 定义的组件作用域)并定义基于主要槽的布局(通过 primaryLayout)。对于每个布局,必须恰好调用这两个函数各一次。

主题

颜色

Material 3 Expressive 的一大亮点是“动态主题”功能:启用此功能(默认处于开启状态)的功能块将以系统提供的主题显示(具体取决于用户的设备和配置)。

M3 中的另一项变更是颜色令牌数量从 4 增加到了 29。您可以在 ColorScheme 类中找到新的颜色令牌。

排版

与 M2.5 类似,M3 非常依赖于预定义的字号常量,不建议直接指定字号。这些常量位于 Typography 类中,提供了更多更具表现力的选项。

如需了解完整详情,请参阅排版文档

形状

大多数 M3 组件都可以在形状和颜色维度上有所不同。

形状为 fulltextButton(在 mainSlot 中):

采用“完整”形状(圆角更大)的功能块
图 2.:采用“全局”形状的功能块

形状为 small 的相同 textButton:

采用“小”形状(圆角较少)的功能块
图 3.:形状为“small”的功能块

组件

M3 组件比 M2.5 组件更加灵活且可配置。M2.5 通常需要使用不同的组件来实现不同的视觉效果,而 M3 通常采用具有良好默认值的通用且高度可配置的“基础”组件。

此原则适用于“根”布局。在 M2.5 中,此值为 PrimaryLayoutEdgeContentLayout。在 M3 中,建立单个顶级 MaterialScope 后,系统会调用 primaryLayout() 函数。这会直接返回根布局(无需构建器),并且它接受多个“槽”(例如 titleSlotmainSlotbottomSlot)的 LayoutElements。这些槽位可以填充具体的界面组件(例如 text()button()card() 返回的组件)或布局结构(例如 LayoutElementBuilders 中的 RowColumn)。

主题是 M3 的另一项重要增强功能。默认情况下,界面元素会自动遵循 M3 样式规范并支持动态主题设置。

M2.5 M3
互动元素
ButtonChip
Text
Text text()
进度指示器
CircularProgressIndicator circularProgressIndicator()segmentedCircularProgressIndicator()
布局
PrimaryLayoutEdgeContentLayout primaryLayout()
buttonGroup()
Images
icon()avatarImage()backgroundImage()

修饰符

在 M3 中,用于修饰或增强组件的 Modifiers 更像 Compose。此更改可以通过自动构建适当的内部类型来减少样板代码。(这项变更与使用 M3 界面组件无关;如有必要,您可以将 ProtoLayout 1.2 中的构建器样式修饰符与 M3 界面组件搭配使用,反之亦然。)

M2.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()

M3

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

您可以使用任一 API 样式构建修饰符,还可以使用 toProtoLayoutModifiers() 扩展函数将 LayoutModifier 转换为 ModifiersBuilders.Modifier

辅助函数

虽然 ProtoLayout 1.3 允许使用 Compose 风格的 API 表达许多界面组件,但 LayoutElementBuilders 中的等基础布局元素仍会使用构建器模式。为了弥合这种样式差异并与新的 M3 组件 API 保持一致,不妨考虑使用辅助函数。

不使用帮助程序

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))
        }
    }
)

迁移到功能块 1.2 / ProtoLayout 1.0

从版本 1.2 开始,大多数功能块布局 API 都位于 androidx.wear.protolayout 命名空间中。如需使用最新的 API,请在代码中完成以下迁移步骤。

更新依赖项

在应用模块的 build 文件中,进行以下更改:

Groovy

  // 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

  // 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.EventBuildersandroidx.wear.tiles.RequestBuildersandroidx.wear.tiles.TileBuildersandroidx.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()