Mostra aggiornamenti dinamici nei riquadri

A partire da Tile 1.2, puoi trasmettere in streaming gli aggiornamenti dei dati della piattaforma utilizzando le espressioni dinamiche. Puoi quindi associare questi aggiornamenti alle animazioni nei riquadri. La tua app riceve aggiornamenti a questo valore ogni secondo.

Utilizzando le espressioni dinamiche, non è necessario aggiornare l'intero riquadro quando ne cambiano i contenuti. Per creare un'esperienza più coinvolgente con i riquadri, anima gli oggetti dinamici.

Associare espressioni dinamiche alle origini dati

Gli spazi dei nomi androidx.wear.protolayout e androidx.wear.protolayout.material contengono molte classi i cui campi accettano espressioni dinamiche. Tra gli esempi di tali prodotti o servizi figurano:

Per utilizzare un'espressione dinamica come possibile valore per un elemento nel riquadro, utilizza il tipo di proprietà dinamica *Prop corrispondente e trasmetti l'origine dati al metodo setDynamicValue() della classe del generatore del tipo di proprietà dinamica.

I riquadri supportano i seguenti tipi di proprietà dinamiche:

Quando utilizzi un'espressione dinamica che influisce sulle dimensioni fisiche (qualsiasi valore in un riquadro tranne il colore), devi specificare anche un insieme di vincoli correlati, ad esempio un formato stringa. Questi vincoli consentono al renderer di sistema di determinare la quantità massima di spazio che un valore potrebbe occupare nel tuo riquadro. In genere, per specificare questi vincoli a livello di elemento, non a livello di espressione dinamica, richiama un metodo che inizia con setLayoutConstraintsForDynamic*.

Il seguente snippet di codice mostra come visualizzare gli aggiornamenti di una frequenza cardiaca utilizzando tre cifre, con un valore di riserva di --:

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

Utilizzare un numero ridotto di espressioni all'interno di un singolo riquadro

Wear OS pone un limite al numero di espressioni che può avere un singolo riquadro. Se un riquadro contiene troppe espressioni dinamiche totali, i valori dinamici vengono ignorati e il sistema utilizza i valori statici da te forniti ai rispettivi tipi di proprietà dinamiche.

Puoi aggiungere tranquillamente il seguente insieme di espressioni a un riquadro, perché non esistono molte espressioni totali. Pertanto, il riquadro si comporta correttamente:

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

Tuttavia, questo riquadro potrebbe contenere troppe espressioni:

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

Consolidare i dati dinamici in un oggetto di stato

Puoi consolidare l'ultimo insieme di aggiornamenti provenienti dalle origini dati in uno stato, che dovrai trasferire al tuo riquadro per il rendering del valore.

Per utilizzare le informazioni sullo stato nei riquadri, completa questi passaggi:

  1. Definisci un set di chiavi che rappresenti i diversi valori dello stato del riquadro. In questo esempio vengono create le chiavi per la presa di acqua e una nota:

    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. Nell'implementazione di onTileRequest(), chiama setState() e stabilisci le mappature iniziali da ogni chiave a un determinato valore dei dati dinamici:

    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. Quando crei il layout, utilizza un oggetto di tipo Dynamic* in un punto in cui vuoi mostrare questi dati sullo stato. Puoi anche chiamare animate() per mostrare un'animazione dal valore precedente a quello corrente:

    Kotlin

    DynamicInt32.from(KEY_WATER_INTAKE).animate()
    

    Java

    DynamicInt32.from(KEY_WATER_INTAKE).animate();
    
  4. Se necessario, puoi anche aggiornare lo stato con nuovi valori. Può far parte di un riquadro LoadAction.

    In questo esempio, il valore della quantità di acqua assunta viene aggiornato a 400:

    Kotlin

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

    Java

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