Afficher des informations dynamiques dans des cartes

À partir de la version 1.2 des cartes, vous pouvez diffuser des mises à jour de données de plate-forme en flux continu à l'aide d'expressions dynamiques. Vous pouvez ensuite associer ces mises à jour aux animations de vos cartes. Votre application reçoit cette valeur toutes les secondes.

Avec les expressions dynamiques, il n'est pas nécessaire d'actualiser toute la carte lorsque son contenu change. Pour créer une expérience plus attrayante dans vos cartes, animez ces objets dynamiques.

Associer des expressions dynamiques à des sources de données

Les espaces de noms androidx.wear.protolayout et androidx.wear.protolayout.material contiennent de nombreuses classes dont les champs acceptent les expressions dynamiques. Exemples :

Pour utiliser une expression dynamique comme valeur possible pour un élément de votre carte, utilisez le type de propriété dynamique *Prop correspondant de l'élément et transmettez la source de données à la méthode setDynamicValue() de la classe de compilateur du type de propriété dynamique.

Les cartes sont compatibles avec les types de propriétés dynamiques suivants :

Lorsque vous utilisez une expression dynamique qui affecte les dimensions physiques (n'importe quelle valeur d'une carte, à l'exception de la couleur), vous devez également spécifier un ensemble de contraintes associées, comme un format de chaîne. Ces contraintes permettent au moteur de rendu système de déterminer l'espace maximal qu'une valeur peut occuper dans votre carte. En règle générale, vous spécifiez ces contraintes au niveau de l'élément, et non au niveau de l'expression dynamique, en appelant une méthode commençant par setLayoutConstraintsForDynamic*.

L'extrait de code suivant montre comment afficher les mises à jour d'une fréquence cardiaque à trois chiffres, avec la valeur de remplacement -- :

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

Utiliser un petit nombre d'expressions dans une même carte

Wear OS limite le nombre d'expressions qu'une seule carte peut contenir. Si une carte contient un trop grand nombre d'expressions dynamiques au total, les valeurs dynamiques sont ignorées, et le système revient aux valeurs statiques que vous fournissez aux types de propriétés dynamiques respectifs.

Vous pouvez ajouter en toute sécurité l'ensemble d'expressions suivant à une carte, car il n'y a pas beaucoup d'expressions au total. Par conséquent, la carte se comporte correctement :

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

Cependant, cette carte peut comporter trop d'expressions :

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

Regrouper des données dynamiques dans un objet d'état

Vous pouvez consolider le dernier ensemble de mises à jour de sources de données dans un état, que vous transmettez à votre carte pour le rendu de la valeur.

Pour utiliser des informations d'état dans vos cartes, procédez comme suit :

  1. Établissez un ensemble de clés qui représentent les différentes valeurs d'état de votre carte. Cet exemple crée des clés pour la consommation d'eau et une note :

    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. Dans votre implémentation de onTileRequest(), appelez setState() et établissez les mises en correspondance initiales de chaque clé avec une valeur de données dynamiques spécifique :

    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. Lorsque vous créez votre mise en page, à un endroit où vous souhaitez afficher ces données à partir de l'état, utilisez un objet de type Dynamic*. Vous pouvez également appeler animate() pour afficher une animation entre la valeur précédente et la valeur actuelle :

    Kotlin

    DynamicInt32.from(KEY_WATER_INTAKE).animate()
    

    Java

    DynamicInt32.from(KEY_WATER_INTAKE).animate();
    
  4. Si nécessaire, vous pouvez également mettre à jour l'état avec de nouvelles valeurs. Cela peut faire partie de l'élément LoadAction d'une carte.

    Dans cet exemple, la valeur de consommation d'eau est remplacée par 400 :

    Kotlin

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

    Java

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