一文带你学会使用Jetpack Compose Animations

简介: 1. Animation是由state驱动的 Compose的核心思想状态驱动UI刷新,这一思想同样体现在动画上。 Compose动画主要是通过不断计算最新的state值来刷新UI,这类似于传统的Va

Node:本文基于Jetpack Compose 1.0.0-beta01

1. Animation是由state驱动的

Compose的核心思想状态驱动UI刷新,这一思想同样体现在动画上。

UI = f(state)

Compose动画主要是通过不断计算最新的state值来刷新UI,这类似于传统的ValueAnimator,根据动画的插值器和估值器计算当前value,在映射到View的对应属性。Compose天然是基于state驱动的,相关API变得更加简单、合理。

2. AnimateAsState:属性动画

AnimateAsState提供了传统的属性动画的能力。 如下代码,点击Button可以改变Box的颜色

@Preview
@Composable
fun AnimateAsStateDemo() {
    var blue by remember { mutableStateOf(true) }
    val color = if (blue) Blue else Red,

    Column(Modifier.padding(16.dp)) {
        Text("AnimateAsStateDemo")
        Spacer(Modifier.height(16.dp))

        Button(
            onClick = { blue = !blue }
        ) {
            Text("Change Color")
        }
        Spacer(Modifier.height(16.dp))
        Box(
            Modifier
                .preferredSize(128.dp)
                .background(color)
        )
    }
}

d65ffa4169c34e49b9e211a47276cbc3~tplv-k3u1fbpfcp-zoom-1.image

如果想让Color以动画的方式切换,可以借助用animateColorAsState

@Composable
fun AnimateAsStateDemo() {
    var blue by remember { mutableStateOf(true) }
    val color by animateColorAsState(
        if (blue) Blue else Red,
        animationSpec = spring(Spring.StiffnessVeryLow)
    )

     //...
}

433e63a6b3814c12bf20d0ecb619edfa~tplv-k3u1fbpfcp-zoom-1.image

animateColorAsState将Color的变化过程转换为一个可订阅的state;animationSpec用来进行动画配置,比如例子总配置了一个弹簧动画效果

animationSped还可以监听动画结束的回调

val color by animateColorAsState(
    if (blue) Blue else Red,
    animationSpec = spring(stiffness = Spring.StiffnessVeryLow),
    finishedListener = {
        blue = !blue
    }
)

如上,可以实现折返动画的效果

除了AnimateColorAsState以外,还支持其他各类型的动画:
在这里插入图片描述

3. updateTransition:多个动画同步

如果想同时进行多个属性的动画,并保持同步,需要使用updateTransition,类似使用AnimationSet组合多个动画


private sealed class BoxState(val color: Color, val size: Dp) {
    operator fun not() = if (this is Small) Large else Small
    object Small : BoxState(Blue, 64.dp)
    object Large : BoxState(Red, 128.dp)
}

@Composable
fun UpdateTransitionDemo() {

    var boxState: BoxState by remember { mutableStateOf(BoxState.Small) }
    val transition = updateTransition(targetState = boxState)

    Column(Modifier.padding(16.dp)) {
        Text("UpdateTransitionDemo")
        Spacer(Modifier.height(16.dp))

        val color by transition.animateColor {
            boxState.color
        }
        val size by transition.animateDp(transitionSpec = {
            if (targetState == BoxState.Large) {
                spring(stiffness = Spring.StiffnessVeryLow)
            } else {
                spring(stiffness = Spring.StiffnessHigh)
            }
        }) {
            boxState.size
        }

        Button(
            onClick = { boxState = !boxState }
        ) {
            Text("Change Color and size")
        }
        Spacer(Modifier.height(16.dp))
        Box(
            Modifier
                .preferredSize(size)
                .background(color)
        )
    }
}

f8776ba76094403fb5adf8e8380767f6~tplv-k3u1fbpfcp-zoom-1.image

updateTransition根据targetState创建一个Transition,然后通过Transition的扩展函数可以创建各种属性动画所需的state。

需要注意,Transition的codename容易跟传统的位移动画混淆,其实完全没有联系,这里的Transition是用来同步多个动画的工具。

transitionSpec可以进行动画配置,上面例子中,在Box放大和缩小过程中,分别设置不同的弹性动画效果。

同样,Transition根据属性类型的不同,有多种扩展函数

在这里插入图片描述

4. AnimateVisibility:可见性动画

在View的可见性发生变化时做动画是一个常见需求。传统的View体系中,一般使用alpha值变化实现fadeIn/fadeOut,或者通过transitionX/Y的变化,实现slideIn/slideOut

Compose中则使用AnimatedVisibility

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun AnimateVisibilityDemo() {
    var visible by remember { mutableStateOf(true) }

    Column(Modifier.padding(16.dp)) {
        Text("AnimateVisibilityDemo")
        Spacer(Modifier.height(16.dp))

        Button(
            onClick = { visible = !visible }
        ) {
            Text(text = if (visible) "Hide" else "Show")
        }

        Spacer(Modifier.height(16.dp))

        AnimatedVisibility(visible) {
            Box(
                Modifier
                    .preferredSize(128.dp)
                    .background(Blue)
            )
        }
    }
}

ffbda61310d9450f9bcba55f3f17d70e~tplv-k3u1fbpfcp-zoom-1.image

如上,默认的可见性动画效果是淡入/淡出 + 收缩/放大:
在这里插入图片描述

