タイルは、情報を表示するだけでなく、インタラクティブにすることもできます。textButton()
などの要素をタップに反応させるには、clickable()
を使用してクリック ハンドラを生成し、レイアウト要素に関連付けます。
アクションをトリガーするように Clickable
を構成する方法は主に 2 つあります。
- アクティビティを直接起動する: アクティビティをすぐに開く必要がある場合は、
launchAction()
を使用します。 - タイルサービスに委任する:
loadAction()
を使用して、TileService
内のロジックをトリガーします。これはより柔軟なアプローチで、タイルのコンテンツを更新したり、状態を更新したり、より複雑なアクティビティを起動したりできます。
エクスポートされたアクティビティを起動する
ユーザーがタップしたときにすぐにアクティビティを起動する必要がある場合は、launchAction()
を使用します。アクティビティを識別する ComponentName
を指定します。アクティビティはエクスポートされている必要があります。この方法では、アクションとともに Intent
エクストラを渡すことができます。ただし、カスタム Intent
フラグを設定することはできません。
次の例は、2 つのエクストラ(name
と age
)を使用して TileActivity
を起動する Clickable
を作成する方法を示しています。
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), ), ) ), )
起動されたアクティビティ内で、インテント エクストラから値を取得します。
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") // ... }
タイルサービスでインタラクションを処理する
より柔軟なインタラクションを実現するには、loadAction()
を使用します。ユーザーが loadAction
で構成された要素をタップすると、システムは TileService.onTileRequest()
を再び呼び出します。これにより、サービスでロジックを実行して、タイルを更新したり、状態を変更したり、より複雑なタスクを実行したりできます。
タイルのコンテンツを更新する
loadAction
の最も簡単な用途は、更新を通知することです。引数なしで loadAction
を呼び出します。タップされると、システムは onTileRequest()
を呼び出し、サービスは更新されたコンテンツを含む新しいレイアウトを返すことができます。
textButton( onClick = clickable(loadAction()), labelContent = { text("Refresh".layoutString) }, )
複数のインタラクティブな要素を区別する
タイルに複数のインタラクティブ要素が含まれている場合は、Clickable
修飾子に ID を関連付けることができます。
textButton( labelContent = { text("Deep Link me!".layoutString, typography = BODY_LARGE) }, onClick = clickable(id = "foo", action = loadAction()), )
onTileRequest()
内で requestParams.currentState.lastClickableId
を使用してこの ID を確認することで、実行するアクションを決定できます。
例: ディープリンクを使用してアクティビティを起動する
このパターンは、ディープリンクを使用してアクティビティを起動する場合に適しています。ユーザーがタップするとタイルが再読み込みされ、サービスが ID を検査して新しいアクティビティを起動します。バックスタックを制御するには、TaskStackBuilder
を使用してユーザーに優れたナビゲーション エクスペリエンスを提供します。ユーザーが要素をタップすると、ディープリンクが設定された画面(例の message_detail/1
画面)に直接移動します。.addNextIntentWithParentStack()
が使用されているため、親アクティビティもバックスタックに追加されます。つまり、ユーザーが [戻る] をスワイプすると、タイルにすぐに移動するのではなく、アプリのメイン画面(この例では MessageList
)に移動します。もう一度後方にスワイプすると、タイルに移動します。
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) // ... }
次に、TileActivity
で、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") } } }
TaskStackBuilder
を使用して、ユーザーのナビゲーション エクスペリエンスを向上させます。ユーザーが要素をタップすると、ディープリンクが設定された画面(この例では message_detail/1
画面)に直接移動します。.addNextIntentWithParentStack()
が使用されたため、親アクティビティもバックスタックに追加されます。つまり、ユーザーが「戻る」をスワイプすると、タイルにすぐに移動するのではなく、アプリのメイン画面(この例では MessageList
)に移動します。もう一度後方にスワイプすると、タイルに戻ります。
タイル内の状態を更新する
タイルに StateBuilders.State
オブジェクトがあり、Key-Value ペアを保存して再読み込み後も保持します。ユーザーがタイルを操作したときに、loadAction()
を使用してこの状態を更新できます。
これを行うには、新しい状態値を含む DynamicDataMap
を loadAction()
に渡します。
textButton( labelContent = { text("loadAction()".layoutString, typography = BODY_LARGE) }, onClick = clickable( action = loadAction( dynamicDataMapOf( stringAppDataKey("name") mapTo "Javier", intAppDataKey("age") mapTo 37, ) ) ), )
このアクションによって onTileRequest()
がトリガーされると、更新されたデータを requestParams.currentState.stateMap
から読み取ることができます。これは、タイルのデータを直接変更する操作(カウンタの増分や設定の切り替えなど)に便利です。
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")] } // ... }