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

如何在进入TvLazyRow时直接聚焦指定项?

解决TvLazyRow进入时直接聚焦指定项的问题

你遇到的问题是默认焦点先触发,随后才跳转到目标项,本质是焦点请求时机晚于默认聚焦逻辑,且没有提前将目标项滚动到可视区域。以下是可行的解决方案:

核心思路

先让列表滚动到目标项的位置,确保滚动完成后再请求焦点;同时每个TvLazyRow必须使用独立的状态,避免多列表逻辑冲突。

具体实现代码

Column {
    // 第一个横向列表:独立状态+预聚焦逻辑
    val row1State = rememberTvLazyListState()
    val row1PreFocusIndex = 3 // 要聚焦的目标项索引(从0开始)
    val row1ItemRequesters = remember { mutableMapOf<Int, FocusRequester>() }

    TvLazyRow(
        horizontalArrangement = Arrangement.spacedBy(15.dp),
        state = row1State,
        modifier = modifier.padding(end = 5.dp),
        pivotOffsets = PivotOffsets(0f)
    ) {
        items(yourItemList1) { item ->
            val index = yourItemList1.indexOf(item)
            val focusRequester = remember { FocusRequester() }
            
            // 维护每个项的FocusRequester,组件销毁时清理
            DisposableEffect(index) {
                row1ItemRequesters[index] = focusRequester
                onDispose { row1ItemRequesters.remove(index) }
            }

            // 你的项视图,绑定FocusRequester
            YourItemComponent(
                item = item,
                modifier = Modifier.focusRequester(focusRequester)
            )
        }
    }

    // 触发聚焦逻辑:依赖列表滚动状态,确保滚动完成后请求焦点
    LaunchedEffect(row1State.isScrollInProgress) {
        // 先滚动到目标项位置
        row1State.scrollToItem(row1PreFocusIndex)
        // 等待滚动完全结束
        row1State.awaitScrollEnd()
        // 请求目标项焦点
        row1ItemRequesters[row1PreFocusIndex]?.requestFocus()
    }

    // 第二个横向列表:重复上述逻辑
    val row2State = rememberTvLazyListState()
    val row2PreFocusIndex = 5
    val row2ItemRequesters = remember { mutableMapOf<Int, FocusRequester>() }

    TvLazyRow(
        horizontalArrangement = Arrangement.spacedBy(15.dp),
        state = row2State,
        modifier = modifier.padding(end = 5.dp),
        pivotOffsets = PivotOffsets(0f)
    ) {
        items(yourItemList2) { item ->
            val index = yourItemList2.indexOf(item)
            val focusRequester = remember { FocusRequester() }
            
            DisposableEffect(index) {
                row2ItemRequesters[index] = focusRequester
                onDispose { row2ItemRequesters.remove(index) }
            }

            YourItemComponent(
                item = item,
                modifier = Modifier.focusRequester(focusRequester)
            )
        }
    }

    LaunchedEffect(row2State.isScrollInProgress) {
        row2State.scrollToItem(row2PreFocusIndex)
        row2State.awaitScrollEnd()
        row2ItemRequesters[row2PreFocusIndex]?.requestFocus()
    }

    // 第三个横向列表同理实现
}

关键细节说明

  1. 独立状态管理:每个TvLazyRow必须使用专属的TvLazyListState,共用状态会导致多列表的滚动、焦点逻辑互相干扰,这是原代码的潜在问题。
  2. 滚动优先于聚焦:通过scrollToItem将目标项提前滚动到可视区域,再用awaitScrollEnd确保滚动完成后才请求焦点,彻底避免默认焦点先触发的问题。
  3. FocusRequester生命周期:用DisposableEffect维护每个项的FocusRequester,确保组件销毁时及时清理,避免内存泄漏。

可选优化

如果希望列表初始化时就定位到目标项,可以直接在创建状态时指定初始滚动位置:

val row1State = rememberTvLazyListState(initialFirstVisibleItemIndex = row1PreFocusIndex)

这种情况下,LaunchedEffect中可以直接请求焦点,无需等待滚动,因为列表一开始就处于目标位置。

内容的提问来源于stack exchange,提问作者Rahul Rawat

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 19:43:17