Jetpack Compose 用MotionLayout打造动画开关

「这是我参与2022首次更文挑战的第13天,活动详情查看:2022首次更文挑战

简单介绍

MotionLayout是constraintlayout2.0以后才有的功能。 MotionLayoutConstraintLayout的子布局。专门用来实现运动过程中的控件动画

在Android中实现动画是一件复杂的事情。MotionLayout的问世就是为了化繁为简 MotionLayout是在Android中实现过渡的一种新的有效方法。 它充当布局过渡和复杂运动处理之间的桥梁,在属性动画框架 , TransitionManagerCoordinatorLayout之间提供了多种功能

MotionScence

作为动画布局中的根节点

ConstraintSet

用于布局ConstraintLayout子项的约束的不变描述

分为开始和结束

使用MotionLayout时要传入开始和结束的ConstraintSet

开始使用

首先导入依赖

//约束布局
implementation("androidx.constraintlayout:constraintlayout-compose:2.1.0")

写这篇文章时时2.1.0

效果图

73c1ed1506579a898f378a9c18fcd45.jpg

首先在ConstraintSet里面定义一个layoutId-xxx 接着在Box引用layoutId

Box(
    modifier = Modifier
        .layoutId("xxx")
)

实现这个开关需要4个组件 两个Box和两个Text分别表示按钮背景开关按钮还有Ligthdark文本

先写ConstraintSet

按钮背景

开始的ConstraintSet

ConstraintSet(
    """ {
    //按钮背景
    backgroundSwitch: { 
        start: ['parent', 'start', 36],
        top: ['parent', 'top', 66],
        end: ['parent', 'end', 36],
        custom: {
          color: "#d2d2d2"
        }
    },
)

结束的ConstraintSet

ConstraintSet(
    """ {
    backgroundSwitch: { 
        start: ['parent', 'start', 36],
        top: ['parent', 'top', 66],
        end: ['parent', 'end', 36],
        custom: {
          color: "#343434"
        }
    },
)
Box(
    modifier = Modifier
        .layoutId("backgroundSwitch")
        .width(300.dp)
        .height(72.dp)
        .clip(RoundedCornerShape(36.dp))
        .background(motionProperties("backgroundSwitch").value.color("color"))
)

0186ed08b9daace6cba7ba4d2c3da7a.jpg

按钮

开始的ConstraintSet

buttonSwitch: { 
    top: ['backgroundSwitch', 'top', 0],
    start: ['backgroundSwitch', 'start', 0]
},

结束的ConstraintSet

buttonSwitch: { 
    top: ['backgroundSwitch', 'top', 0],
    end: ['backgroundSwitch', 'end', 0]
},

e287e893d077abec1c276322d403efc.jpg

文本

开始的ConstraintSet

light: { 
    top: ['backgroundSwitch', 'top', 0],
    start: ['backgroundSwitch', 'start', 0],
    bottom: ['backgroundSwitch', 'bottom', 0]
},
dark: { 
    top: ['backgroundSwitch', 'top', 0],
    end: ['backgroundSwitch', 'end', 0],
    bottom: ['backgroundSwitch', 'bottom', 0]
}

结束的ConstraintSet

light: { 
    top: ['backgroundSwitch', 'top', 0],
    start: ['backgroundSwitch', 'start', 0],
    bottom: ['backgroundSwitch', 'bottom', 0]
},
dark: { 
    top: ['backgroundSwitch', 'top', 0],
    end: ['backgroundSwitch', 'end', 0],
    bottom: ['backgroundSwitch', 'bottom', 0]
}

点击事件


var animateToEnd by remember { mutableStateOf(false) }

...
clickable(onClick = { animateToEnd = !animateToEnd })

MotionLayout动画

val progress by animateFloatAsState(
    targetValue = if (animateToEnd) 1f else 0f,
    animationSpec = tween(1000)//持续时间1秒
)

完整代码

@Composable
private fun MotionLayout2Example() {
    var animateToEnd by remember { mutableStateOf(false) }
    val progress by animateFloatAsState(
        targetValue = if (animateToEnd) 1f else 0f,
        animationSpec = tween(1000)
    )
    Column(Modifier.background(Color.White)) {
        MotionLayout(
            ConstraintSet(
                """ {
               
                backgroundSwitch: { 
                    start: ['parent', 'start', 36],
                    top: ['parent', 'top', 66],
                    end: ['parent', 'end', 36],
                    custom: {
                      color: "#d2d2d2"
                    }
                },
               
                buttonSwitch: { 
                    top: ['backgroundSwitch', 'top', 0],
                    start: ['backgroundSwitch', 'start', 0]
                },
                light: { 
                    top: ['backgroundSwitch', 'top', 0],
                    start: ['backgroundSwitch', 'start', 0],
                    bottom: ['backgroundSwitch', 'bottom', 0]
                },
                dark: { 
                    top: ['backgroundSwitch', 'top', 0],
                    end: ['backgroundSwitch', 'end', 0],
                    bottom: ['backgroundSwitch', 'bottom', 0]
                }
             }"""
            ),
            ConstraintSet(
                """ {
                backgroundSwitch: { 
                    start: ['parent', 'start', 36],
                    top: ['parent', 'top', 66],
                    end: ['parent', 'end', 36],
                    custom: {
                      color: "#343434"
                    }
                },
               
                buttonSwitch: { 
                    top: ['backgroundSwitch', 'top', 0],
                    end: ['backgroundSwitch', 'end', 0]
                },
                light: { 
                    top: ['backgroundSwitch', 'top', 0],
                    start: ['backgroundSwitch', 'start', 0],
                    bottom: ['backgroundSwitch', 'bottom', 0]
                },
                dark: { 
                    top: ['backgroundSwitch', 'top', 0],
                    end: ['backgroundSwitch', 'end', 0],
                    bottom: ['backgroundSwitch', 'bottom', 0]
                }
              }"""
            ),
            progress = progress,
            modifier = Modifier
                .fillMaxSize()
                .background(Color.White)
        ) {

            Box(
                modifier = Modifier
                    .layoutId("backgroundSwitch")
                    .width(300.dp)
                    .height(72.dp)
                    .clip(RoundedCornerShape(36.dp))
                    .clickable(onClick = { animateToEnd = !animateToEnd })
                    .background(motionProperties("backgroundSwitch").value.color("color"))
            )


            Box(
                modifier = Modifier
                    .layoutId("buttonSwitch")
                    .width(150.dp)
                    .height(72.dp)
                    .clip(RoundedCornerShape(36.dp))
                    .background(Color.Gray)
            )

            Text(
                text = "light",
                modifier = Modifier
                    .layoutId("light")
                    .width(150.dp),
                color = Color.White,
                fontSize = 24.sp,
                textAlign = TextAlign.Center
            )
            Text(
                text = "dark",
                modifier = Modifier
                    .layoutId("dark")
                    .width(150.dp),
                color = Color.Black,
                fontSize = 24.sp,
                textAlign = TextAlign.Center
            )
        }
    }
}

ezgif.com-gif-maker (10).gif