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

如何让Android TV的LazyVerticalGrid支持DPad逐项滚动?

解决Compose LazyVerticalGrid电视端DPad逐项滚动问题

问题背景

TVLazyVerticalGrid已被废弃,改用LazyVerticalGrid后,无法通过电视遥控器DPad实现网格列表的逐项滚动,期望实现网格项按上下左右方向逐个导航的效果。

问题分析

原代码存在以下问题导致DPad导航异常:

  • 所有网格项共用同一个isItemInFocus状态,焦点状态混乱,无法正确识别当前聚焦项
  • 仅设置focusable()但未适配电视端焦点导航逻辑,LazyVerticalGrid默认滚动行为不支持逐项导航
  • 未在焦点切换时触发滚动,导致聚焦项可能不在可视区域内

解决方案

  1. 为每个网格项创建独立的焦点状态和FocusRequester,避免状态冲突
  2. 通过focusProperties自定义DPad方向的焦点导航规则,确保焦点按网格行列顺序切换
  3. 在项获得焦点时,自动滚动到对应项的可视位置,实现逐项滚动的视觉效果

修改后的代码

@Composable
fun AnimalsGrid(
    animalUiModelList: ImmutableList<AnimalUiModel>,
    isLoading: Boolean,
    onLastAnimalVisible: () -> Unit,
    modifier: Modifier = Modifier,
) {
    val lazyGridState = rememberLazyGridState()
    val coroutineScope = rememberCoroutineScope()

    val itemWidth = 468.dp / 2.5f
    val itemHeight = 225.dp / 2.5f
    val itemSpacing = 10.dp

    LazyVerticalGrid(
        columns = GridCells.Fixed(3),
        state = lazyGridState,
        verticalArrangement = Arrangement.spacedBy(itemSpacing),
        horizontalArrangement = Arrangement.spacedBy(itemSpacing),
        userScrollEnabled = true,
        modifier = modifier
            .clip(RoundedCornerShape(12.dp))
            .background(color = MaterialTheme.colorScheme.surface)
            .padding(16.dp)
            .fillMaxSize()
    ) {
        items(animalUiModelList.size) { index ->
            val animal = animalUiModelList[index]
            val focusRequester = remember { FocusRequester() }
            var isFocused by remember { mutableStateOf(false) }

            // 计算当前项的行列位置
            val row = index / 3
            val column = index % 3

            Animal(
                animalModel = animal,
                modifier = Modifier
                    .width(itemWidth)
                    .height(itemHeight)
                    .focusRequester(focusRequester)
                    .focusable()
                    .focusProperties {
                        // 定义上下左右方向的焦点导航规则
                        up = if (row > 0) FocusRequester.Default else FocusRequester.Cancel
                        down = if (row < (animalUiModelList.size - 1) / 3) FocusRequester.Default else FocusRequester.Cancel
                        left = if (column > 0) FocusRequester.Default else FocusRequester.Cancel
                        right = if (column < 2) FocusRequester.Default else FocusRequester.Cancel
                    }
                    .onFocusChanged { focusState ->
                        isFocused = focusState.isFocused
                        if (isFocused) {
                            // 聚焦时自动滚动到当前项的可视区域
                            coroutineScope.launch {
                                lazyGridState.animateScrollToItem(index)
                            }
                        }
                    }
                    .background(if (isFocused) Color.Green else Color.Transparent)
            )
        }

        if (isLoading) {
            item {
                CircularProgressIndicator(
                    modifier = Modifier
                        .padding(20.dp)
                        .size(24.dp)
                        .align(Alignment.Center),
                    color = MaterialTheme.colorScheme.onSurface,
                )
            }
        }
    }
}

关键说明

  • 每个网格项通过remember初始化独立的FocusRequester和焦点状态,确保焦点状态互不干扰
  • focusProperties定义了DPad各方向的焦点导航逻辑,让焦点在网格中按行列顺序切换
  • 利用rememberCoroutineScope和animateScrollToItem,在项获得焦点时平滑滚动到目标位置,保证聚焦项始终可见
  • 确保Animal组件内部没有抢占焦点的元素,避免焦点被内部组件拦截

内容的提问来源于stack exchange,提问作者Katarina Zivkovic

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 23:37:02