Jetpack Compose 动画(自定义动画)

很多动画 API 通常接受用于自定义其行为的参数。

使用 AnimationSpec 参数自定义动画

大多数动画 API 允许开发者通过可选的 AnimationSpec 参数来自定义动画规范。

开发者可以使用不同类型的 AnimationSpec 来创建不同类型的动画。

使用 spring 创建基于物理特性的动画

spring 可在起始值和结束值之间创建基于物理特性的动画。它接受 2 个参数:dampingRatiostiffness

dampingRatio 定义弹簧的弹性。默认值为 Spring.DampingRatioNoBouncy

ts 复制代码
@Composable
private fun example1() {
    val enabled = remember { mutableStateOf(true) }

    val boxSize = 50

    val stiffnessList = arrayListOf(Spring.StiffnessVeryLow, Spring.StiffnessLow, Spring.StiffnessMediumLow, Spring.StiffnessMedium, Spring.StiffnessHigh)

    val selectedStiffness = remember {
        mutableStateOf(0)
    }

    val dampingRatioNoBouncyTranslationX = animateFloatAsState(if (enabled.value) dipToPx(15) else screenWidth().dp.value  - dipToPx(boxSize) - dipToPx(15),
        label = "animateFloatAsState",
        animationSpec = spring(dampingRatio = DampingRatioNoBouncy, stiffness = stiffnessList[selectedStiffness.value])
    )
    val dampingRatioLowBouncyTranslationX = animateFloatAsState(if (enabled.value) dipToPx(15) else screenWidth().dp.value  - dipToPx(boxSize) - dipToPx(15),
        label = "animateFloatAsState",
        animationSpec = spring(dampingRatio = DampingRatioLowBouncy, stiffness = stiffnessList[selectedStiffness.value])
    )
    val dampingRatioMediumBouncyTranslationX = animateFloatAsState(if (enabled.value) dipToPx(15) else screenWidth().dp.value  - dipToPx(boxSize) - dipToPx(15),
        label = "animateFloatAsState",
        animationSpec = spring(dampingRatio = DampingRatioMediumBouncy, stiffness = stiffnessList[selectedStiffness.value])
    )
    val dampingRatioHighBouncyTranslationX = animateFloatAsState(if (enabled.value) dipToPx(15) else screenWidth().dp.value  - dipToPx(boxSize) - dipToPx(15),
        label = "animateFloatAsState",
        animationSpec = spring(dampingRatio = DampingRatioHighBouncy, stiffness = stiffnessList[selectedStiffness.value])
    )
    Column(modifier = Modifier.fillMaxWidth()) {
        Box(
            Modifier
                .padding(top = 15.dp)
                .size(boxSize.dp)
                .graphicsLayer(translationX = dampingRatioNoBouncyTranslationX.value)
                .background(Color.Red)
        )
        Text(text = "DampingRatioNoBouncy", modifier = Modifier.padding(all = 15.dp))
        Box(
            Modifier
                .padding(top = 15.dp)
                .size(boxSize.dp)
                .graphicsLayer(translationX = dampingRatioLowBouncyTranslationX.value)
                .background(Color.Red)
        )
        Text(text = "DampingRatioLowBouncy", modifier = Modifier.padding(all = 15.dp))
        Box(
            Modifier
                .padding(top = 15.dp)
                .size(boxSize.dp)
                .graphicsLayer(translationX = dampingRatioMediumBouncyTranslationX.value)
                .background(Color.Red)
        )
        Text(text = "DampingRatioMediumBouncy", modifier = Modifier.padding(all = 15.dp))
        Box(
            Modifier
                .padding(top = 15.dp)
                .size(boxSize.dp)
                .graphicsLayer(translationX = dampingRatioHighBouncyTranslationX.value)
                .background(Color.Red)
        )
        Text(text = "DampingRatioHighBouncy", modifier = Modifier.padding(all = 15.dp))

        Text(text = "当前 stiffness (移动速度):${when(selectedStiffness.value) {
            0-> "StiffnessVeryLow"
            1-> "StiffnessLow"
            2-> "StiffnessMediumLow"
            3-> "StiffnessMedium"
            4-> "StiffnessHigh"
            else -> "StiffnessVeryLow"
        }}", modifier = Modifier.padding(all = 15.dp))

        Slider(
            value = selectedStiffness.value.toFloat(),
            onValueChange = { selectedStiffness.value = it.toInt() },
            modifier = Modifier.padding(horizontal = 15.dp),
            steps = stiffnessList.size - 2,
            valueRange = 0f..stiffnessList.size - 1f
        )
        Button(modifier = Modifier.padding(horizontal = 15.dp), onClick = {
            enabled.value = !enabled.value
        }) {
            Text(text = if (enabled.value) "正向" else "反向")
        }
    }
}

使用 tween 通过加/​减速曲线在起始值和结束值之间添加动画效果

