如何在Jetpack Compose中使用MotionLayout、MotionScene、ConstraintSet?
Jetpack Compose MotionLayout 折叠动画实现示例
你要实现的顶部折叠渐变动画用MotionLayout可以快速实现,以下是可直接运行的完整示例:
首先需要添加依赖:
// build.gradle(模块级) 依赖配置 dependencies { implementation "androidx.constraintlayout:constraintlayout-compose:1.1.0" }
核心实现代码:
@Composable fun CollapsibleHeaderDemo() { // isCollapsed 可以由按钮点击或者列表滚动状态控制 val isCollapsed = remember { mutableStateOf(false) } // 动画进度 0=完全展开 1=完全折叠 val progress by animateFloatAsState(targetValue = if (isCollapsed.value) 1f else 0f, label = "collapseProgress") // 1. 定义展开状态的约束集 val startConstraintSet = ConstraintSet { val headerBg = createRefFor("headerBg") val avatar = createRefFor("avatar") val username = createRefFor("username") val bio = createRefFor("bio") constrain(headerBg) { top.linkTo(parent.top) start.linkTo(parent.start) end.linkTo(parent.end) height = Dimension.value(200.dp) } constrain(avatar) { top.linkTo(headerBg.bottom, margin = (-40).dp) start.linkTo(parent.start, margin = 16.dp) width = Dimension.value(80.dp) height = Dimension.value(80.dp) } constrain(username) { top.linkTo(avatar.bottom, margin = 16.dp) start.linkTo(avatar.start) } constrain(bio) { top.linkTo(username.bottom, margin = 8.dp) start.linkTo(username.start) end.linkTo(parent.end, margin = 16.dp) } } // 2. 定义折叠状态的约束集 val endConstraintSet = ConstraintSet { val headerBg = createRefFor("headerBg") val avatar = createRefFor("avatar") val username = createRefFor("username") val bio = createRefFor("bio") constrain(headerBg) { top.linkTo(parent.top) start.linkTo(parent.start) end.linkTo(parent.end) height = Dimension.value(56.dp) } constrain(avatar) { centerVerticallyTo(headerBg) start.linkTo(parent.start, margin = 16.dp) width = Dimension.value(32.dp) height = Dimension.value(32.dp) } constrain(username) { centerVerticallyTo(headerBg) start.linkTo(avatar.end, margin = 12.dp) } constrain(bio) { top.linkTo(headerBg.bottom) start.linkTo(username.start) end.linkTo(parent.end, margin = 16.dp) alpha = 0f // 折叠时隐藏简介 } } // 3. 定义MotionScene配置过渡动画 val motionScene = remember { MotionScene { defaultTransition( from = startConstraintSet, to = endConstraintSet ) { // 配置全局动画参数 duration = 300 ease = EaseInOutQuad // 单独指定简介透明度的动画曲线 property("alpha", "bio") { duration = 150 } } } } // 4. MotionLayout承载内容 MotionLayout( motionScene = motionScene, progress = progress, modifier = Modifier.fillMaxSize() ) { // 顶部背景栏 Box( modifier = Modifier .fillMaxWidth() .background(Color.Blue) .layoutId("headerBg") ) // 头像 Image( painter = painterResource(id = R.drawable.default_avatar), contentDescription = null, modifier = Modifier .clip(CircleShape) .layoutId("avatar") ) // 用户名 Text( text = "用户名", style = MaterialTheme.typography.titleLarge, color = Color.White, modifier = Modifier.layoutId("username") ) // 个人简介 Text( text = "这是个人简介内容,折叠时会隐藏", style = MaterialTheme.typography.bodyMedium, modifier = Modifier.layoutId("bio") ) } }
关键注意点
- 所有子组件必须设置和ConstraintSet中对应的
layoutId,否则约束不会生效 progress参数取值范围为0~1,和列表滚动进度绑定即可实现示例中跟随滚动逐步折叠的效果,不需要额外写补间动画- 可以单独对某个组件的某个属性配置专属的动画时长、插值器,实现更细腻的效果
学习参考
- Android Studio内置的Compose样例库中包含多个MotionLayout的不同场景实现,新建项目时选择「Compose Samples」即可查看源码
- ConstraintLayout Compose库的源码API注释对每个参数的说明非常详尽,可以直接在代码中跳转查看
内容的提问来源于stack exchange,提问作者Tiberiu Paduraru
相关产品推荐
相关产品推荐

