Плитки могут делать больше, чем просто отображать информацию; они также могут быть интерактивными. Чтобы элемент, такой как textButton()
реагировал на нажатия, сгенерируйте обработчик щелчков с помощью clickable()
и свяжите его с элементом макета.
Вы можете настроить Clickable
для запуска действия двумя основными способами:
- Запустите действие напрямую : используйте
launchAction()
в случаях, когда вам нужно немедленно открыть действие. - Делегируйте своей службе плиток : используйте
loadAction()
для запуска логики в вашейTileService
. Это более гибкий подход, который позволяет вам обновлять содержимое плитки, обновлять ее состояние или запускать более сложную активность.
Запуск экспортируемой деятельности
Если нажатие пользователя должно немедленно запустить действие, используйте launchAction()
. Укажите ComponentName
для идентификации действия. Действие должно быть экспортировано . При таком подходе вы можете передавать Intent
extras с действием. Однако невозможно установить пользовательские флаги Intent
.
В следующем примере показано, как создать Clickable
для запуска TileActivity
с двумя дополнительными данными: name
и 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), ), ) ), )
Внутри запущенной активности извлеките значения из дополнительных намерений:
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
:
textButton( labelContent = { text("Deep Link me!".layoutString, typography = BODY_LARGE) }, onClick = clickable(id = "foo", action = loadAction()), )
Внутри onTileRequest()
вы можете проверить этот идентификатор с помощью requestParams.currentState.lastClickableId
, чтобы решить, какое действие выполнить.
Пример: запуск действия с глубокой ссылкой
Этот шаблон идеально подходит для запуска действия с глубокой ссылкой . Касание пользователя перезагружает плитку, ваш сервис проверяет идентификатор, а затем запускает новое действие. Чтобы управлять стеком переходов назад, используйте 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
, который хранит пары ключ-значение и сохраняется при перезагрузках. Вы можете использовать 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")] } // ... }