当用户使用实体键盘或方向键浏览复杂的界面时,他们经常会在屏幕的不同部分之间移动,例如从侧边导航栏跳转到内容列表,与某个项目互动,然后又跳转回来。 焦点恢复功能可让容器记住之前获得焦点的子元素,并在焦点重新进入容器时将焦点恢复到该子元素。
在可滚动容器中恢复焦点
在 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()。