Pokazuj dynamiczne aktualizacje w kafelkach

Od wersji 1.2 możesz przesyłać strumieniowo aktualizacje danych platformy za pomocą wyrażeń dynamicznych. Aktualizacje możesz następnie powiązać z animacjami w kafelkach. Aplikacja aktualizuje tę wartość co sekundę.

Korzystając z wyrażeń dynamicznych, nie musisz odświeżać całego kafelka po zmianie jego zawartości. Aby zwiększyć atrakcyjność kafelków, animuj te obiekty dynamiczne.

Powiązywanie wyrażeń dynamicznych ze źródłami danych

Przestrzenie nazw androidx.wear.protolayout i androidx.wear.protolayout.material zawierają wiele klas, których pola akceptują wyrażenia dynamiczne. Oto kilka przykładów:

Aby użyć wyrażenia dynamicznego jako możliwej wartości elementu w kafelku, użyj odpowiedniego typu właściwości dynamicznej *Prop tego elementu i przekaż źródło danych do metody setDynamicValue() klasy konstruktora typu właściwości dynamicznej.

Kafelki obsługują te typy właściwości dynamicznych:

Gdy używasz wyrażenia dynamicznego, które ma wpływ na wymiary fizyczne – dowolną wartość w kafelku z wyjątkiem koloru – musisz też określić zestaw powiązanych ograniczeń, takich jak format ciągu znaków. Te ograniczenia pozwalają mechanizmowi renderowania systemowego określić maksymalną ilość miejsca, jaką dana wartość może zająć w kafelku. Zwykle te ograniczenia określa się na poziomie elementu, a nie na poziomie wyrażeń dynamicznych, wywołując metodę, która zaczyna się od setLayoutConstraintsForDynamic*.

Poniższy fragment kodu pokazuje, jak wyświetlać aktualizacje tętna za pomocą 3 cyfr z wartością zastępczą --:

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

Używanie niewielkiej liczby wyrażeń w jednym kafelku

Wear OS nakłada limit na liczbę wyrażeń, które może zawierać pojedynczy kafelek. Jeśli kafelek zawiera łącznie zbyt wiele wyrażeń dynamicznych, wartości dynamiczne są ignorowane, a system wraca do wartości statycznych podanych przez Ciebie dla poszczególnych typów właściwości dynamicznych.

Do kafelka możesz bezpiecznie dodać poniższy zestaw wyrażeń, ponieważ łączna liczba wyrażeń nie jest zbyt duża. W związku z tym kafelek działa prawidłowo:

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

Ten kafelek może jednak zawierać zbyt wiele wyrażeń:

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

Konsolidacja danych dynamicznych w obiekcie stanu

Możesz skonsolidować najnowszy zestaw aktualizacji ze źródeł danych w stan, który przekazujesz do kafelka na potrzeby renderowania wartości.

Aby korzystać w swoich kafelkach z informacji o stanie, wykonaj te czynności:

  1. Ustal zestaw kluczy reprezentujących różne wartości stanu kafelka. W tym przykładzie tworzymy klucze do ilości wody oraz notatkę:

    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. W swojej implementacji onTileRequest() wywołaj metodę setState() i ustal początkowe mapowania z każdego klucza na konkretną wartość danych dynamicznych:

    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. Podczas tworzenia układu w miejscu, w którym chcesz wyświetlać dane ze stanu, użyj obiektu typu Dynamic*. Możesz też wywołać funkcję animate(), aby wyświetlić animację od poprzedniej do bieżącej wartości:

    Kotlin

    DynamicInt32.from(KEY_WATER_INTAKE).animate()
    

    Java

    DynamicInt32.from(KEY_WATER_INTAKE).animate();
    
  4. W razie potrzeby możesz też zaktualizować stan, dodając nowe wartości. Może być on częścią LoadAction kafelka.

    W tym przykładzie ilość wody zostaje zmieniona na 400:

    Kotlin

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

    Java

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