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

如何让TVLazyRow获得焦点时始终聚焦首个条目?

问题:TVLazyRow聚焦时强制选中第一个条目

我正尝试实现一个TVLazyRow,要求当从该Row上方的其他Composable组件聚焦到该Row时,必须始终先聚焦第一个条目。目前按下向下键时,焦点会落到上方组件正下方的Row条目上,请问如何实现所需行为?

以下是我的代码供参考:

val tvListState = rememberTvLazyListState()
val coScope = rememberCoroutineScope()

TvLazyRow(
        horizontalArrangement = Arrangement.spacedBy(15.dp),
        state = tvListState,
        modifier = modifier
            .fillMaxHeight()
            .padding(end = 5.dp)
            .onFocusChanged {
                if (it.isFocused) {
                    coScope.launch {
                        tvListState.scrollToItem(0)
                    }
                }
            }, pivotOffsets = PivotOffsets(0f)
) { *items* }

解决方案

你当前的代码只完成了滚动到第一个条目的操作,但没有强制将焦点转移到第一个item上——默认焦点会优先选择位置最接近上方组件的条目,所以需要主动控制焦点走向。

实现思路

  1. 给第一个item绑定FocusRequester,用于主动触发焦点请求。
  2. 在TVLazyRow获得焦点时,先滚动到第一个条目,再请求第一个item获取焦点。

优化后代码示例

方式一:用延迟确保滚动完成(简单直接)

val tvListState = rememberTvLazyListState()
val coScope = rememberCoroutineScope()
// 存储第一个item的焦点请求器
val firstItemFocusRequester = remember { FocusRequester() }

TvLazyRow(
    horizontalArrangement = Arrangement.spacedBy(15.dp),
    state = tvListState,
    modifier = modifier
        .fillMaxHeight()
        .padding(end = 5.dp)
        .onFocusChanged {
            if (it.isFocused) {
                coScope.launch {
                    // 先滚动到第一个条目
                    tvListState.scrollToItem(0)
                    // 等待滚动完成后请求焦点,延迟时间可根据实际调整
                    delay(100)
                    firstItemFocusRequester.requestFocus()
                }
            }
        },
    pivotOffsets = PivotOffsets(0f)
) {
    // 用itemsIndexed更方便判断是否为第一个条目
    itemsIndexed(yourItemList) { index, item ->
        val focusRequester = if (index == 0) firstItemFocusRequester else remember { FocusRequester() }
        YourItemComponent(
            item = item,
            modifier = Modifier
                .focusRequester(focusRequester)
                .focusable()
        )
    }
}

方式二:监听滚动状态(更稳定,无硬编码延迟)

val tvListState = rememberTvLazyListState()
val coScope = rememberCoroutineScope()
val firstItemFocusRequester = remember { FocusRequester() }
// 标记是否正在滚动到第一个条目
val isScrollingToFirst = remember { mutableStateOf(false) }

// 监听滚动状态,滚动完成后请求焦点
LaunchedEffect(tvListState.isScrollInProgress) {
    if (!tvListState.isScrollInProgress && isScrollingToFirst.value) {
        firstItemFocusRequester.requestFocus()
        isScrollingToFirst.value = false
    }
}

TvLazyRow(
    horizontalArrangement = Arrangement.spacedBy(15.dp),
    state = tvListState,
    modifier = modifier
        .fillMaxHeight()
        .padding(end = 5.dp)
        .onFocusChanged {
            if (it.isFocused) {
                coScope.launch {
                    isScrollingToFirst.value = true
                    tvListState.scrollToItem(0)
                }
            }
        },
    pivotOffsets = PivotOffsets(0f)
) {
    itemsIndexed(yourItemList) { index, item ->
        val focusRequester = if (index == 0) firstItemFocusRequester else remember { FocusRequester() }
        YourItemComponent(
            item = item,
            modifier = Modifier
                .focusRequester(focusRequester)
                .focusable()
        )
    }
}

关键说明

  • 必须给每个item添加.focusable(),否则无法获取焦点。
  • FocusRequester是Jetpack Compose中控制焦点的核心API,能精准指定哪个组件获得焦点。
  • 方式二通过监听tvListState.isScrollInProgress判断滚动是否完成,避免了硬编码延迟的不确定性,更适合复杂场景。

内容的提问来源于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.13 23:25:21