ts 复制代码
@Composable
private fun example2() {
    val enabled = remember { mutableStateOf(true) }

    val boxSize = 50

    val x1 = remember { mutableStateOf(0f) }
    val y1 = remember { mutableStateOf(0f) }
    val x2 = remember { mutableStateOf(1f) }
    val y2 = remember { mutableStateOf(1f) }

    val easingList = arrayListOf(FastOutSlowInEasing,LinearOutSlowInEasing, FastOutLinearInEasing, LinearEasing, CubicBezierEasing(x1.value, y1.value, x2.value, y2.value))

    val selectedEasing = remember {
        mutableStateOf(0)
    }

    val canvasSize = 200
    val canvasSizeDp = dipToPx(dp = canvasSize)

    val canvasPointSize = 20f

    val translationX = animateFloatAsState(if (enabled.value) dipToPx(15) else screenWidth().dp.value  - dipToPx(boxSize) - dipToPx(15),
        label = "animateFloatAsState",
        animationSpec = tween(durationMillis  = 1000, easing = easingList[selectedEasing.value])
    )
    Column(modifier = Modifier.fillMaxWidth()) {
        Text(text = "tween 动画", modifier = Modifier.padding(all = 15.dp))
        Box(
            Modifier
                .padding(top = 15.dp)
                .size(boxSize.dp)
                .graphicsLayer(translationX = translationX.value)
                .background(Color.Red)
        )
        Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.clickable { selectedEasing.value = 0 }) {
            RadioButton(selected = selectedEasing.value == 0, onClick = { selectedEasing.value = 0 })
            Text(text = "FastOutSlowInEasing")
        }
        Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.clickable { selectedEasing.value = 1 }) {
            RadioButton(selected = selectedEasing.value == 1, onClick = { selectedEasing.value = 1 })
            Text(text = "LinearOutSlowInEasing")
        }
        Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.clickable { selectedEasing.value = 2 }) {
            RadioButton(selected = selectedEasing.value == 2, onClick = { selectedEasing.value = 2 })
            Text(text = "FastOutLinearInEasing")
        }
        Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.clickable { selectedEasing.value = 3 }) {
            RadioButton(selected = selectedEasing.value == 3, onClick = { selectedEasing.value = 3 })
            Text(text = "LinearEasing")
        }
        Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.clickable { selectedEasing.value = 4 }) {
            RadioButton(selected = selectedEasing.value == 4, onClick = { selectedEasing.value = 4 })
            Text(text = "CubicBezierEasing")
        }
        if (selectedEasing.value == 4) {
            Canvas(modifier = Modifier
                .padding(start = 15.dp)
                .size(canvasSize.dp), onDraw = {
                drawRect(color = Color.Gray)
                drawCircle(color = Color.Red, radius = canvasPointSize, center = Offset((x1.value * canvasSizeDp).coerceIn(canvasPointSize, canvasSizeDp - canvasPointSize),
                    (canvasSizeDp - y1.value * canvasSizeDp).coerceIn(canvasPointSize, canvasSizeDp - canvasPointSize)))
                drawCircle(color = Color.Blue, radius = canvasPointSize, center = Offset((x2.value * canvasSizeDp).coerceIn(canvasPointSize, canvasSizeDp - canvasPointSize),
                    (canvasSizeDp - y2.value * canvasSizeDp).coerceIn(canvasPointSize, canvasSizeDp - canvasPointSize)))
            })
            Row(verticalAlignment = Alignment.CenterVertically) {
                Text(text = "x1=%.1f".format(x1.value), modifier = Modifier.padding(start = 15.dp))
                Slider(value = x1.value, onValueChange = { x1.value = it }, modifier = Modifier
                    .width(200.dp)
                    .padding(start = 15.dp))
            }
            Row(verticalAlignment = Alignment.CenterVertically) {
                Text(text = "y1=%.1f".format(y1.value), modifier = Modifier.padding(start = 15.dp))
                Slider(value = y1.value, onValueChange = { y1.value = it }, modifier = Modifier
                    .width(200.dp)
                    .padding(start = 15.dp))
            }
            Row(verticalAlignment = Alignment.CenterVertically) {
                Text(text = "x2=%.1f".format(x2.value), modifier = Modifier.padding(start = 15.dp))
                Slider(value = x2.value, onValueChange = { x2.value = it }, modifier = Modifier
                    .width(200.dp)
                    .padding(start = 15.dp))
            }
            Row(verticalAlignment = Alignment.CenterVertically) {
                Text(text = "y2=%.1f".format(y2.value), modifier = Modifier.padding(start = 15.dp))
                Slider(value = y2.value, onValueChange = { y2.value = it }, modifier = Modifier
                    .width(200.dp)
                    .padding(start = 15.dp))
            }
        }
        Button(modifier = Modifier.padding(horizontal = 15.dp), onClick = {
            enabled.value = !enabled.value
        }) {
            Text(text = if (enabled.value) "正向" else "反向")
        }
    }
}
相关推荐
方之长3 天前
我写了个App,上架 Google Play 一年,下载不到 10 次,于是决定把它开源了
android·github·android jetpack
我命由我123453 天前
Android Studio - Android Studio 查看项目的 Android SDK 版本(4 种方式)
android·java·ide·java-ee·android studio·android jetpack·android runtime
工程师老罗12 天前
我用Ai学Android Jetpack Compose之CircularProgressIndicator
android·android jetpack
工程师老罗12 天前
我用Ai学Android Jetpack Compose之Icon
android·android jetpack
工程师老罗12 天前
我用Ai学Android Jetpack Compose之Row
android·android jetpack
砖厂小工13 天前
AI 也能"看懂"图片: Android AI 搜图的奥秘
openai·android jetpack
普通网友1 个月前
Android-Jetpack架构组件(一)带你了解Android-Jetpack
jvm·架构·android jetpack
2401_897915651 个月前
Android Jetpack 之 Paging3的一些踩坑记录
android·android jetpack
我命由我123451 个月前
Android 项目依赖冲突问题:Duplicate class found in modules
android·xml·java·java-ee·android studio·android jetpack·android-studio
我命由我123451 个月前
11-3.Android 项目结构 - 认识 .idea 目录
android·xml·java·java-ee·gitee·android jetpack·android runtime