5. AnimateContentSize : 布局大小动画

animateContentSizeModifier的扩展方法,添加这个方法的Composable,会监听子Composable大小的变化,并以动画方式作成相应调整

@Composable
fun AnimateContentSizeDemo() {
    var expend by remember { mutableStateOf(false) }

    Column(Modifier.padding(16.dp)) {
        Text("AnimateContentSizeDemo")
        Spacer(Modifier.height(16.dp))

        Button(
            onClick = { expend = !expend }
        ) {
            Text(if (expend) "Shrink" else "Expand")
        }
        Spacer(Modifier.height(16.dp))

        Box(
            Modifier
                .background(Color.LightGray)
                .animateContentSize()
        ) {
            Text(
                text = "animateContentSize() animates its own size when its child modifier (or the child composable if it is already at the tail of the chain) changes size. " +
                        "This allows the parent modifier to observe a smooth size change, resulting in an overall continuous visual change.",
                fontSize = 16.sp,
                textAlign = TextAlign.Justify,
                modifier = Modifier.padding(16.dp),
                maxLines = if (expend) Int.MAX_VALUE else 2
            )
        }
    }
}

7695b6a21d88486d8a5d4b4cddf8b30d~tplv-k3u1fbpfcp-zoom-1.image

animateContentSize同样可以配置animSpec以及endListener

在这里插入图片描述

6. Crossfade : 布局切换动画

Crossfade 本身是一个Composable,其内部的子布局发生切换时,可以添加淡入淡出效果:

@Composable
fun CrossfadeDemo() {

    var scene by remember { mutableStateOf(DemoScene.Text) }

    Column(Modifier.padding(16.dp)) {

        Text("AnimateVisibilityDemo")
        Spacer(Modifier.height(16.dp))

        Button(onClick = {
            scene = when (scene) {
                DemoScene.Text -> DemoScene.Icon
                DemoScene.Icon -> DemoScene.Text
            }
        }) {
            Text("toggle")
        }

        Spacer(Modifier.height(16.dp))

        Crossfade(
            current = scene,
            animation = tween(durationMillis = 1000)
        ) {
            when (scene) {
                DemoScene.Text ->
                    Text(text = "Phone", fontSize = 32.sp)
                DemoScene.Icon ->
                    Icon(
                        imageVector = Icons.Default.Phone,
                        null,
                        modifier = Modifier.preferredSize(48.dp)
                    )
            }
        }

    }
}

782ba977f76e4623800d5e5a13d76107~tplv-k3u1fbpfcp-zoom-1.image

参考

Sample Code

目录
相关文章
|
3月前
|
存储 缓存 编译器
探索 Jetpack Compose 内核:深入 SlotTable 系统
探索 Jetpack Compose 内核:深入 SlotTable 系统
69 1
|
3月前
|
IDE API 开发工具
Google I/O :Android Jetpack 最新变化(四)Compose
Google I/O :Android Jetpack 最新变化(四)Compose
100 0
|
3月前
|
前端开发 API Android开发
Jetpack Compose 实现波浪加载效果
Jetpack Compose 实现波浪加载效果
63 0
|
3月前
|
前端开发 算法 PHP
Jetpack Compose Runtime : 声明式 UI 的基础
Jetpack Compose Runtime : 声明式 UI 的基础
44 0
|
3月前
|
XML 前端开发 IDE
在 Compose 中使用 Jetpack 组件库
在 Compose 中使用 Jetpack 组件库
70 0
|
6月前
|
存储 算法 Android开发
Jetpack-Compose 学习笔记(三)—— Compose 的自定义“View”(下)
Jetpack-Compose 学习笔记(三)—— Compose 的自定义“View”(下)
31 0
|
7月前
|
前端开发 Android开发
Jetpack Compose 学习指南(二)
Jetpack Compose 学习指南(二)
84 0
|
6月前
|
缓存 API Android开发
Jetpack-Compose 学习笔记(一)—— Compose 初探(下)
Jetpack-Compose 学习笔记(一)—— Compose 初探(下)
44 0
|
9天前
|
XML 移动开发 Android开发
构建高效安卓应用:采用Jetpack Compose实现动态UI
【4月更文挑战第10天】 在现代移动开发中,用户界面的流畅性和响应性对于应用的成功至关重要。随着技术的不断进步,安卓开发者寻求更加高效和简洁的方式来构建动态且吸引人的UI。本文将深入探讨Jetpack Compose这一革新性技术,它通过声明式编程模型简化了UI构建过程,并提升了性能与跨平台开发的可行性。我们将从基本概念出发,逐步解析如何利用Jetpack Compose来创建具有数据动态绑定能力的安卓应用,同时确保应用的高性能和良好用户体验。
14 0
|
12天前
|
XML 开发工具 Android开发
构建高效的安卓应用:使用Jetpack Compose优化UI开发
【4月更文挑战第7天】 随着Android开发不断进化,开发者面临着提高应用性能与简化UI构建流程的双重挑战。本文将探讨如何使用Jetpack Compose这一现代UI工具包来优化安卓应用的开发流程,并提升用户界面的流畅性与一致性。通过介绍Jetpack Compose的核心概念、与传统方法的区别以及实际集成步骤,我们旨在提供一种高效且可靠的解决方案,以帮助开发者构建响应迅速且用户体验优良的安卓应用。