Jetpack Compose中的动画

这篇具有很好参考价值的文章主要介绍了Jetpack Compose中的动画。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

动画分类概览

Jetpack Compose中没有沿用Android原有的View动画和属性动画,而是新创建了一套全新的动画系统API,这是理所当然的,因为旧的动画系统主要是基于View体系的,而Compose中需要针对的是Composable可组合函数进行处理,那么势必要创造一套新的玩具出来,同时,这也无疑增加了开发者的学习成本。

Jetpack Compose中的动画

乍一看Jetpack Compose中的动画Api,尼玛是真的多呀,我C了,简直令人眼花缭乱、云里雾里、天马行空、小兔乱撞、手脚慌乱、头冒虚汗、四肢抓狂、不知所措呀 。。。😭

但是我们可以对其进行分一下类,如果按照使用的方便程度划分,大概可以分为两大类:高级动画API和低级动画API(这里类比高级开发语言的分类,并不是指效果多高级)。

其中高级动画API使用比较简单方便,封装度高,更加适用于日常业务开发,而低级动画API则使用起来较为麻烦,因为其配置项或流程较多,但是却更加灵活,能对动画效果做出更加精细的控制,适合自定义要求度较高的业务场景。

我们还可以按照功能类型进行一个大概的分类,也就是上图中的划分,下面再按照需求点用表格整理一下:

功能需求点 可能符合的API类型
◻️ 单个组件的显示隐藏转场动画
◻️ 每个子组件需要不同的入场/出场效果
AnimatedVisibility
◻️ 根据组件内容状态变化的动画(数据、尺寸等)
◻️ 不同组件间的切换动画
AnimatedContent
Modifier.animateContentSize
单纯的淡入淡出动画 Crossfade
◻️ 根据数据估值状态自动执行连续动画
◻️ 基于单个数据值的状态变化执行动画
◻️ 基于自定义数据类型进行估值动画
◻️ 指定每一帧/每一时刻的动画状态
◻️ 替代传统 View 属性动画的方案
animateXXXAsState
◻️ 根据不同状态同时管理和运行多个动画
◻️ 进入界面时自动执行一次动画
◻️ 监听动画状态
◻️ 替代传统 View 动画中的 AnimationSet 的方案。
updateTransition
MutableTransitionState
永不停止、无限循环的动画 rememberInfiniteTransition
◻️ 更加底层的低级动画API
◻️ 可高度自由定制的估值属性动画
◻️ 需要在协程中执行的动画
◻️ 需要控制一些动画并行执行
Animatable
◻️ 更加底层的低级动画API
◻️ 需要手动精确控制动画的时间
◻️ 手势动画,fling衰减动画
TargetBasedAnimation
DecayAnimation

高级动画API

AnimatedVisibility

AnimatedVisibility主要用于页面显示状态的动画,即显示/隐藏的过渡动画,或者入场/离场动画。
可以使用 + 运算符组合多个 EnterTransitionExitTransition 对象,并且每个对象都接受可选参数以自定义其行为。

@Composable
fun AnimatedVisibilityExample() {
   
    var visible by remember {
    mutableStateOf(true) }
    val density = LocalDensity.current
    Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.TopCenter) {
   
        AnimatedVisibility(
            visible = visible,
            enter = slideInVertically {
    with(density) {
    -40.dp.roundToPx() } } // 从顶部 40dp 的地方开始滑入
                    + expandVertically(expandFrom = Alignment.Top)  // 从顶部开始展开
                    + fadeIn(initialAlpha = 0.3f), // 从初始透明度 0.3f 开始淡入
            exit = slideOutVertically() + shrinkVertically() + fadeOut()
        ) {
   
            Text("Hello",
                Modifier.background(Color.Green).fillMaxWidth().height(200.dp)
                    .wrapContentWidth(Alignment.CenterHorizontally),
                fontSize = 20.sp
            )
        }
        Button(
            onClick = {
    visible = !visible },
            modifier = Modifier.padding(top = 200.dp)
        ) {
   
            Text(text = if(visible) "隐藏" else "显示")
        }
    }
}

运行效果:
Jetpack Compose中的动画

默认情况下 EnterTransitionfadeIn() + expandIn() 的效果,而 ExitTransitionshrinkOut() + fadeOut() 的效果, Compose额外提供了RowScope.AnimatedVisibilityColumnScope.AnimatedVisibility两个扩展方法, 当我们在RowColumn中调用时,该组件的默认动画效果会根据父容器的布局特征进行调整,比如在RowEnterTransition默认是fadeIn + expandHorizontally组合,而在ColumnEnterTransition默认是fadeIn + expandVertically组合方案。

