从 Tiles 1.2 开始,您可以使用动态表达式流式传输平台数据更新。然后,您可以将这些更新与磁贴中的动画关联。您的应用每秒都会收到此值的更新。
使用动态表达式,您无需在内容更改时刷新整个磁贴。为了在磁贴中创建更具吸引力的体验,请为这些动态对象添加动画。
将动态表达式与数据源关联
androidx.wear.protolayout
和 androidx.wear.protolayout.material
命名空间包含许多类,它们的字段接受动态表达式。一些示例包括:
- 多个长度值,包括
Arc
对象的长度和CircularProgressIndicator
对象的长度。 - 任何颜色,例如
Button
对象的内容颜色。 - 许多字符串值,包括
Text
对象的内容、LayoutElementsBuilders.Text
对象的内容以及CircularProgressIndicator
对象的内容说明。
要将动态表达式用作磁贴中元素的可能值,请使用元素的相应 *Prop
动态属性类型,并将数据源传递给动态属性类型的构建器类的 setDynamicValue()
方法。
磁贴支持以下动态属性类型:
- 对于以显示无关像素为单位测量的线性尺寸,使用
DimensionBuilders.DpProp
。 - 对于以度为单位测量的角度尺寸,使用
DimensionBuilders.DegreesProp
。 - 对于字符串值,使用
TypeBuilders.StringProp
。 - 对于颜色值,使用
ColorBuilders.ColorProp
。 - 对于浮点值,使用
TypeBuilders.FloatProp
。
当您使用的动态表达式影响物理尺寸(磁贴中除颜色以外的任何值)时,您还必须指定一组相关的约束,例如字符串格式。这些约束允许系统渲染器确定一个值在磁贴中可能占据的最大空间量。通常,您通过调用以 setLayoutConstraintsForDynamic*
开头的方法,在元素级别而不是动态表达式级别指定这些约束。
以下代码片段展示了如何使用 3 位数字显示心率更新,并将 --
作为回退值:
Kotlin
import androidx.wear.protolayout.material.Text public override fun onTileRequest(requestParams: RequestBuilders.TileRequest) = Futures.immediateFuture(Tile.Builder() .setResourcesVersion(RESOURCES_VERSION) .setFreshnessIntervalMillis(60 * 60 * 1000) // 60 minutes .setTileTimeline(Timeline.fromLayoutElement( Text.Builder(this, TypeBuilders.StringProp.Builder("--") .setDynamicValue(PlatformHealthSources.heartRateBpm() .format() .concat(DynamicBuilders.DynamicString.constant(" bpm"))) .build(), StringLayoutConstraint.Builder("000") .build() ).build() ) ).build() )
Java
import androidx.wear.protolayout.material.Text; @Override protected ListenableFuture<Tile> onTileRequest( @NonNull TileRequest requestParams ) { return Futures.immediateFuture(new Tile.Builder() .setResourcesVersion(RESOURCES_VERSION) .setFreshnessIntervalMillis(60 * 60 * 1000) // 60 minutes .setTileTimeline(Timeline.fromLayoutElement( new Text.Builder( this, new TypeBuilders.StringProp.Builder("--") .setDynamicValue(PlatformHealthSources.heartRateBpm() .format() .concat(DynamicBuilders.DynamicString.constant(" bpm"))) .build(), new StringLayoutConstraint.Builder("000") .build() ).build()) ).build() ); }
在单个磁贴中使用少量表达式
Wear OS 对单个磁贴可以拥有的表达式数量设有上限。如果磁贴包含的动态表达式总数过多,动态值将被忽略,系统将回退到您提供给相应动态属性类型的静态值。
您可以安全地将以下表达式集添加到磁贴中,因为总表达式数量不多。因此,磁贴行为正确:
Kotlin
val personHealthInfo = DynamicString.constant("This person has walked ") .concat(PlatformHealthSources.dailySteps() .div(1000) .format()) .concat("thousands of steps and has a current heart rate ") .concat(PlatformHealthSources.heartRateBpm() .format()) .concat(" beats per minute")
Java
DynamicString personHealthInfo = DynamicString.constant("This person has walked ") .concat(PlatformHealthSources.dailySteps() .div(1000) .format()) .concat("thousands of steps and has a current heart rate ") .concat(PlatformHealthSources.heartRateBpm() .format()) .concat(" beats per minute");
然而,这个磁贴可能包含过多的表达式:
Kotlin
// Note that this template is applied as many times as the loop iterates. // The system doesn't reuse dynamic expressions. val dynamicStringTemplate = PlatformHealthSources.dailySteps() .div(1000) .format() for (person in people) { // SomeProperty .setDynamicValue( DynamicBuilders.DynamicString.constant("Steps for ") .concat(person) .concat(" are ") .concat(dynamicStringTemplate) ) }
Java
// Note that this template is applied as many times as the loop iterates. // The system doesn't reuse dynamic expressions. DynamicString dynamicStringTemplate = PlatformHealthSources.dailySteps() .div(1000) .format(); for (int i = 0; i < people.size(); i++) { // SomeProperty .setDynamicValue( DynamicBuilders.DynamicString.constant("Steps for ") .concat(people[i]) .concat(" are ") .concat(dynamicStringTemplate) ); }
将动态数据整合到状态对象中
您可以将数据源的最新更新集整合到一个“状态”中,然后将其传递给磁贴进行值渲染。
要在磁贴中使用状态信息,请完成以下步骤:
建立一组表示磁贴状态不同值的键。此示例创建了用于表示饮水量和备注的键:
Kotlin
companion object { val KEY_WATER_INTAKE = AppDataKey<DynamicInt32>("water_intake") val KEY_NOTE = AppDataKey<DynamicString>("note") }
Java
private static final AppDataKey<DynamicInt32> KEY_WATER_INTAKE = new AppDataKey<DynamicInt32>("water_intake"); private static final AppDataKey<DynamicString> KEY_NOTE = new AppDataKey<DynamicString>("note");
在您的
onTileRequest()
实现中,调用setState()
并建立从每个键到特定动态数据值的初始映射:Kotlin
override fun onTileRequest(requestParams: TileRequest): ListenableFuture<Tile> { val state = State.Builder() .addKeyToValueMapping(KEY_WATER_INTAKE, DynamicDataBuilders.DynamicDataValue.fromInt(200)) .addKeyToValueMapping(KEY_NOTE, DynamicDataBuilders.DynamicDataValue.fromString("Note about day")) .build() // ... return Futures.immediateFuture(Tile.Builder() // Set resources, timeline, and other tile properties. .setState(state) .build() )
Java
@Override protected ListenableFuture<Tile> onTileRequest( ListenableFuture<Tile> { State state = new State.Builder() .addKeyToValueMapping(KEY_WATER_INTAKE, DynamicDataBuilders.DynamicDataValue.fromInt(200)) .addKeyToValueMapping(KEY_NOTE, DynamicDataBuilders.DynamicDataValue.fromString("Note about day")) .build(); // ... return Futures.immediateFuture(Tile.Builder() // Set resources, timeline, and other tile properties. .setState(state) .build() ); }
创建布局时,在您想要显示来自状态的数据的地方,使用
Dynamic*
类型对象。您还可以调用animate()
来显示从前一个值到当前值的动画:Kotlin
DynamicInt32.from(KEY_WATER_INTAKE).animate()
Java
DynamicInt32.from(KEY_WATER_INTAKE).animate();
需要时,您也可以使用新值更新状态。这可以是磁贴
LoadAction
的一部分。在此示例中,饮水量值更新为
400
:Kotlin
val loadAction = LoadAction.Builder() .setRequestState( State.Builder() .addKeyToValueMapping( KEY_WATER_INTAKE, DynamicDataBuilders.DynamicDataValue.fromInt(400) ) .build() ) .build()
Java
LoadAction loadAction = new LoadAction.Builder() .setRequestState( new State.Builder() .addKeyToValueMapping( KEY_WATER_INTAKE, DynamicDataBuilders.DynamicDataValue.fromInt(400) ).build() ).build();
为您推荐
- 注意:当 JavaScript 关闭时,会显示链接文本
- 迁移到 ProtoLayout 命名空间
- 磁贴入门
- 其他注意事项