如需开始在应用中提供功能块,请在应用的 build.gradle
文件中添加以下依赖项。
Groovy
dependencies { // Use to implement support for wear tiles implementation "androidx.wear.tiles:tiles:1.4.1" // Use to utilize standard components and layouts in your tiles implementation "androidx.wear.protolayout:protolayout:1.2.1" // Use to utilize components and layouts with Material Design in your tiles implementation "androidx.wear.protolayout:protolayout-material:1.2.1" // Use to include dynamic expressions in your tiles implementation "androidx.wear.protolayout:protolayout-expression:1.2.1" // Use to preview wear tiles in your own app debugImplementation "androidx.wear.tiles:tiles-renderer:1.4.1" // Use to fetch tiles from a tile provider in your tests testImplementation "androidx.wear.tiles:tiles-testing:1.4.1" }
Kotlin
dependencies { // Use to implement support for wear tiles implementation("androidx.wear.tiles:tiles:1.4.1") // Use to utilize standard components and layouts in your tiles implementation("androidx.wear.protolayout:protolayout:1.2.1") // Use to utilize components and layouts with Material Design in your tiles implementation("androidx.wear.protolayout:protolayout-material:1.2.1") // Use to include dynamic expressions in your tiles implementation("androidx.wear.protolayout:protolayout-expression:1.2.1") // Use to preview wear tiles in your own app debugImplementation("androidx.wear.tiles:tiles-renderer:1.4.1") // Use to fetch tiles from a tile provider in your tests testImplementation("androidx.wear.tiles:tiles-testing:1.4.1") }
主要概念
功能块的构建方式与 Android 应用不同,并且采用不同的概念:
- 布局模板:定义显示屏上视觉元素的整体排列方式。这可通过
primaryLayout()
函数实现。 - 布局元素:表示单个图形元素(例如按钮或卡片),或使用列、buttonGroup 或类似元素将多个此类元素分组在一起。这些元素嵌入在布局模板中。
- 资源:
ResourceBuilders.Resources
对象由用于渲染布局所需的 Android 资源(图片)的键值对映射和版本组成。 - 时间轴:
TimelineBuilders.Timeline
对象是 布局对象的一个或多个实例的列表。您可以提供各种机制和表达式,以指明渲染程序何时应从一个布局对象切换到另一个布局对象,例如在特定时间停止显示布局。 - 状态:一种类型为
StateBuilders.State
的数据结构,在功能块和应用之间传递,以便这两个组件能够相互通信。例如,如果用户点按功能块上的按钮,状态将包含该按钮的 ID。您还可以使用映射交换数据类型。 - 功能块:表示功能块的
TileBuilders.Tile
对象,由时间轴、资源版本 ID、新鲜度间隔和状态组成。 - Protolayout:此术语出现在各种与功能块相关的类的名称中,是指 Wear OS Protolayout 库,这是一个在各种 Wear OS 途径中使用的图形库。
创建功能块
如需在应用中提供功能块,请实现 TileService
类型的服务,并在清单中注册该服务。因此,系统会在调用 onTileRequest()
时请求必要的功能块,并在调用 onTileResourcesRequest()
时请求资源。
class MyTileService : TileService() { override fun onTileRequest(requestParams: RequestBuilders.TileRequest) = Futures.immediateFuture( Tile.Builder() .setResourcesVersion(RESOURCES_VERSION) .setTileTimeline( Timeline.fromLayoutElement( materialScope(this, requestParams.deviceConfiguration) { primaryLayout( mainSlot = { text("Hello, World!".layoutString, typography = BODY_LARGE) } ) } ) ) .build() ) override fun onTileResourcesRequest(requestParams: ResourcesRequest) = Futures.immediateFuture( Resources.Builder().setVersion(RESOURCES_VERSION).build() ) }
接下来,在 AndroidManifest.xml
文件的 <application>
标记内添加服务。
<service android:name=".snippets.m3.tile.MyTileService" android:label="@string/tile_label" android:description="@string/tile_description" android:icon="@mipmap/ic_launcher" android:exported="true" android:permission="com.google.android.wearable.permission.BIND_TILE_PROVIDER"> <intent-filter> <action android:name="androidx.wear.tiles.action.BIND_TILE_PROVIDER" /> </intent-filter> <meta-data android:name="androidx.wear.tiles.PREVIEW" android:resource="@drawable/tile_preview" /> </service>
权限和 intent 过滤器会将此服务注册为功能块提供程序。
当用户在手机或手表上配置功能块时,系统会向用户显示图标、标签、说明和预览资源。请注意,预览资源支持 Android 的所有标准资源限定符,因此您可以根据屏幕尺寸和设备语言等因素来调整预览。如需获取更多建议,请参阅预览核对清单。
部署应用,然后将功能块添加到功能块轮播界面(还有更适合开发者的功能块预览方式,但目前请仅手动操作)。

如需查看完整示例,请参阅 GitHub 上的代码示例或 Codelab。
为功能块创建界面
Material 3 富有表现力的界面元素是使用由 Kotlin 的类型安全的构建器模式提供支持的结构化方法创建的。
布局
如需创建布局,请执行以下操作:
启动 Material Design 作用域:调用
materialScope()
函数,提供所需的context
和deviceConfiguration
。您可以添加可选参数,例如allowDynamicTheme
和defaultColorScheme
。allowDynamicTheme
默认为true
,defaultColorScheme
表示在动态配色不可用(例如用户已关闭该功能)或设备不支持该功能或allowDynamicTheme
为false
时使用的ColorScheme
。在作用域内构建界面:给定功能块布局的所有界面组件都必须在单个顶级 materialScope() 调用的 lambda 中定义。这些组件函数(例如
primaryLayout()
和textEdgeButton()
)是MaterialScope
上的扩展函数,只有在接收器作用域中调用时才可用。materialScope( context = context, deviceConfiguration = requestParams.deviceConfiguration, // requestParams is passed to onTileRequest defaultColorScheme = myFallbackColorScheme ) { // inside the MaterialScope, you can call functions like primaryLayout() primaryLayout( titleSlot = { text(text = "Title".layoutString) }, mainSlot = { text(text = "Main Content".layoutString) }, bottomSlot = { textEdgeButton(text = "Action".layoutString) } ) }
老虎机
在 M3 中,功能块布局采用了 Compose 风格的方法,该方法使用三个不同的槽。从上到下依次为:
titleSlot
,通常用于主要标题或标题。mainSlot
,用于核心内容。bottomSlot
,通常用于操作或补充信息。边缘按钮也会显示在此处。

每个槽的内容如下所示:
titleSlot
(可选):通常是text()
生成的几个字词。mainSlot
(必填):按行、列和按钮组等结构整理的组件。这些组件还可以递归嵌套在彼此内部;例如,列可以包含行。bottomSlot
(可选):通常填充边缘贴合按钮或文本标签。
由于功能块无法滚动,因此没有用于分页、滚动或处理长内容列表的组件。请注意,当字体大小增大或文本因翻译而变长时,内容是否仍可见。
界面组件
protolayout-material3
库提供了大量根据 Material 3 Expressive 规范和界面建议设计的组件。
按钮
- textButton():具有一个用于(短)文本内容的槽位的按钮
- iconButton():具有一个用于表示图标的槽位的按钮
- avatarButton():药丸形头像按钮,最多可提供三个槽,用于放置代表垂直堆叠标签和辅助标签的内容,以及旁边的图片(头像)
- imageButton():可点击的图片按钮,不提供其他槽,仅提供图片(例如,backgroundImage 作为背景)
- compactButton():紧凑按钮,最多提供两个槽位来接收水平堆叠的内容,表示图标及其旁边的文本
- button():药丸形按钮,最多提供三个槽位来接收内容,表示垂直堆叠的标签和辅助标签,以及旁边的图标
边缘按钮
- iconEdgeButton():边缘按钮,提供一个槽位来接收图标或类似的圆形小内容
- textEdgeButton():边缘按钮,提供单个槽位来接收文本或类似的长宽内容
卡片
- titleCard():提供一到三个槽位的片头卡片,通常基于文本
- appCard():最多提供 5 个槽位的应用卡片,通常基于文本
- textDataCard():最多可提供三个垂直堆叠的槽的数据卡片,通常基于文本或数字
- iconDataCard():最多可提供三个垂直堆叠的槽的数据卡片,通常基于文本或数字,并带有图标
- graphicDataCard():图形数据卡片,提供一个用于图形数据(例如进度指示器)的槽,以及最多两个垂直堆叠的槽(通常用于文本说明)
进度指示器
- circularProgressIndicator():使用放射状元素指示目标达成进度
- segmentedCircularProgressIndicator():使用具有不同阶段的径向元素指示实现目标的进度
对布局元素进行分组
- buttonGroup():一种组件布局,用于按水平序列放置其子级
- primaryLayout():全屏布局,表示建议的 M3 布局样式,该样式具有自适应性,并负责处理元素的放置,以及应用建议的外边距和内边距
主题
在 Material 3 Expressive 中,颜色系统由 29 个标准颜色角色定义,分为六组:主色、副色、第三色、错误色、表面色和轮廓色。

ColorScheme
会将这 29 个角色中的每个角色映射到相应的颜色,并且由于它是 MaterialScope
的一部分,并且必须在其中创建组件,因此组件会自动从配色方案中获取颜色。通过这种方法,所有界面元素都会自动遵循 Material Design 标准。
如需让用户在您定义的配色方案(例如反映您品牌颜色的配色方案)和系统提供的配色方案(从用户当前的表盘派生或由用户选择)之间进行选择,请按如下方式初始化 MaterialScope
:
val myColorScheme =
ColorScheme(
primary = ...
onPrimary = ...
// 27 more
)
materialScope(
defaultColorScheme = myColorScheme
) {
// If the user selects "no theme" in settings, myColorScheme is used.
// Otherwise, the system-provided theme is used.
}
如需强制功能块以您提供的配色方案显示,请将 allowDynamicTheme
设置为 false
,以停用对动态主题的支持:
materialScope(
allowDynamicTheme = false,
defaultColorScheme = myColorScheme
) {
// myColorScheme is *always* used.
}
颜色
每个单独的组件都使用 ColorScheme
定义的 29 种颜色角色中的一部分。例如,按钮最多可使用四种颜色,默认情况下,这些颜色取自活动 ColorScheme
的“主色”组:
ButtonColors 组件令牌 |
ColorScheme 角色 |
---|---|
containerColor | primary |
iconColor | onPrimary |
labelColor | onPrimary |
secondaryLabelColor | onPrimary(不透明度 0.8) |
您可能需要为特定界面元素使用与默认颜色令牌不同的颜色。例如,您可能希望一个 textEdgeButton
使用“辅色”或“第三色”组(而不是“主色”组)中的颜色,以便脱颖而出并提供更好的对比度。
您可以通过多种方式自定义组件颜色:
使用辅助函数处理预定义颜色。使用
filledTonalButtonColors()
等辅助函数为 Material 3 Expressive 应用标准按钮样式。这些函数会创建预配置的ButtonColors
实例,将填充、色调或轮廓等常见样式映射到MaterialScope
中有效ColorScheme
的适当角色。这样,您无需为常见按钮类型手动定义每种颜色,即可应用一致的样式。textEdgeButton( colors = filledButtonColors() // default /* OR colors = filledTonalButtonColors() */ /* OR colors = filledVariantButtonColors() */ // ... other parameters )
对于卡片,请使用等效的
filledCardColors()
系列函数。如果您只需要更改一个或两个令牌,还可以使用辅助函数的
copy()
方法修改辅助函数返回的ButtonColors
对象:textEdgeButton( colors = filledButtonColors() .copy( containerColor = colorScheme.tertiary, labelColor = colorScheme.onTertiary ) // ... other parameters )
明确提供替换颜色角色。创建自己的
ButtonColors
对象,并将其传递给组件。对于卡片,请使用等效的CardColors
对象。textEdgeButton( colors = ButtonColors( // the materialScope makes colorScheme available containerColor = colorScheme.secondary, iconColor = colorScheme.secondaryDim, labelColor = colorScheme.onSecondary, secondaryLabelColor = colorScheme.onSecondary ) // ... other parameters )
指定固定颜色(请谨慎使用)。虽然通常建议根据语义角色(例如
colorScheme.primary
),您还可以提供直接的颜色值。应谨慎使用此方法,因为这可能会导致与整体主题不一致,尤其是在主题动态变化的情况下。textEdgeButton( colors = filledButtonColors().copy( containerColor = android.graphics.Color.RED.argb, // Using named colors labelColor = 0xFFFFFF00.argb // Using a hex code for yellow ) // ... other parameters )
排版
为了在 Wear OS 平台上实现视觉一致性并优化性能,功能块上的所有文本均使用系统提供的字体呈现。也就是说,功能块不支持自定义字体。在 Wear OS 6 及更高版本中,这是 OEM 专用字体。在大多数情况下,它将是可变字体,可提供更具表现力的体验和更精细的控制。
如需创建文本样式,您通常会将 text()
方法与排版常量结合使用。借助此组件,您可以使用 Material 3 Expressive 中的预定义排版角色,这有助于功能块遵循已建立的排版最佳实践,从而提高可读性和层次结构。该库提供了一组 18 个语义排版常量,例如 BODY_MEDIUM。这些常量还会影响字体轴(除了尺寸之外)。
text(
text = "Hello, World!".layoutString,
typography = BODY_MEDIUM,
)
如需进行更多控制,您可以提供其他设置。在 Wear OS 6 及更高版本中,系统可能会使用可变字体,您可以沿 italic、weight、width 和 roundness 轴修改该字体。您可以使用 settings
参数控制这些轴:
text(
text = "Hello, World".layoutString,
italic = true,
// Use elements defined in androidx.wear.protolayout.LayoutElementBuilders.FontSetting
settings =
listOf(weight(500), width(100F), roundness(100)),
)
最后,如果您需要控制大小或字母间距(不建议),请使用 basicText() 而非 text(),并使用 fontStyle() 为 fontStyle
属性构造值。
形状和边距
您可以使用几乎每个组件的 shape
属性来更改其圆角半径。值来自 MaterialScope
属性 shapes
:
textButton(
height = expand(),
width = expand(),
shape = shapes.medium, // OR another value like shapes.full
colors = filledVariantButtonColors(),
labelContent = { text("Hello, World!".layoutString) },
)
更改组件形状后,如果您认为其在显示屏边缘周围留有太多或太少的空间,请使用 primaryLayout()
的 margin
参数调整边距:
primaryLayout(
mainSlot = {
textButton(
shape = shapes.small,
/* ... */
)
},
// margin constants defined in androidx.wear.protolayout.material3.PrimaryLayoutMargins
margins = MAX_PRIMARY_LAYOUT_MARGIN,
)
弧形
支持以下 Arc
容器子元素:
ArcLine
:沿着弧形渲染一条曲线。ArcText
:沿着弧形渲染曲线文本。ArcAdapter
:沿着弧形渲染基本布局元素,在弧线的切线方向上进行绘制。
如需了解详情,请参阅每种元素类型的参考文档。
修饰符
对于每种可用的布局元素,您都可以选择对其应用修饰符。这些修饰符有以下用途:
- 更改布局的视觉外观。例如,为布局元素添加背景、边框或内边距。
- 添加有关布局的元数据。例如,为布局元素添加 semantics 修饰符,以便与屏幕阅读器结合使用。
- 添加功能。例如,为布局元素添加 clickable 修饰符,使用户可与功能块互动。如需了解详情,请参阅与功能块互动。
例如,我们可以自定义 Image
的默认外观和元数据,如以下代码示例所示:
Kotlin
private fun myImage(): LayoutElement = Image.Builder() .setWidth(dp(24f)) .setHeight(dp(24f)) .setResourceId("image_id") .setModifiers(Modifiers.Builder() .setBackground(Background.Builder().setColor(argb(0xFFFF0000)).build()) .setPadding(Padding.Builder().setStart(dp(12f)).build()) .setSemantics(Semantics.builder() .setContentDescription("Image description") .build() ).build() ).build()
Java
private LayoutElement myImage() { return new Image.Builder() .setWidth(dp(24f)) .setHeight(dp(24f)) .setResourceId("image_id") .setModifiers(new Modifiers.Builder() .setBackground(new Background.Builder().setColor(argb(0xFFFF0000)).build()) .setPadding(new Padding.Builder().setStart(dp(12f)).build()) .setSemantics(new Semantics.Builder() .setContentDescription("Image description") .build() ).build() ).build(); }
Spannable
Spannable
是一种特殊的容器,其元素布局方式与文本类似。如果您想仅针对一大段文本中的一个子字符串应用不同样式,使用 Text
元素是不可能实现的,此时 Spannable 就非常有用。
Spannable
容器中填充的是 Span
子元素。不允许填充其他子元素或嵌套的 Spannable
实例。
Span
子元素有两种类型:
例如,您可以将“Hello world”功能块中的“world”设为斜体,并在两个单词之间插入图片,如以下代码示例所示:
Kotlin
private fun mySpannable(): LayoutElement = Spannable.Builder() .addSpan(SpanText.Builder() .setText("Hello ") .build() ) .addSpan(SpanImage.Builder() .setWidth(dp(24f)) .setHeight(dp(24f)) .setResourceId("image_id") .build() ) .addSpan(SpanText.Builder() .setText("world") .setFontStyle(FontStyle.Builder() .setItalic(true) .build()) .build() ).build()
Java
private LayoutElement mySpannable() { return new Spannable.Builder() .addSpan(new SpanText.Builder() .setText("Hello ") .build() ) .addSpan(new SpanImage.Builder() .setWidth(dp(24f)) .setHeight(dp(24f)) .setResourceId("image_id") .build() ) .addSpan(new SpanText.Builder() .setText("world") .setFontStyle(newFontStyle.Builder() .setItalic(true) .build()) .build() ).build(); }
使用资源
功能块无法访问应用的任何资源。这意味着您无法将 Android 图片 ID 传递给 Image
布局元素并指望应用能够对其进行解析。您需要替换 onTileResourcesRequest()
方法并手动提供所有资源。
在 onTileResourcesRequest()
方法中,您可以通过以下两种方式提供图片:
- 使用
setAndroidResourceByResId()
提供可绘制资源。 - 使用
setInlineResource()
以ByteArray
形式提供动态图片。
Kotlin
override fun onTileResourcesRequest( requestParams: ResourcesRequest ) = Futures.immediateFuture( Resources.Builder() .setVersion("1") .addIdToImageMapping("image_from_resource", ImageResource.Builder() .setAndroidResourceByResId(AndroidImageResourceByResId.Builder() .setResourceId(R.drawable.image_id) .build() ).build() ) .addIdToImageMapping("image_inline", ImageResource.Builder() .setInlineResource(InlineImageResource.Builder() .setData(imageAsByteArray) .setWidthPx(48) .setHeightPx(48) .setFormat(ResourceBuilders.IMAGE_FORMAT_RGB_565) .build() ).build() ).build() )
Java
@Override protected ListenableFuture<Resources> onTileResourcesRequest( @NonNull ResourcesRequest requestParams ) { return Futures.immediateFuture( new Resources.Builder() .setVersion("1") .addIdToImageMapping("image_from_resource", new ImageResource.Builder() .setAndroidResourceByResId(new AndroidImageResourceByResId.Builder() .setResourceId(R.drawable.image_id) .build() ).build() ) .addIdToImageMapping("image_inline", new ImageResource.Builder() .setInlineResource(new InlineImageResource.Builder() .setData(imageAsByteArray) .setWidthPx(48) .setHeightPx(48) .setFormat(ResourceBuilders.IMAGE_FORMAT_RGB_565) .build() ).build() ).build() ); }
功能块预览图片核对清单
系统会在功能块轮播界面编辑器中显示 Android 应用清单中引用的功能块预览图片。此编辑器会显示在 Wear OS 设备上,也会显示在手机上的手表配套应用中。
为帮助用户充分利用此预览图片,请验证功能块的以下详细信息:
- 反映最新设计。预览应准确反映功能块的最新设计。
- 使用静态颜色主题。使用功能块的静态配色主题,而不是动态配色主题。
- 包含应用图标。确认应用的图标显示在预览图片的顶部。
- 显示已加载/已登录状态。预览应显示完全正常的“已加载”或“已登录”状态,避免出现任何空白或占位符内容。
- 利用资源解析规则进行自定义(可选)。 不妨考虑使用 Android 的资源解析规则来提供与设备的显示大小、语言或语言区域设置相匹配的预览。如果功能块的外观因设备而异,此功能尤为有用。