EnterTransitionExitTransition 动画分类效果示例:

EnterTransition ExitTransition
FadeIn Jetpack Compose中的动画 FadeOut Jetpack Compose中的动画
slideIn Jetpack Compose中的动画 slideOut Jetpack Compose中的动画
slideInHorizontally Jetpack Compose中的动画 slideOutHorizontally Jetpack Compose中的动画
slideInVertically Jetpack Compose中的动画 slideOutVertically Jetpack Compose中的动画
scaleIn Jetpack Compose中的动画 scaleOut Jetpack Compose中的动画
expandIn Jetpack Compose中的动画 shrinkOut Jetpack Compose中的动画
expandHorizontally Jetpack Compose中的动画 shrinkHorizontally Jetpack Compose中的动画
expandVertically Jetpack Compose中的动画 shrinkVertically Jetpack Compose中的动画

为子项添加进入和退出动画效果

AnimatedVisibility 中的内容(直接或间接子项)可以使用 Modifier.animateEnterExit 修饰符为每个子项指定不同的动画行为。

其中每个子项的视觉效果均由 AnimatedVisibility 可组合项中指定的动画与子项自己的进入和退出动画构成。

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun AnimatedVisibilityExample3() {
   
    var visible by remember {
    mutableStateOf(true) }
    Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.TopCenter) {
   
        AnimatedVisibility(visible = visible, enter = fadeIn(), exit = fadeOut()) {
   
            // 外层Box组件淡入淡出进出屏幕
            Box(Modifier.fillMaxSize().background(Color.DarkGray)) {
   
                Box(Modifier.align(Alignment.Center)
                    .sizeIn(minWidth = 256.dp, minHeight = 64.dp).background(Color.Green)
                    .animateEnterExit(enter = slideInVertically(), exit = slideOutVertically())
                ) {
   
                    Text(text = "内层Box组件滑动进出屏幕", Modifier.align(Alignment.Center))
                }
                Box(Modifier.padding(top = 150.dp).align(Alignment.Center)
                    .sizeIn(minWidth = 256.dp, minHeight = 64.dp).background(Color.Cyan)
                    .animateEnterExit(enter = scaleIn(), exit = scaleOut())
                ) {
   
                    Text(text = "内层层Box组件缩放进出屏幕", Modifier.align(Alignment.Center))
                }
            }
        }
        Button(
            onClick = {
    visible = !visible },
            modifier = Modifier.padding(top = 50.dp)
        ) {
   
            Text(text = if(visible) "隐藏" else "显示")
        }
    }
}

运行效果:
Jetpack Compose中的动画

有时我们希望 AnimatedVisibility 内的每个子组件有不同的过渡动画,此时请在 AnimatedVisibility 可组合项中指定 EnterTransition.NoneExitTransition.None,即完全不应用任何动画,这样子项就可以通过 Modifier.animateEnterExit 拥有各自的不同动画了。

自定义 Enter/Exit 动画

如果想在内置进入和退出动画之外添加自定义动画效果,请在 AnimatedVisibilityScope 内设置 transition, 添加到 Transition 实例的所有动画状态都将与 AnimatedVisibility 的进入和退出动画同时运行。

AnimatedVisibility 会等到 Transition 中的所有动画都完成后再移除其内容。对于独立于 Transition(例如使用 animate*AsState)创建的退出动画,AnimatedVisibility 将无法解释这些动画,因此可能会在完成之前移除内容可组合项。

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun AnimatedVisibilityExample4() {
   
    var visible by remember {
    mutableStateOf(true) }
    Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.TopCenter) {
   
        AnimatedVisibility(visible = visible, enter = scaleIn(), exit = scaleOut()) {
   
            // 使用 AnimatedVisibilityScope#transition 添加自定义的动画与AnimatedVisibility同时执行
            val background by transition.animateColor(label = "backgroundTransition") {
    state ->
                if (state == EnterExitState.Visible) Color.Blue else Color.Green
            }
            Box(modifier = Modifier.size(100.dp).background(background))
        }
        Button(
            onClick = {
    visible = !visible },
            modifier = Modifier.padding(top = 120.dp)
        ) {
   
            Text(text = if(visible) "隐藏" else "显示")
        }
    }
}

