You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose TV应用返回主界面时恢复焦点问题排查

解决Jetpack Compose TV返回主界面后焦点丢失问题

问题根源

你用的是Compose TV 1.0.0-alpha06,这个早期测试版本的嵌套TvLazyColumn+TvLazyRow存在焦点恢复bug——从详情页返回时,系统不会自动把焦点还给之前选中的列表项,必须手动触发焦点请求。你之前试的rememberTvLazyListState只能保存滚动位置,管不了焦点;设置唯一键也解决不了这个版本的焦点逻辑问题。

可行解决方案

1. 手动记录焦点位置,返回时主动请求焦点

额外保存焦点所在的行索引和项索引,返回主界面时通过FocusRequester主动让目标项获取焦点:

// 主界面中保存焦点坐标
var focusedRowIdx by remember { mutableIntStateOf(0) }
var focusedItemIdx by remember { mutableIntStateOf(0) }
// 存储每个列表项的FocusRequester,用行列索引做键
val focusRequesters = remember { mutableMapOf<Int, MutableMap<Int, FocusRequester>>() }

TvLazyColumn(state = rememberTvLazyListState()) {
    itemsIndexed(categories) { rowIdx, category ->
        // 初始化当前行的FocusRequester Map
        if (!focusRequesters.containsKey(rowIdx)) {
            focusRequesters[rowIdx] = mutableMapOf()
        }
        TvLazyRow(
            modifier = Modifier.onFocusChanged {
                if (it.isFocused) focusedRowIdx = rowIdx
            }
        ) {
            itemsIndexed(category.items) { itemIdx, item ->
                // 初始化当前项的FocusRequester
                val requester = focusRequesters[rowIdx]?.getOrPut(itemIdx) { FocusRequester() } ?: FocusRequester()
                ItemCard(
                    item = item,
                    modifier = Modifier
                        .focusRequester(requester)
                        .onFocusChanged {
                            if (it.isFocused) focusedItemIdx = itemIdx
                        }
                        .focusable()
                ) {
                    navController.navigate("detail/${item.id}")
                }
            }
        }
    }
}

// 监听返回事件,恢复焦点
LaunchedEffect(navController.currentBackStackEntry) {
    val currentRoute = navController.currentBackStackEntry?.destination?.route
    if (currentRoute == "main") {
        // 延迟确保列表完成布局,避免请求焦点失败
        delay(100)
        focusRequesters[focusedRowIdx]?.get(focusedItemIdx)?.requestFocus()
    }
}

2. 升级Compose TV版本

1.0.0-alpha06是非常早期的版本,后续的1.0.0-beta01及以上版本已经修复了大量焦点相关的bug,包括这种返回时焦点丢失的问题。直接升级版本大概率能解决问题,这是最省心的方案。

3. 强制触发列表焦点

如果暂时不想升级版本,也可以在返回主界面时,让TvLazyColumn滚动到之前的位置后,强制触发焦点:

val columnState = rememberTvLazyListState()

TvLazyColumn(state = columnState) {
    // 列表内容...
}

LaunchedEffect(navController.currentBackStackEntry) {
    if (navController.currentBackStackEntry?.destination?.route == "main") {
        // 滚动到之前的行
        columnState.scrollToItem(focusedRowIdx)
        delay(150)
        // 通过遍历或者焦点请求器触发焦点
        focusRequesters[focusedRowIdx]?.get(focusedItemIdx)?.requestFocus()
    }
}

注意点

  • 保存焦点位置时,要在onFocusChanged的isFocused为true时记录,避免误触。
  • FocusRequester要通过remember或者存入Map持有,不要在重组时重复创建,否则会失效。

内容的提问来源于stack exchange,提问作者Tim Schmidt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 02:35:31