当用户使用硬件键盘或 D-pad 导航复杂的界面时,经常会在屏幕的不同部分之间移动,例如从侧边导航栏跳转到内容列表,与某个项目互动,然后再跳回来。借助焦点恢复功能,容器可以记住之前获得焦点的子元素,并在焦点重新进入容器时将焦点恢复到该子元素。
恢复可滚动容器中的焦点
在 Jetpack Compose 中,LazyColumn、LazyRow 和 LazyVerticalGrid 等可滚动容器会自动充当焦点组。如需在延迟列表或网格上启用焦点恢复,请附加 focusRestorer 修饰符:
LazyColumn( modifier = Modifier.focusRestorer() ) { items(items) { item -> Button(onClick = { /* Handle click */ }) { Text(item) } } }
当焦点退出 LazyColumn 时,容器会记录哪个项处于活动状态。
当用户返回到列表时,焦点会恢复到该项,而不是从顶部重新开始。
恢复 Row 和 Column 中的焦点
与延迟布局容器不同,标准非滚动布局容器(例如 Row 和 Column)默认情况下不会建立焦点边界。由于它们本身不会拦截焦点进入事件,因此仅应用 focusRestorer 修饰符是不够的。
如需在 Row 或 Column 上启用焦点恢复功能,请将 focusGroup 与 focusRestorer 结合使用:
Row( modifier = Modifier .focusGroup() .focusRestorer() ) { Button(onClick = { /* Action 1 */ }) { Text("First item") } Button(onClick = { /* Action 2 */ }) { Text("Second item") } Button(onClick = { /* Action 3 */ }) { Text("Third item") } }
指定后备焦点目标
当焦点首次进入容器时,不存在之前获得焦点的子项。Compose 会遵循默认焦点顺序来确定要聚焦哪个子项。
您可以传递回退 FocusRequester 给 focusRestorer(或在较早的 Compose 版本中传递返回 FocusRequester 的 lambda),以自定义首次进入时哪个子项获得焦点:
val firstItemRequester = remember { FocusRequester() } LazyColumn( modifier = Modifier.focusRestorer(firstItemRequester) ) { itemsIndexed(items) { index, item -> val itemModifier = if (index == 0) { Modifier.focusRequester(firstItemRequester) } else { Modifier } Button( onClick = { /* Handle click */ }, modifier = itemModifier ) { Text(item) } } }
如果之前没有聚焦任何子级(或者焦点恢复失败),Compose 会将焦点移至关联的后备目标。
将焦点直接置于导航组件中的所选内容上
在 NavigationRail、NavigationBar、NavigationDrawer 或 TabRow 等导航组件中,用户希望在从内容区域进入组件时,焦点落在当前所选的目标位置上。
您可以结合使用 Modifier.focusGroup() 和 focusProperties 并设置 onEnter 属性(或在较早的 Compose 版本中设置 enter),将焦点直接指向活动目的地:
val (homeRequester, searchRequester, settingsRequester) = remember { FocusRequester.createRefs() } val selectedRequester = when (currentDestination) { "home" -> homeRequester "search" -> searchRequester "settings" -> settingsRequester else -> homeRequester } NavigationRail( modifier = modifier .focusProperties { // Redirect focus to the selected item when focus enters the navigation rail onEnter = { selectedRequester.requestFocus() } } .focusGroup() ) { NavigationRailItem( selected = currentDestination == "home", onClick = { onNavigate("home") }, icon = { Icon(Icons.Default.Home, contentDescription = "Home") }, label = { Text("Home") }, modifier = Modifier.focusRequester(homeRequester) ) NavigationRailItem( selected = currentDestination == "search", onClick = { onNavigate("search") }, icon = { Icon(Icons.Default.Search, contentDescription = "Search") }, label = { Text("Search") }, modifier = Modifier.focusRequester(searchRequester) ) NavigationRailItem( selected = currentDestination == "settings", onClick = { onNavigate("settings") }, icon = { Icon(Icons.Default.Settings, contentDescription = "Settings") }, label = { Text("Settings") }, modifier = Modifier.focusRequester(settingsRequester) ) }
在屏幕过渡期间恢复焦点
当用户点击某项内容以导航到详情界面,随后使用“返回”按钮或手势返回时,您可以将焦点恢复到用户点击的内容。
如需在屏幕过渡期间保留和恢复焦点,请执行以下操作:
- 将容器与
FocusRequester相关联。 - 在触发导航事件之前,请在容器的
FocusRequester上调用saveFocusedChild()。 - 当用户返回到相应界面时,请求将初始焦点放在容器上或调用
restoreFocusedChild()。