要开始从您的应用提供磁贴,请在您的应用的 build.gradle
文件中包含以下依赖项。
Groovy
dependencies { // Use to implement support for wear tiles implementation "androidx.wear.tiles:tiles:1.4.0" // Use to utilize standard components and layouts in your tiles implementation "androidx.wear.protolayout:protolayout:1.2.0" // Use to utilize components and layouts with Material Design in your tiles implementation "androidx.wear.protolayout:protolayout-material:1.2.0" // Use to include dynamic expressions in your tiles implementation "androidx.wear.protolayout:protolayout-expression:1.2.0" // Use to preview wear tiles in your own app debugImplementation "androidx.wear.tiles:tiles-renderer:1.4.0" // Use to fetch tiles from a tile provider in your tests testImplementation "androidx.wear.tiles:tiles-testing:1.4.0" }
Kotlin
dependencies { // Use to implement support for wear tiles implementation("androidx.wear.tiles:tiles:1.4.0") // Use to utilize standard components and layouts in your tiles implementation("androidx.wear.protolayout:protolayout:1.2.0") // Use to utilize components and layouts with Material Design in your tiles implementation("androidx.wear.protolayout:protolayout-material:1.2.0") // Use to include dynamic expressions in your tiles implementation("androidx.wear.protolayout:protolayout-expression:1.2.0") // Use to preview wear tiles in your own app debugImplementation("androidx.wear.tiles:tiles-renderer:1.4.0") // Use to fetch tiles from a tile provider in your tests testImplementation("androidx.wear.tiles:tiles-testing:1.4.0") }
创建磁贴
要从您的应用提供磁贴,请创建一个扩展 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>
权限和意图过滤器将此服务注册为磁贴提供程序。
图标、标签和描述将在用户在手机或手表上配置磁贴时显示给用户。
使用预览元数据标签在手机上配置磁贴时显示磁贴的预览。
磁贴服务生命周期概述
在您的应用清单中创建并声明了 TileService
后,您可以对磁贴服务的狀態更改做出响应。
TileService
是一种 绑定服务。您的 TileService
作为您应用请求的结果绑定,或者系统需要与之通信。典型的 绑定服务生命周期 包含以下四个回调方法:onCreate()
、onBind()
、onUnbind()
和 onDestroy()
。每次服务进入新的生命周期阶段时,系统都会调用这些方法。
除了控制绑定服务生命周期的回调之外,您还可以实现其他特定于 TileService
生命周期的方法。所有磁贴服务都必须实现 onTileRequest()
和 onTileResourcesRequest()
来响应来自系统的更新请求。
onTileAddEvent()
: 仅当用户首次添加您的磁贴时,系统才会调用此方法,如果用户移除并再次添加您的磁贴,系统也会调用此方法。这是执行任何一次性初始化的最佳时间。onTileAddEvent()
仅在瓦片集被_重新配置_时调用,而不是在系统_创建_瓦片时调用。例如,当设备重启或开机时,对于已添加的瓦片,onTileAddEvent()
不会被调用。您可以使用getActiveTilesAsync()
来获取_属于您_的活动瓦片快照。onTileRemoveEvent()
: 系统仅在用户移除您的瓦片时调用此方法。onTileEnterEvent()
: 当此提供程序提供的瓦片出现在屏幕上时,系统会调用此方法。onTileLeaveEvent()
: 当此提供程序提供的瓦片从屏幕上消失时,系统会调用此方法。onTileRequest()
: 当系统请求此提供程序的新 时间线 时,系统会调用此方法。onTileResourcesRequest()
: 当系统请求此提供程序的 资源包 时,系统会调用此方法。这可能发生在首次加载瓦片时,或者资源版本发生变化时。
查询哪些瓦片处于活动状态
活动瓦片是指已添加到手表上显示的瓦片。使用 TileService
的静态方法 getActiveTilesAsync()
来查询 _属于您的应用程序_ 的活动瓦片。
创建瓦片的 UI
瓦片的布局使用构建器模式编写。瓦片的布局像一棵树一样构建,这棵树由布局容器和基本布局元素组成。每个布局元素都有属性,您可以通过各种 setter 方法设置这些属性。
基本布局元素
以下来自 protolayout
库的视觉元素受支持,以及 Material 组件
Material 组件
除了基本元素之外,protolayout-material
库还提供了一些组件,这些组件可以确保瓦片设计符合 Material Design 用户界面建议。
Button
: 可点击的圆形组件,旨在包含图标。Chip
: 可点击的体育场形状组件,旨在包含最多两行文本和一个可选图标。CompactChip
: 可点击的体育场形状组件,旨在包含一行文本。TitleChip
: 可点击的体育场形状组件,类似于Chip
,但高度更大,可以容纳标题文本。CircularProgressIndicator
: 圆形进度指示器,可以放置在EdgeContentLayout
内,以显示围绕屏幕边缘的进度。
布局容器
以下容器受支持,以及 Material 布局
Row
: 将子元素水平排列,一个接一个。Column
: 将子元素垂直排列,一个接一个。Box
: 将子元素叠加在一起。Arc
: 将子元素以圆形排列。Spannable
: 对文本部分应用特定的FontStyles
,以及交错文本和图像。有关更多信息,请参阅 Spannables。
每个容器都可以包含一个或多个子元素,这些子元素本身也可以是容器。例如,一个 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
: 将单个主 actionCompactChip
放在底部,内容居中在其上方。MultiSlotLayout
: 将主标签和副标签与可选内容放在一起,并在底部提供一个可选的CompactChip
。MultiButtonLayout
: 将一组按钮放置在 Material 指南规定的位置。EdgeContentLayout
: 将内容放置在屏幕边缘,例如CircularProgressIndicator
。当使用此布局时,其中的内容会自动应用适当的边距和填充。
圆弧
以下 Arc
容器子元素受支持
ArcLine
: 围绕圆弧渲染一条弯曲的线。ArcText
: 在圆弧中渲染弯曲的文本。ArcAdapter
: 在圆弧中渲染一个基本布局元素,该元素在与圆弧相切的位置绘制。
有关更多信息,请参阅每种元素类型的 参考文档。
修饰符
每个可用的布局元素都可以选择应用修饰符。将这些修饰符用于以下目的
- 更改布局的视觉外观。例如,向您的布局元素添加背景、边框或填充。
- 添加有关布局的元数据。例如,向您的布局元素添加语义修饰符以供屏幕阅读器使用。
- 添加功能。例如,向您的布局元素添加可点击修饰符,以使您的瓦片具有交互性。有关更多信息,请参阅 与瓦片交互。
例如,我们可以自定义 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(); }
Spannables
Spannable
是一种特殊的容器类型,它以类似于文本的方式布局元素。当您想对较大文本块中的一个子字符串应用不同的样式时,这很有用,而使用 Text
元素是无法实现的。
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() ); }
为您推荐
- 注意:当 JavaScript 关闭时,会显示链接文本。
- 迁移到 ProtoLayout 命名空间
ConstraintLayout
在 Compose 中- 为您的应用创建自定义快速设置磁贴