Os blocos podem fazer mais do que apenas mostrar informações. Eles também podem ser interativos.
Para fazer com que um elemento, como textButton()
, responda a toques, gere um manipulador
de clique usando clickable()
e o associe ao elemento de layout.
É possível configurar um Clickable
para acionar uma ação de duas maneiras principais:
- Iniciar uma atividade diretamente: use
launchAction()
para casos em que você precisa abrir uma atividade imediatamente. - Delegar ao serviço de Bloco: use
loadAction()
para acionar a lógica noTileService
. Essa é uma abordagem mais flexível que permite atualizar o conteúdo do bloco, atualizar o estado dele ou iniciar uma atividade mais complexa.
Iniciar uma atividade exportada
Se um toque do usuário precisar iniciar uma atividade imediatamente, use launchAction()
.
Forneça uma ComponentName
para identificar a atividade. A atividade precisa ser
exportada. Com essa abordagem, é possível transmitir extras Intent
com a ação.
No entanto, não é possível definir flags Intent
personalizadas.
O exemplo a seguir mostra como criar uma Clickable
para iniciar TileActivity
com dois extras, name
e 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), ), ) ), )
Na atividade iniciada, extraia os valores dos extras da 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") // ... }
Processar interações no serviço de Bloco
Para interações mais flexíveis, use loadAction()
. Quando um usuário toca em um
elemento configurado com loadAction
, o sistema invoca novamente o
TileService.onTileRequest()
. Isso permite executar a lógica no serviço para
atualizar o bloco, mudar o estado dele e realizar tarefas mais complexas.
Atualizar o conteúdo do bloco
O uso mais simples de loadAction
é sinalizar uma atualização. Chame loadAction
sem argumentos. Quando tocado, o sistema chama onTileRequest()
, permitindo
que o serviço retorne um novo layout com conteúdo atualizado.
textButton( onClick = clickable(loadAction()), labelContent = { text("Refresh".layoutString) }, )
Distinguir entre vários elementos interativos
Se o bloco tiver vários elementos interativos, você poderá associar um ID
ao modificador Clickable
:
textButton( labelContent = { text("Deep Link me!".layoutString, typography = BODY_LARGE) }, onClick = clickable(id = "foo", action = loadAction()), )
No onTileRequest()
, você pode verificar esse ID usando
requestParams.currentState.lastClickableId
para decidir qual ação realizar.
Exemplo: como iniciar uma atividade com um link direto
Esse padrão é ideal para iniciar uma atividade com um link direto. O toque
do usuário recarrega o bloco, seu serviço inspeciona o ID e inicia a nova
atividade. Para controlar a backstack, use um TaskStackBuilder
e ofereça uma
experiência de navegação melhor para o usuário. Quando o usuário toca no elemento, ele
é direcionado diretamente para a tela com link direto (a tela message_detail/1
do
exemplo). Como .addNextIntentWithParentStack()
foi usado, a
atividade pai também é adicionada à backstack. Isso significa que, se o usuário deslizar
para trás, ele vai navegar até a tela principal do app (MessageList
no
exemplo) em vez de sair imediatamente para o bloco. Deslizar de volta uma segunda vez
os leva de volta ao bloco.
override fun onTileRequest( requestParams: RequestBuilders.TileRequest ): ListenableFuture<Tile?> { val lastClickableId = requestParams.currentState.lastClickableId if (lastClickableId == "foo") { TaskStackBuilder.create(this) .addNextIntentWithParentStack( Intent( Intent.ACTION_VIEW, "googleandroidsnippets://app/message_detail/1".toUri(), this, TileActivity::class.java, ) ) .startActivities() } // ... User didn't tap a button (either first load or tapped somewhere else) // ... }
Em seguida, em TileActivity
, configure a navegação para corresponder ao
padrão googleandroidsnippets://app/message_detail/{id}
.
AppScaffold { val navController = rememberSwipeDismissableNavController() SwipeDismissableNavHost( navController = navController, startDestination = "message_list", ) { // ... composable( route = "message_detail/{id}", deepLinks = listOf( navDeepLink { uriPattern = "googleandroidsnippets://app/message_detail/{id}" } ), ) { val id = it.arguments?.getString("id") ?: "0" MessageDetails(details = "message $id") } } }
Use TaskStackBuilder
para oferecer uma melhor experiência de navegação ao usuário.
Quando o usuário toca no elemento, ele é direcionado diretamente para a tela
com link direto. Neste exemplo, é a tela message_detail/1
. Como
.addNextIntentWithParentStack()
foi usado, a atividade pai também é
adicionada à backstack. Isso significa que, se o usuário deslizar para trás, ele vai navegar
até a tela principal do app (MessageList
no exemplo) em vez de sair
imediatamente para o bloco. Deslizar para trás uma segunda vez retorna os itens ao bloco.
Atualizar o estado dentro do bloco
Seu bloco tem um objeto StateBuilders.State
que armazena pares de chave-valor
e persiste em recarregar. Use loadAction()
para atualizar esse estado
quando um usuário interagir com o Bloco.
Para fazer isso, transmita um DynamicDataMap
para loadAction()
contendo os novos
valores de estado.
textButton( labelContent = { text("loadAction()".layoutString, typography = BODY_LARGE) }, onClick = clickable( action = loadAction( dynamicDataMapOf( stringAppDataKey("name") mapTo "Javier", intAppDataKey("age") mapTo 37, ) ) ), )
Quando onTileRequest()
é acionado por essa ação, é possível ler os dados
atualizados de requestParams.currentState.stateMap
. Isso é útil para interações
que modificam diretamente os dados no Bloco, como incrementar um contador ou alternar uma
configuração.
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")] } // ... }