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
相关产品推荐
相关产品推荐

