Interagir avec les cartes

Les cartes ne servent pas seulement à afficher des informations. Elles peuvent aussi être interactives. Pour qu'un élément tel que textButton() réponde aux appuis, générez un gestionnaire de clics à l'aide de clickable() et associez-le à l'élément de mise en page.

Vous pouvez configurer un Clickable pour déclencher une action de deux manières principales :

  1. Lancer une activité directement : utilisez launchAction() lorsque vous devez ouvrir une activité immédiatement.
  2. Déléguer à votre service de carte : utilisez loadAction() pour déclencher une logique dans votre TileService. Il s'agit d'une approche plus flexible qui vous permet d'actualiser le contenu de la carte, de mettre à jour son état ou de lancer une activité plus complexe.

Lancer une activité exportée

Si un appui de l'utilisateur doit lancer immédiatement une activité, utilisez launchAction(). Fournissez un ComponentName pour identifier l'activité. L'activité doit être exportée. Avec cette approche, vous pouvez transmettre des extras Intent avec l'action. Toutefois, vous ne pouvez pas définir d'options Intent personnalisées.

L'exemple suivant montre comment créer un Clickable pour lancer TileActivity avec deux extras, name et age :

textButton(
    labelContent = {
        text("launchAction()".layoutString, typography = BODY_LARGE)
    },
    onClick =
    clickable(
        action =
        launchAction(
            ComponentName(
                "com.example.wear",
                "com.example.wear.snippets.m3.tile.TileActivity",
            ),
            mapOf(
                "name" to ActionBuilders.stringExtra("Bartholomew"),
                "age" to ActionBuilders.intExtra(21),
            ),
        )
    ),
)

Dans l'activité lancée, récupérez les valeurs des extras d'intent :

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // When this activity is launched from the tile InteractionLaunchAction,
    // "name" will be "Bartholomew" and "age" will be 21
    val name = intent.getStringExtra("name")
    val age = intent.getStringExtra("age")

    // ...
}

Gérer les interactions dans votre service de cartes

Pour des interactions plus flexibles, utilisez loadAction(). Lorsqu'un utilisateur appuie sur un élément configuré avec loadAction, le système réappelle votre TileService.onTileRequest(). Vous pouvez ainsi exécuter une logique dans votre service pour mettre à jour la carte, modifier son état et effectuer des tâches plus complexes.

Actualiser le contenu de la carte

L'utilisation la plus simple de loadAction consiste à signaler une actualisation. Appelez loadAction sans argument. Lorsque l'utilisateur appuie sur l'élément, le système appelle onTileRequest(), ce qui permet à votre service de renvoyer une nouvelle mise en page avec du contenu mis à jour.

textButton(
    onClick = clickable(loadAction()),
    labelContent = { text("Refresh".layoutString) },
)

Distinguer les différents éléments interactifs

Si votre carte contient plusieurs éléments interactifs, vous pouvez associer un ID au modificateur Clickable :

Dans onTileRequest(), vous pouvez vérifier cet ID à l'aide de requestParams.currentState.lastClickableId pour décider de l'action à effectuer.

Exemple : Lancer une activité avec un lien profond

Ce modèle est idéal pour lancer une activité avec un lien profond. Lorsque l'utilisateur appuie sur la carte, elle est actualisée. Votre service inspecte l'ID, puis lance la nouvelle activité. Pour contrôler la pile "Retour", utilisez un TaskStackBuilder afin d'offrir une meilleure expérience de navigation à l'utilisateur. Lorsque l'utilisateur appuie sur l'élément, il est directement redirigé vers l'écran associé au lien profond (l'écran message_detail/1 de l'exemple). Étant donné que .addNextIntentWithParentStack() a été utilisé, l'activité parente est également ajoutée à la pile "Retour". Cela signifie que si l'utilisateur balaie l'écran vers l'arrière, il revient à l'écran principal de l'application (MessageList dans l'exemple) au lieu de quitter immédiatement la carte. Si vous balayez l'écran vers l'arrière une seconde fois, vous revenez à la carte.

Ensuite, dans TileActivity, configurez votre navigation pour qu'elle corresponde au modèle googleandroidsnippets://app/message_detail/{id}.

Utilisez TaskStackBuilder pour offrir une meilleure expérience de navigation à l'utilisateur. Lorsque l'utilisateur appuie sur l'élément, il est directement redirigé vers l'écran du lien profond (dans cet exemple, l'écran message_detail/1). Étant donné que .addNextIntentWithParentStack() a été utilisé, l'activité parente est également ajoutée à la pile "Retour". Cela signifie que si l'utilisateur balaie l'écran vers l'arrière, il accédera à l'écran principal de l'application (MessageList dans l'exemple) au lieu de quitter immédiatement la carte. Si vous balayez l'écran vers l'arrière une deuxième fois, vous revenez à la carte.

Mettre à jour l'état dans la carte

Votre carte contient un objet StateBuilders.State qui stocke des paires clé-valeur et persiste lors des rechargements. Vous pouvez utiliser loadAction() pour mettre à jour cet état lorsqu'un utilisateur interagit avec la carte.

Pour ce faire, transmettez un DynamicDataMap à loadAction() contenant les nouvelles valeurs d'état.

textButton(
    labelContent = {
        text("loadAction()".layoutString, typography = BODY_LARGE)
    },
    onClick =
    clickable(
        action =
        loadAction(
            dynamicDataMapOf(
                stringAppDataKey("name") mapTo "Javier",
                intAppDataKey("age") mapTo 37,
            )
        )
    ),
)

Lorsque onTileRequest() est déclenché par cette action, vous pouvez lire les données mises à jour à partir de requestParams.currentState.stateMap. Cela est utile pour les interactions qui modifient directement les données de la carte, comme l'incrémentation d'un compteur ou l'activation/la désactivation d'un paramètre.

override fun onTileRequest(
    requestParams: RequestBuilders.TileRequest
): ListenableFuture<Tile> {

    // When triggered by loadAction(), "name" will be "Javier", and "age" will
    // be 37.
    with(requestParams.currentState.stateMap) {
        val name = this[stringAppDataKey("name")]
        val age = this[intAppDataKey("age")]
    }

    // ...
}