タイルに動的な更新を表示する

Tiles 1.2 以降では、動的式を使用して、プラットフォーム データの更新をストリーミングできます。そのうえでこのような更新をタイル内のアニメーションに関連付けられます。アプリはこの値を毎秒更新します。

動的式を使用すると、コンテンツを変更したときにタイル全体を更新する必要がありません。タイルでより魅力的な体験ができるようにするには、そのような動的オブジェクトをアニメーション化します。

動的式をデータソースに関連付ける

androidx.wear.protolayout 名前空間と androidx.wear.protolayout.material 名前空間には、フィールドで動的式が使えるクラスが多数含まれています。以下はその一例です。

タイル内の要素の許容値として動的式を使用するには、要素に対応する *Prop 動的プロパティ型を使用して、データソースを動的プロパティ型のビルダークラスの setDynamicValue() メソッドに渡します。

タイルは次の動的プロパティ型をサポートしています。

物理寸法(色以外のタイル内の値)に影響を与える動的式を使用する場合は、文字列形式などの関連する制約も指定する必要があります。これらの制約に基づき、システム レンダラはタイル内で値が占有できる最大スペースを決定します。通常、この制約は動的式レベルではなく、要素レベルで指定します。指定するには 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()
    );
}

1 つのタイル内で限られた数の式を使用する

Wear OS では、1 つのタイルに設定できる式の数に上限が設定されています。タイルに含まれる動的式が多すぎる場合、動的な値は無視され、システムはそれぞれの動的プロパティ型で指定された静的な値にフォールバックします。

次の式のセットは式の数が多すぎないため、タイルに問題なく追加できます。その結果、タイルは正常に動作します。

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

動的データを状態オブジェクトに統合する

データソースからの最新の更新セットを状態に統合して、それをタイルに渡し、値をレンダリングできます。

タイルの状態情報を使用する手順は次のとおりです。

  1. タイルの状態のさまざまな値を表すキーのセットを設定します。この例では、水分摂取量とメモのキーを作成します。

    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");
    
  2. 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()
        );
    }
    
  3. レイアウトを作成する際に、状態からのこのデータを表示する場所では Dynamic* タイプのオブジェクトを使用します。animate() を呼び出して、前の値から現在の値までのアニメーションを表示することもできます。

    Kotlin

    DynamicInt32.from(KEY_WATER_INTAKE).animate()
    

    Java

    DynamicInt32.from(KEY_WATER_INTAKE).animate();
    
  4. 必要に応じて、新しい値を使って状態を更新することもできます。これはタイルの LoadAction の一部として設定できます。

    この例では、水分摂取量を 400 に更新します。

    Kotlin

    state.addKeyToValueMapping(KEY_WATER_INTAKE,
            DynamicDataBuilders.DynamicDataValue.fromInt(400))
    

    Java

    state.addKeyToValueMapping(KEY_WATER_INTAKE,
            DynamicDataBuilders.DynamicDataValue.fromInt(400));