运行效果:
Jetpack Compose中的动画

AnimatedContent

AnimatedContent 可组合项会在内容根据目标状态发生变化时,为内容添加动画效果。
与 AnimatedVisibility 的区别是: AnimatedVisibility用来添加组件自身的入场/离场动画,而AnimatedContent是实现不同组件间的切换动画

AnimatedContent接收一个targetState和一个contentcontent 是基于 targetState 创建的Composable,当targetState变化时,content的内容也会随之变化。AnimatedContent内部维护着targetStatecontent的映射表,查找 targetState新旧值对应的content后,在content发生重组时附加动画效果。

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun AnimatedContentExample() {
   
    Column {
   
        var count by remember {
    mutableStateOf(0) }
        Button(onClick = {
    count++ }) {
    Text("Add") }
        AnimatedContent(targetState = count) {
    targetCount ->
            // 这里要使用lambda的参数 `targetCount`, 而不是 `count`,否则将没有意义(API 会将此值用作键,以标识当前显示的内容)
            Text(text = "Count: $targetCount", Modifier.background(Color.Green), fontSize = 25.sp)
        }
    }
}

运行效果:
Jetpack Compose中的动画

ContentTransform

AnimatedContent默认是淡入淡出效果,可以为 transitionSpec 参数指定 ContentTransform 对象,以自定义此动画行为。

可以使用 with infix 函数来组合 EnterTransitionExitTransition,以创建 ContentTransform

 @ExperimentalAnimationApi
 infix fun EnterTransition.with(exit: ExitTransition) = ContentTransform(this, exit)

ContentTransform本质上就是currentContent(initial)ExitTransitiontargetContentEnterTransition组合, EnterTransition 定义了目标内容应如何显示,ExitTransition 则定义了初始内容应如何消失。

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun AnimatedContentExample2() {
   
    Column {
   
        var count by remember {
    mutableStateOf(0) }
        Button(onClick = {
    count++ }) {
    Text("Add") }
        AnimatedContent(
            targetState = count,
            transitionSpec = {
   
                // 从右往左切换,并伴随淡入淡出效果(initialOffsetX = width, targetOffsetX = -width)
                slideInHorizontally{
   width -> width} + fadeIn() with
                        slideOutHorizontally{
   width -> -width} + fadeOut()
            }
        ) {
    targetCount ->
            Text(text = "Count: $targetCount", Modifier.background(Color.Green), fontSize = 25.sp)
        }
    }
}

运行效果:
Jetpack Compose中的动画

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun AnimatedContentExample3() {
   
    Column(horizontalAlignment = Alignment.CenterHorizontally) {
   
        var count by remember {
    mutableStateOf(0) }
        Button(onClick = {
    count++ }) {
    Text("Add") }
        val animationSpec = tween<IntOffset>(200)
        val animationSpec2 = tween<Float>(200)
        AnimatedContent(
            targetState = count,
            transitionSpec = {
   
                slideInVertically(animationSpec){
    height -> height} + fadeIn(animationSpec2) with
                    slideOutVertically(animationSpec) {
   height -> height} + fadeOut(animationSpec2)
            }
        ) {
    targetCount ->
            Text(text = "$targetCount", fontSize = 40.sp)
        }
    }
}

运行效果:
Jetpack Compose中的动画

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun AnimatedContentExample4() {
   
    Column {
   
        var count by remember {
    mutableStateOf(0) }
        Row(horizontalArrangement = Arrangement.SpaceAround) {
   
            Button(onClick = {
    count-- }) {
    Text("Minus") }
            Spacer(Modifier.size(60.dp))
            Button(onClick = {
    count++ }) {
    Text("Plus ") }
        }
        Spacer(Modifier.size(20.dp))
        AnimatedContent(
            targetState = count,
            transitionSpec = {
   
                if (targetState > initialState) {
   
                    // 如果targetState更大,则从下往上切换并伴随淡入淡出效果
                    slideInVertically {
    height -> height } + fadeIn() with
                            slideOutVertically {
    height -> -height } + fadeOut()
                } else {
   
                    // 如果targetState更小,则从上往下切换并伴随淡入淡出效果
                    slideInVertically {
    height -> -height } + fadeIn() with
                            slideOutVertically {
    height -> height } + fadeOut()
                }.using(
                    // Disable clipping since the faded slide-in/out should be displayed out of bounds.
                    SizeTransform(clip = false)
                )
            }
        ) {
    targetCount ->
            Text(text = "Count: $targetCount", Modifier.background(Color.Green), fontSize = 25.sp)
        }
    }
}

