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

Jetpack Compose:如何让LazyVerticalGrid首个项进入页面时获取焦点?

解决TV应用LazyVerticalGrid首项初始化焦点异常问题

问题根源

你的代码里LaunchedEffect(Unit)会在组件刚启动就执行焦点请求,但LazyVerticalGrid是按需加载的,此时首项大概率还没完成渲染组合,FocusRequester还没和首项的Modifier绑定,直接请求就会触发FocusRequester is not initialized异常。

可行解决方案

方案1:监听列表状态,等首项可见再请求焦点

通过LazyGridState监听首项是否进入可见区域,确认后再触发焦点请求:

@Composable
fun TwoColumnGrid(
    animals: List<Animal>,
    onAnimalClicked: (Animal) -> Unit
) {
    val firstItemFocusRequester = remember { FocusRequester() }
    val gridState = rememberLazyGridState()

    MyTheme {
        LazyVerticalGrid(
            columns = GridCells.Fixed(2),
            state = gridState
        ) {
            items(animals) { animal ->
                val modifier = if (animals.isNotEmpty() && animal == animals[0]) {
                    Modifier.focusRequester(firstItemFocusRequester)
                } else {
                    Modifier
                }
                GridItem(
                    animal,
                    onClick = onAnimalClicked,
                    modifier = modifier
                )
            }
        }

        LaunchedEffect(gridState) {
            // 监听首项是否进入可见范围
            snapshotFlow { gridState.layoutInfo.visibleItemsInfo.firstOrNull()?.index == 0 }
                .filter { it }
                .first()
            firstItemFocusRequester.requestFocus()
        }
    }
}

方案2:在首项内部触发焦点请求

把焦点请求逻辑放到GridItem组件里,当首项被渲染时自动请求焦点:

@Composable
fun GridItem(
    animal: Animal,
    onClick: (Animal) -> Unit,
    modifier: Modifier = Modifier,
    isFirstItem: Boolean = false
) {
    val focusRequester = remember { FocusRequester() }
    val itemModifier = if (isFirstItem) {
        modifier.focusRequester(focusRequester)
    } else {
        modifier
    }

    if (isFirstItem) {
        LaunchedEffect(Unit) {
            focusRequester.requestFocus()
        }
    }

    // 这里写你的GridItem布局代码
    Box(modifier = itemModifier.clickable { onClick(animal) }) {
        // ... 组件内容
    }
}

// 调用TwoColumnGrid时修改为:
@Composable
fun TwoColumnGrid(
    animals: List<Animal>,
    onAnimalClicked: (Animal) -> Unit
) {
    MyTheme {
        LazyVerticalGrid(columns = GridCells.Fixed(2)) {
            itemsIndexed(animals) { index, animal ->
                GridItem(
                    animal,
                    onClick = onAnimalClicked,
                    isFirstItem = index == 0
                )
            }
        }
    }
}

方案3:用focusProperties设置默认焦点(推荐TV场景)

TV应用可以直接利用系统的焦点机制,标记首项为默认焦点,系统会自动处理:

@Composable
fun TwoColumnGrid(
    animals: List<Animal>,
    onAnimalClicked: (Animal) -> Unit
) {
    MyTheme {
        LazyVerticalGrid(columns = GridCells.Fixed(2)) {
            itemsIndexed(animals) { index, animal ->
                val modifier = if (index == 0) {
                    Modifier.focusProperties {
                        isDefaultFocus = true
                    }
                } else {
                    Modifier
                }
                GridItem(
                    animal,
                    onClick = onAnimalClicked,
                    modifier = modifier
                )
            }
        }
    }
}

注意点

  • Lazy组件的列表项是按需渲染的,绝对不能在根组件的LaunchedEffect(Unit)里直接请求焦点,必须等目标项渲染完成。
  • 方案3最适合TV应用,逻辑更简洁,也符合TV端的焦点管理规范。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 05:45:00