Jetpack Compose中LazyRow/LazyColumn居中选中项如何添加缩放动画
LazyRow中心吸附项放大效果实现
效果参考

当列表项滚动吸附到屏幕中心时自动放大,中心两侧的项恢复原始尺寸,不需要修改现有已实现的无限滚动、snapper吸附逻辑,只需要给列表项增加基于偏移量的缩放动画即可。
实现步骤
- 提前定义缩放参数:设置中心选中项的最大缩放比例(常用值为1.2f,可按需调整),未选中项保持1f原始缩放比例
- 监听每个列表项相对于LazyRow容器的位置偏移
- 根据item中心和屏幕中心的距离计算实时缩放值,搭配Compose动画API做平滑过渡,避免缩放效果生硬
修改后完整代码
@OptIn(ExperimentalSnapperApi::class) @Composable fun CircularList( data: List<SingleBox>, modifier: Modifier = Modifier, isEndless: Boolean = true ) { val listState = rememberLazyListState( if (isEndless) Int.MAX_VALUE / 2 else 0 ) val configuration = LocalConfiguration.current val itemDimensions = ItemDimensions() val screenWidth = configuration.screenWidthDp.dp val contentPadding = PaddingValues(start = screenWidth/2 - (itemDimensions.itemWidth/2)) // 缩放参数配置:中心项放大1.2倍,默认弹簧动效更贴合原生滚动手感 val maxSelectedScale = 1.2f val originScale = 1f // 存储LazyRow容器的中心X坐标 var containerCenterX by remember { mutableFloatStateOf(0f) } BoxWithConstraints( modifier = modifier.onGloballyPositioned { containerCenterX = it.size.width / 2f } ) { LazyRow( state = listState, modifier = Modifier.fillMaxWidth(), contentPadding = contentPadding, flingBehavior = rememberSnapperFlingBehavior( listState, SnapOffsets.Start, snapIndex = { _, startIndex, targetIndex -> targetIndex.coerceIn(startIndex - 7, startIndex + 7) } ), ) { items( count = if (isEndless) Int.MAX_VALUE else data.size, key = { it } ) { val index = it % data.size // 存储当前item的中心X坐标 var itemCenterX by remember { mutableFloatStateOf(0f) } // 计算当前item与容器中心的偏移比例 val offsetPercent = remember(itemCenterX, containerCenterX) { if (containerCenterX == 0f || itemCenterX == 0f) 1f else { val distance = kotlin.math.abs(itemCenterX - containerCenterX) // 偏移距离超过单个item宽度时,比例为1,保持原始大小 (distance / itemDimensions.itemWidth.toPx()).coerceIn(0f, 1f) } } // 动画过渡缩放值 val scale by animateFloatAsState( targetValue = originScale + (maxSelectedScale - originScale) * (1 - offsetPercent), label = "item_scale_anim" ) Box( modifier = Modifier .graphicsLayer { scaleX = scale scaleY = scale } .onGloballyPositioned { layoutCoordinates -> itemCenterX = layoutCoordinates.positionInRoot().x + layoutCoordinates.size.width / 2f } ) { CustomItem(data[index]) } } } } }
优化说明
- 缩放逻辑使用
graphicsLayer修饰符实现,不会触发全局重组,滚动性能更好 - 给LazyRow的items增加
key参数,避免滚动复用item时位置计算错乱 - 缩放倍数、动画参数可根据设计稿需求调整,如果需要给非中心项加透明度、位移等效果,也可以复用同样的偏移比例计算对应参数
内容的提问来源于stack exchange,提问作者Erick Sorto
相关产品推荐
相关产品推荐