运行效果:
Jetpack Compose中的动画

slideIntoContainerslideOutOfContainer

除了可用于 AnimatedVisibility 的所有 EnterTransition 和 ExitTransition 函数之外,AnimatedContent 还提供了 slideIntoContainerslideOutOfContainer。这些是 slideInHorizontally/VerticallyslideOutHorizontally/Vertically 的便捷替代方案,它们可根据初始内容的大小和 AnimatedContent 内容的目标内容来计算滑动距离。(官方例子可见:slideIntoContainer)

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun SlideIntoContainerSample() {
   
    val transitionSpec: AnimatedContentScope<Int>.() -> ContentTransform = {
   
        if (initialState < targetState) {
   
            slideIntoContainer(towards = AnimatedContentScope.SlideDirection.Up) + fadeIn() with
                    slideOutOfContainer(towards = AnimatedContentScope.SlideDirection.Up) + fadeOut()
        } else {
   
            slideIntoContainer(towards = AnimatedContentScope.SlideDirection.Down) + fadeIn()  with
                    slideOutOfContainer(towards = AnimatedContentScope.SlideDirection.Down) + fadeOut()
        }.apply {
   
            // 这里可指定目标内容的 zIndex ,值越大越上层,值越小越下层
//            targetContentZIndex = when (targetState) {
   
//                NestedMenuState.Level1 -> 1f
//                NestedMenuState.Level2 -> 2f
//                NestedMenuState.Level3 -> 3f
//            }
        }.using(SizeTransform(clip = false))
    }
    Column {
   
        var count by remember {
    mutableStateOf(0) }
        Row(horizontalArrangement = Arrangement.SpaceAround) {
   
            Button(onClick = {
    count-- }) {
    Text("Minus") }
            Spacer(Modifier.size(60.dp))
            Button(onClick = {
    count++ }) {
    Text("Plus ") }
        }
        Spacer(Modifier.size(20.dp))
        AnimatedContent(
            targetState = count,
            transitionSpec = transitionSpec,
        ) {
    targetCount ->
            Text(text = "Count: $targetCount", Modifier.background(Color.Green), fontSize = 25.sp)
        }
    }
}

运行效果:同上一个例子一样

SizeTransform

SizeTransform 定义了大小应如何在初始内容与目标内容之间添加动画效果。在创建动画时,您可以访问初始大小和目标大小。 SizeTransform 还可控制在动画播放期间是否应将内容裁剪为组件大小。文章来源地址https://www.toymoban.com/news/detail-478392.html

