Android TV Compose中如何用Dpad实现LazyRow快速滚动
问题背景
- 开发场景:使用Jetpack Compose实现Android TV端轮播组件
- 核心约束:聚焦项必须始终为屏幕显示的首个条目
- 故障现象:Dpad快速滚动时性能差、响应延迟高
现象说明

录屏前半段是逐次点按右键的滚动效果,后半段是长按右键连续滚动到列表末尾的效果:
- 逐次点按时,按下右键后列表先执行滚动动画,动画结束后焦点才移动到下一个条目,操作有明显滞后感
- 长按快速滚动时,整体滚动速度远慢于按键触发速度
现有实现代码
自定义横向列表组件
@Composable private fun CustomLazyRow() { val scrollState = rememberLazyListState() LazyRow( state = scrollState, horizontalArrangement = Arrangement.spacedBy(16.dp) ) { itemsIndexed( items = (1..20).toList() ) { index, item -> var isFocused by remember { mutableStateOf(false) } Text( text = "Item $item", modifier = Modifier .dpadNavigation(scrollState, index) .width(156.dp) .aspectRatio(4 / 3F) .onFocusChanged { isFocused = it.isFocused } .focusable() .border(if (isFocused) 4.dp else Dp.Hairline, Color.Black) ) } } }
Dpad导航Modifier实现
fun Modifier.dpadNavigation( scrollState: LazyListState, index: Int ) = composed { val focusManager = LocalFocusManager.current var focusDirectionToMove by remember { mutableStateOf<FocusDirection?>(null) } val scope = rememberCoroutineScope() onKeyEvent { if (it.type == KeyEventType.KeyDown) { when (it.nativeKeyEvent.keyCode) { KeyEvent.KEYCODE_DPAD_LEFT -> focusDirectionToMove = FocusDirection.Left KeyEvent.KEYCODE_DPAD_RIGHT -> focusDirectionToMove = FocusDirection.Right } if (focusDirectionToMove != null) { scope.launch { if (focusDirectionToMove == FocusDirection.Left && index > 0) { // 该写法测试无效: // scope.launch { scrollState.animateScrollToItem(index - 1) } scrollState.animateScrollToItem(index - 1) focusManager.moveFocus(FocusDirection.Left) } if (focusDirectionToMove == FocusDirection.Right) { // 该写法测试无效: // scope.launch { scrollState.animateScrollToItem(index + 1) } scrollState.animateScrollToItem(index + 1) focusManager.moveFocus(FocusDirection.Right) } } } } true } }
已排查尝试
最初判断卡顿根因是animateScrollToItem为挂起函数,必须等滚动动画执行完成才会执行后续的moveFocus逻辑,因此尝试将滚动逻辑放到独立协程中与焦点移动并行执行,但测试发现该方案完全失效,列表不会触发滚动。
内容的提问来源于stack exchange,提问作者Geekarist
相关产品推荐
相关产品推荐

