如需开始在应用中提供功能块,请在应用的 build.gradle
文件中添加以下依赖项。
Groovy
dependencies { // Use to implement support for wear tiles implementation "androidx.wear.tiles:tiles:1.4.0-alpha04" // Use to utilize standard components and layouts in your tiles implementation "androidx.wear.protolayout:protolayout:1.2.0-alpha04" // Use to utilize components and layouts with Material Design in your tiles implementation "androidx.wear.protolayout:protolayout-material:1.2.0-alpha04" // Use to include dynamic expressions in your tiles implementation "androidx.wear.protolayout:protolayout-expression:1.2.0-alpha04" // Use to preview wear tiles in your own app debugImplementation "androidx.wear.tiles:tiles-renderer:1.4.0-alpha04" // Use to fetch tiles from a tile provider in your tests testImplementation "androidx.wear.tiles:tiles-testing:1.4.0-alpha04" }
Kotlin
dependencies { // Use to implement support for wear tiles implementation("androidx.wear.tiles:tiles:1.4.0-alpha04") // Use to utilize standard components and layouts in your tiles implementation("androidx.wear.protolayout:protolayout:1.2.0-alpha04") // Use to utilize components and layouts with Material Design in your tiles implementation("androidx.wear.protolayout:protolayout-material:1.2.0-alpha04") // Use to include dynamic expressions in your tiles implementation("androidx.wear.protolayout:protolayout-expression:1.2.0-alpha04") // Use to preview wear tiles in your own app debugImplementation("androidx.wear.tiles:tiles-renderer:1.4.0-alpha04") // Use to fetch tiles from a tile provider in your tests testImplementation("androidx.wear.tiles:tiles-testing:1.4.0-alpha04") }
创建功能块
如需在应用中提供功能块,请创建一个扩展 TileService
的类并实现相应方法,如以下代码示例所示:
Kotlin
// Uses the ProtoLayout namespace for tile timeline objects. // If you haven't done so already, migrate to the ProtoLayout namespace. import androidx.wear.protolayout.TimelineBuilders.Timeline import androidx.wear.protolayout.material.Text import androidx.wear.tiles.TileBuilders.Tile private val RESOURCES_VERSION = "1" class MyTileService : TileService() { override fun onTileRequest(requestParams: RequestBuilders.TileRequest) = Futures.immediateFuture(Tile.Builder() .setResourcesVersion(RESOURCES_VERSION) .setTileTimeline( Timeline.fromLayoutElement( Text.Builder(this, "Hello world!") .setTypography(Typography.TYPOGRAPHY_DISPLAY1) .setColor(argb(0xFF000000.toInt())) .build())) .build()) override fun onTileResourcesRequest(requestParams: ResourcesRequest) = Futures.immediateFuture(Resources.Builder() .setVersion(RESOURCES_VERSION) .build() ) }
Java
// Uses the ProtoLayout namespace for tile timeline objects. // If you haven't done so already, migrate to the ProtoLayout namespace. import androidx.wear.protolayout.TimelineBuilders.Timeline; import androidx.wear.protolayout.material.Text; import androidx.wear.tiles.TileBuilders.Tile; public class MyTileService extends TileService { private static final String RESOURCES_VERSION = "1"; @NonNull @Override protected ListenableFuture<Tile> onTileRequest( @NonNull TileRequest requestParams ) { return Futures.immediateFuture(new Tile.Builder() .setResourcesVersion(RESOURCES_VERSION) .setTileTimeline( Timeline.fromLayoutElement( new Text.Builder(this, "Hello world!") .setTypography(Typography.TYPOGRAPHY_DISPLAY1) .setColor(ColorBuilders.argb(0xFF000000)) .build())) .build() ); } @NonNull @Override protected ListenableFuture<Resources> onTileResourcesRequest( @NonNull ResourcesRequest requestParams ) { return Futures.immediateFuture(new Resources.Builder() .setVersion(RESOURCES_VERSION) .build() ); } }
接下来,在 AndroidManifest.xml
文件的 <application>
标记内添加服务。
<service android:name=".MyTileService" android:label="@string/tile_label" android:description="@string/tile_description" android:icon="@drawable/tile_icon_round" android:roundIcon="@drawable/tile_icon_round" 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 过滤器会将此服务注册为功能块提供程序。
当用户在手机或手表上配置功能块时,系统会向用户显示图标、标签和说明。
借助预览元数据标记,可以在用户在手机上配置功能块时显示功能块预览。
功能块服务生命周期概览
在应用清单中创建并声明 TileService
后,您就可以响应功能块服务的状态更改。
TileService
是一项 [绑定服务][37]。由于您的应用请求或系统需要与它通信,您的 TileService
会绑定。典型的 [绑定服务生命周期][38] 包含以下四种回调方法:[onCreate()
][39]、[onBind()
][40]、[onUnbind()
][41] 和 [onDestroy()
][42]。每当服务进入新的生命周期阶段时,系统都会调用这些方法。
除了控制绑定服务生命周期的回调之外,您还可以实现特定于 TileService
生命周期的其他方法。所有功能块服务都必须实现 onTileRequest()
和 onTileResourcesRequest()
,以响应系统发出的更新请求。
[
onTileAddEvent()
][43]:仅当用户首次添加功能块,并且用户移除并重新添加功能块时,系统才会调用此方法。此时最适合执行任何一次性初始化。onTileAddEvent()
仅在功能块集重新配置时才调用,而不是在系统创建功能块时调用。例如,当设备重新启动或开机时,系统不会针对已添加的功能块调用onTileAddEvent()
。您可以改用 [getActiveTilesAsync()
][50] 来获取属于您的哪些功能块的快照。[
onTileRemoveEvent()
][44]:仅当用户移除功能块时,系统才会调用此方法。[
onTileEnterEvent()
][45]:当此提供程序提供的功能块进入屏幕视图时,系统会调用此方法。[
onTileLeaveEvent()
][46]:当此提供程序提供的功能块离开屏幕上的视图时,系统会调用此方法。[
onTileRequest()
][47]:当系统向该提供程序请求新的 [时间轴][51] 时,系统会调用此方法。[
onTileResourcesRequest()
][48]:当系统向此提供程序请求 [资源包][49] 时,系统会调用此方法。首次加载功能块或资源版本发生变化时,可能会发生这种情况。
查询哪些功能块处于活动状态
使用中的功能块是指已添加到手表上显示的功能块。使用 TileService
的静态方法 [getActiveTilesAsync()
][50] 查询属于您的应用的哪些功能块处于活动状态。
为功能块创建界面
功能块的布局是使用构建器模式编写的。功能块布局构建完毕后就像是一棵由布局容器和基本布局元素组成的树。每个布局元素都有其属性,您可以通过各种 setter 方法设置这些属性。
基本布局元素
支持 protolayout
库中的以下视觉元素,以及 [Material 组件][2]:
- [
Text
][3]:渲染文本字符串,可以选择换行。 - [
Image
][4]:渲染图片。 - [
Spacer
][5]:设置元素之间的内边距,也可在设置背景颜色时用作分隔线。
Material 组件
除了基本元素之外,protolayout-material
库还提供了一些组件,用于确保功能块设计符合 Material Design 界面建议。
- [
Button
][6]:可点击的圆形组件,用于包含图标。 [
Chip
][7]:可点击的体育场形组件,最多可包含两行文本和一个可选图标。[
CompactChip
][9]:可点击的体育场形组件,用于包含一行文本。[
TitleChip
][10]:可点击的体育场形组件,与Chip
类似,但高度更高,可容纳标题文本。[
CircularProgressIndicator
][11]:可放置在EdgeContentLayout
内的圆形进度指示器,用于在屏幕边缘显示进度。
布局容器
支持以下容器以及 [Material 布局][12]:
- [
Row
][13]:一个接一个地水平排列子元素。 - [
Column
][14]:一个接一个地垂直排列子元素。 - [
Box
][15]:将一个子元素叠加在另一个子元素上。 - [
Arc
][16]:沿着环形排列子元素。 - [
Spannable
][17]:适用于特定的 [FontStyles
][18] 部分文本以及交错文本和图片。如需了解详情,请参阅 [Spannable][19]。
每个容器可以包含一个或多个子元素,这些子元素本身也可以是容器。例如,Column
可以包含多个 Row
元素作为子元素,从而形成类似于网格的布局。
例如,由一个容器布局和两个子布局元素组成的功能块可能如下所示:
Kotlin
private fun myLayout(): LayoutElement = Row.Builder() .setWidth(wrap()) .setHeight(expand()) .setVerticalAlignment(VALIGN_BOTTOM) .addContent(Text.Builder() .setText("Hello world") .build() ) .addContent(Image.Builder() .setResourceId("image_id") .setWidth(dp(24f)) .setHeight(dp(24f)) .build() ).build()
Java
private LayoutElement myLayout() { return new Row.Builder() .setWidth(wrap()) .setHeight(expand()) .setVerticalAlignment(VALIGN_BOTTOM) .addContent(new Text.Builder() .setText("Hello world") .build() ) .addContent(new Image.Builder() .setResourceId("image_id") .setWidth(dp(24f)) .setHeight(dp(24f)) .build() ).build(); }
Material 布局
除了基本布局之外,protolayout-material
库还提供了几种固定布局,用于将元素放到特定的“槽位”中。
[
PrimaryLayout
][20]:将单个主要操作CompactChip
放在底部,内容居中置于其上。[
MultiSlotLayout
][22]:放置主标签和次级标签,并在两者之间放置可选内容,并在底部放置可选的CompactChip
。[
MultiButtonLayout
][23]:确定一组根据 Material 准则排列的按钮的位置。[
EdgeContentLayout
][24]:将内容沿着屏幕边缘放置,例如CircularProgressIndicator
。使用此布局时,系统会自动为其中的内容应用适当的外边距和内边距。
弧形
支持以下 Arc
容器子元素:
- [
ArcLine
][25]:沿着弧形渲染一条曲线。 - [
ArcText
][26]:沿着弧形渲染曲线文本。 - [
ArcAdapter
][27]:沿着弧形渲染基本布局元素,在弧线的切线方向上进行绘制。
如需了解详情,请参阅每种元素类型的 [参考文档][28]。
修饰符
对于每种可用的布局元素,您都可以选择对其应用修饰符。这些修饰符有以下用途:
- 更改布局的视觉外观。例如,为布局元素添加背景、边框或内边距。
- 添加有关布局的元数据。例如,为布局元素添加 semantics 修饰符,以便与屏幕阅读器结合使用。
- 添加功能。例如,为布局元素添加 clickable 修饰符,使用户可与功能块互动。如需了解详情,请参阅 [与功能块互动][29]。
例如,我们可以自定义 [Image
][4] 的默认外观和元数据,如以下代码示例所示:
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
][31] 个子元素。不允许填充其他子元素或嵌套的 Spannable
实例。
Span
子元素有两种类型:
- [
SpanText
][32]:渲染特定样式的文本。 - [
SpanImage
][33]:渲染内嵌文本的图片。
例如,您可以将“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()
][34] 方法并手动提供所有资源。
在 onTileResourcesRequest()
方法中,您可以通过以下两种方式提供图片:
- 使用 [
setAndroidResourceByResId()
][35] 提供可绘制资源。 - 使用 [
setInlineResource()
][36] 以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() ); }
为您推荐
- 注意:当 JavaScript 处于关闭状态时,系统会显示链接文字
- 迁移到 ProtoLayout 命名空间
- Compose 中的
ConstraintLayout
- 为应用创建自定义“快捷设置”功能块