@OptIn(ExperimentalAnimationApi::class, ExperimentalMaterialApi::class)
@Composable
fun SizeTransformAnimatedContentSample() {
   
    var expanded by remember {
    mutableStateOf(false) }
    Surface(
        color = MaterialTheme.colors.primary,
        onClick = {
    expanded = !expanded },
        modifier = Modifier.padding(10.dp).onSizeChanged {
     }
    ) {
   
        AnimatedContent(
            targetState = expanded,
            transitionSpec = {
   
                fadeIn(animationSpec = tween(150, 150)) with
                    fadeOut(animationSpec = tween(150)) using
                        SizeTransform {
    initialSize, targetSize ->
                            if (targetState) {
   
                                keyframes {
   
                                    // 展开时,先水平方向展开
                                    // 150ms之前:宽度从initialSize.width增大到targetSize.width,高度保持initialSize.height不变
                                    // 150ms之后:宽度保持targetSize.width不变,高度从initialSize.height开始增大到targetSize.height
                                    IntSize(targetSize.width, initialSize.height) at 150
                                    durationMillis = 300
                                }
                            } else {
   
                                keyframes {
   
                                    // 收缩时,先垂直方向收起
                                    // 150ms之前:宽度保持initialSize.width不变,高度从initialSize.height减小到targetSize.height
                                    // 150ms之后:宽度从initialSize.width减小到targetSize.width,高度保持targetSize.height不变
                                    IntSize(initialSize.width, targetSize

到了这里,关于Jetpack Compose中的动画的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Jetpack Compose 中的动态加载、插件化技术探索

    在传统的 Android 开发模式中,由于界面过分依赖于 Activity 、 Fragment 这样的组件,一个业务模块中往往会存在着大量的 Activity 类,因此诞生了很多的插件化框架,这些插件化框架基本都是想方设法的使用各种Hook/反射手段来解决使用未注册的组件问题。在进入 Jetpack Compose 的世

    2024年02月14日
    浏览(38)
  • Android Jetpack Compose 中的分页与缓存展示

    在几乎任何类型的移动项目中,移动开发人员在某个时候都会处理分页数据。如果数据列表太大,无法一次从服务器检索完毕,这就是必需的。因此,我们的后端同事为我们提供了一个端点,返回分页数据列表,并期望我们知道如何在客户端处理它。 在本文中,我们将重点介

    2024年02月13日
    浏览(50)
  • Android开发中的前五个代码异味:Jetpack Compose UI和MVVM

    代码异味是指软件代码中潜在问题的指标,可能并不一定是错误,但可能会导致问题,如增加维护复杂性、降低性能或降低可读性。我们将探讨Android开发中的前五个代码异味,其中包括使用Jetpack Compose UI和Model-View-ViewModel(MVVM)架构的示例。 上帝对象或上帝类是指试图做太

    2024年02月02日
    浏览(40)
  • Jetpack Compose 深入探索系列四: Compose UI

    通过 Compose runtime 集成 UI Compose UI 是一个 Kotlin 多平台框架。它提供了通过可组合函数发出 UI 的构建块和机制。除此之外,这个库还包括 Android 和 Desktop 源代码,为 Android 和 Desktop 提供集成层。 JetBrains积极维护Desktop代码库,而Google维护Android和通用代码库。Android和Desktop源代码

    2024年02月12日
    浏览(48)
  • Jetpack Compose 学习汇总

    关于 Jetpack Compose 的学习本想只是简单的快速学习一下,结果万万没想到,竟然一下子折腾了好几个月。。。 下面将之前记录的 Jetpack Compose 相关的学习博文进行一个汇总链接整理,方便我以后自己查阅,也希望能帮到一些有正在学习 Compose 的道友。 Jetpack Compose 中的基础组件

    2024年02月12日
    浏览(41)
  • Jetpack Compose(4)——重组

    目录 一、状态变化 1.1 状态变化是什么 1.2 mutableStateListOf 和 mutableStateMapOf 二、重组的特性 2.1 Composable 重组是智能的 2.2 Composable 会以任意顺序执行 2.3 Composable 会并发执行 2.4 Composable 会反复执行 2.5 Composable 的执行是“乐观”的 三、重组范围 四、参数类型的稳定性 4.1 稳定和不

    2024年04月08日
    浏览(40)
  • Jetpack Compose UI架构

    Jetpack Compose是我职业生涯中最激动人心的事。它改变了我工作和问题思考的方式,引入了易用且灵活的工具,几乎可轻松实现各种功能。 早期在生产项目中尝试了Jetpack Compose后,我迅速着迷。尽管我已有使用Compose创建UI的经验,但对新的Jetpack Compose驱动特性的组织和架构引发

    2024年02月11日
    浏览(52)
  • Jetpack compose实现MVI

    MVI(Model-View-Intent)是一种前端架构模式,其目标是使状态管理更具可预测性,便于开发和调试。它将应用程序视为一个函数,该函数接受一系列的意图(Intent)作为输入,然后返回一个新的状态作为输出。在 Jetpack Compose 中,你可以使用 Kotlin Flow 和 State 来实现 MVI。 下面是

    2024年02月16日
    浏览(32)
  • Jetpack Compose(3) —— 状态管理

    上一篇文章拿 TextField 组件举例时,提到了 State ,即状态。本篇文章,即讲解 State 的相关改概念。 与其它声明式 UI 框架一样,Compose 的职责非常单纯,仅作为对数据状态的反应。如果数据状态没有改变,则 UI 永远不会自行改变。在 Compose 中,每一个组件都是一个被 @Composa

    2024年03月13日
    浏览(46)
  • Jetpack Compose之选择器

    选择器主要是指Checkbox复选框,单选开关Switch,滑杆组件Slider等用于提供给用户选择一些值和程序交互的组件,比如像复选框Checkbox,可以让用户选择一个或者多个选项,它可以将一个选项打开或者是关闭,通常用来做线上调查问卷或者是模拟考试的场景程序中,再比如滑杆组

    2023年04月11日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包