Jetpack Compose Android:嵌套LazyRow失焦时如何记忆聚焦元素位置
实现嵌套LazyColumn+LazyRow的焦点位置记忆(类似YouTube TV)
完全可行,你之前遇到的问题核心是用单个remember变量存储索引会被所有LazyRow共享,导致切换行时状态混乱。解决思路是给每一行LazyRow分配独立的状态容器,单独存储各自的聚焦索引和滚动状态。
核心方案
- 独立状态存储:用映射结构(比如
MutableStateMap)分别记录每一行的聚焦元素索引和滚动状态,键为行的索引,值为对应行的状态。 - 状态持久化:用
rememberSaveable存储聚焦索引,避免配置变更(比如屏幕旋转)导致状态丢失。 - 自动恢复逻辑:当LazyRow重新获得焦点时,根据存储的索引自动滚动到目标位置并恢复焦点。
代码示例
@Composable fun NestedLazyListsWithFocusRestore() { // 模拟数据源:外层LazyColumn的行数据,每行包含多个子项 val rowDataList = remember { List(5) { rowIndex -> List(20) { itemIndex -> "Row $rowIndex - Item $itemIndex" } } } // 存储每行的聚焦元素索引:key=行索引,value=该行聚焦的元素索引 val focusedIndices = rememberSaveable { mutableStateMapOf<Int, Int>() } // 存储每行LazyRow的滚动状态,确保每行独立控制 val rowScrollStates = remember { mutableStateMapOf<Int, LazyListState>() } // 存储每个元素的焦点请求器,用于主动恢复焦点 val focusRequesters = remember { mutableStateMapOf<Pair<Int, Int>, FocusRequester>() } LazyColumn(modifier = Modifier.fillMaxSize()) { itemsIndexed(rowDataList) { rowIndex, rowItems -> LazyRow( state = rowScrollStates.getOrPut(rowIndex) { rememberLazyListState() }, modifier = Modifier .fillMaxWidth() .height(180.dp) .padding(vertical = 8.dp) .focusable() ) { itemsIndexed(rowItems, key = { itemIndex -> "$rowIndex-$itemIndex" }) { itemIndex, itemText -> val focusRequester = focusRequesters.getOrPut(rowIndex to itemIndex) { FocusRequester() } val isFocused = remember { mutableStateOf(false) } Box( modifier = Modifier .size(150.dp, 160.dp) .padding(horizontal = 4.dp) .focusRequester(focusRequester) .focusable() .onFocusChanged { isFocused.value = it.isFocused if (it.isFocused) { // 记录当前行的聚焦元素索引 focusedIndices[rowIndex] = itemIndex } } .background(if (isFocused.value) Color.DarkGray else Color.LightGray), contentAlignment = Alignment.Center ) { Text(text = itemText, color = Color.White) } } } // 当行加载完成后,恢复之前的聚焦位置 LaunchedEffect(rowIndex) { val savedIndex = focusedIndices[rowIndex] ?: 0 // 滚动到保存的位置 rowScrollStates[rowIndex]?.scrollToItem(savedIndex) // 主动请求焦点到目标元素 focusRequesters[rowIndex to savedIndex]?.requestFocus() } } } }
关键细节说明
- 状态隔离:通过
MutableStateMap给每一行分配独立的LazyListState和聚焦索引,彻底避免不同行之间的状态干扰。 - 焦点主动恢复:使用
FocusRequester在LazyRow加载完成后,主动将焦点定位到之前保存的元素,实现和YouTube TV一致的体验。 - 配置变更兼容:
rememberSaveable会自动序列化存储的索引,确保屏幕旋转、进程重启后状态不丢失。
内容的提问来源于stack exchange,提问作者Mymon
相关产品推荐
相关产品推荐

