一个小时入门 Android Compose 动画

news2024/11/22 4:43:31

0. 前言

前段时间对于Android中的Compose动画做了系统性的学习,相关文章发布在 Compose 动画
专栏里。系统性学完Compose动画后,又对此做了系统性的回顾,抽取其比较重要的部分,希望能帮助大家快速入门Compose动画,所以有了本篇文章 : 一个小时入门 Android Compose 动画

1. Compose中的动画API

我们先来看下官网上的图,看上去很复杂,东西很多
在这里插入图片描述

对此,我进行了重新的整理。其实需要重点关注的Compose动画API,有这些
在这里插入图片描述
接下来我们一个个的来看

2. AnimateXxxAsState

AnimateXxxAsState用来实现单个值变化(大小、位置、颜色等)的动画
Xxx代指很多单位,比如DpColorFloatIntRect等。

我们这里以DpColor为例

  • animateDpAsState : 大小变化
  • animateColorAsState : 颜色变化

2.1 代码示例

下面这段代码点击后,Box的尺寸和颜色会发生动画过渡的变化

var big by remember {
    mutableStateOf(false)
}
//var size = if (big) 300.dp else 50.dp
val size by animateDpAsState(targetValue = if (big) 300.dp else 50.dp)
val color by animateColorAsState(targetValue = if (big) Color.Red else Color.Blue)
Box(
    Modifier
        .size(size)
        .background(color)
        .clickable {
            big = !big
        }
)

效果如下所示

在这里插入图片描述

3. AnimatedVisibility

控制UI组件的显示/隐藏,并可以自定义入场和出场时候的动画效果

3.1 入场和出场动画

AnimatedVisibility通过enter/exit可以配置入场和出场动画效果,比如以下几个

  • 淡入 : fadeIn / fadeout
  • 缩放 : scaleIn / scaleOut
  • 滑动 : slideIn / slideOut
  • 展开 : expandIn / shrinkOut

3.1 代码示例

这段代码点击按钮后,会 显示/隐藏 图片

Column(horizontalAlignment = Alignment.CenterHorizontally) {
    var visible by remember {
        mutableStateOf(true)
    }
    AnimatedVisibility(visible = visible, enter = expandIn(), exit = shrinkOut()) {
        Image(painter = painterResource(id = R.drawable.photot1), contentDescription = null)
    }
    Button(onClick = {
        visible = !visible
    }) {
        Text(text = "显示/隐藏")
    }
}

效果如下所示
在这里插入图片描述

4. Transition

用来管理多个动画,通过updateTransition()来创建一个Transition,再通过Transition创建具体的动画

4.1 代码示例

这段使用Transition实现的代码,效果和使用animateXxxAsState实现的效果一样

var big by remember {
    mutableStateOf(false)
}
val transition = updateTransition(targetState = big, label = "myTransition")
val size by transition.animateDp(label = "mySize") {
    if (it) 300.dp else 50.dp
}
val color by transition.animateColor(label = "myColor") {
    if (it) Color.Red else Color.Blue
}
//val size by animateDpAsState(targetValue = if (big) 300.dp else 50.dp)
//val color by animateColorAsState(targetValue = if (big) Color.Red else Color.Blue)
Box(
    Modifier
    .size(size)
    .background(color)
    .clickable {
        big = !big
    }
)

效果如下所示

在这里插入图片描述

4.2 为什么要有Transition?

animateXxAsState可以实现一样的效果,那为什么还要有 Transition 这个API呢 ?

  • 使用Transition可以对动画做很方便的管理
    • animateXxAsState是面向值的,在多个动画多个状态的情况下存在不便于管理的问题
    • Transition是面向状态的,多个动画可以共用一个状态,能够做到统一的管理
  • Transition支持Compose动画预览
    • Transition支持Compose动画预览功能
    • animateXxAsState当前不支持Compose动画预览

4.3 如何进入Compose动画预览界面 ?

点击Start Animation Preview按钮即可进入

在这里插入图片描述

4.4 封装并复用Transition

在简单的场景下,在同一个页面中使用updateTransition创建Transition并直接操作它完成动画是没问题的。然而,如果需要处理一个具有许多动画属性的复杂场景,可以把Transition动画的实现与用户界面分开,从而提升代码复用率和可维护性。

class TransitionBean(size: State<Dp>, color: State<Color>) {
    val size by size
    val color by color
}

@Composable
private fun updateMyTransition(big: Boolean): TransitionBean {
    val transition = updateTransition(targetState = big, label = "myTransition")
    val size = transition.animateDp(label = "mySize") {
        if (it) 300.dp else 50.dp
    }
    val color = transition.animateColor(label = "myColor") {
        if (it) Color.Red else Color.Blue
    }
    return TransitionBean(size, color)
}

@Composable
private fun TransitionTest() {
    var big by remember {
        mutableStateOf(false)
    }
    val transitionBean = updateMyTransition(big)
    Box(
        Modifier
            .size(transitionBean.size)
            .background(transitionBean.color)
            .clickable {
                big = !big
            }
    )
}

5. AnimationSpec

自定义动画规格 AnimationSpec,类似于传统View体系中的差值器Interpolator,但是比起差值器,又提供了更多的功能。
在这里插入图片描述

5.1 SpringSpec

基于弹簧的物理动画效果,通过spring()进行调用。

  • 很多动画内部AnimationSpec使用的默认值都是spring,比如animateXXXAsState以及updateTransition等
  • 基于物理规律,使动画更真实自然
  • 因为是基于物理规律的,所以无法指定动画执行时长,而是会基于物理规律来确定动画执行时长

5.1.1 SpringSpec的参数

  • dampingRatio :弹簧的阻尼比,这个值越大,阻尼越大
  • stiffness :弹簧的刚度值,弹簧有多想回弹回去,这个值越大,回弹的越快
  • visibilityThreshold :当动画到达这个阈值会立即停止

5.1.2 代码示例

var big by remember {
    mutableStateOf(false)
}
val size by animateDpAsState(
    targetValue = if (big) 300.dp else 50.dp,
    animationSpec = spring(Spring.DampingRatioMediumBouncy, Spring.StiffnessHigh, 0.1.dp)
)
Box(
    Modifier
        .size(size)
        .background(Color.Blue)
        .clickable {
            big = !big
        }
)

效果如下所示

在这里插入图片描述

5.2 TweenSpec

可指定规定时间完成动画,通过tween进行调用。

可以使用Easing来可以控制动画的节奏。

5.2.1 TweenSpec的参数

  • durationMillis : 动画执行时长

  • delayMillis : 动画延迟多久执行

  • easing : 用来控制动画的节奏

5.2.2 Easing

在这里插入图片描述

5.2.3 代码示例

这段代码指定了在2秒时间匀速线性地完成动画

var big by remember {
    mutableStateOf(false)
}
val size by animateDpAsState(
    targetValue = if (big) 300.dp else 50.dp,
    animationSpec = tween(2000, easing = LinearEasing)
)
Box(
    Modifier
    .size(size)
    .background(Color.Blue)
    .clickable {
        big = !big
    }
)

效果如下所示
在这里插入图片描述

5.3 RepeatableSpec / InfiniteRepeatableSpec

  • RepeatableSpec : 可循环播放的动画,需要包裹其他AnimateSpec
  • InfiniteRepeatableSpec: 无限循环动画,需要包裹其他AnimateSpec

repeatable/infiniteRepeatable不支持spring,因为一个循环运动的弹簧是违背物理规律的

5.3.1 代码示例

这段代码使用RepeatableSpec,循环播放动画5

val size by animateDpAsState(
    targetValue = if (big) 300.dp else 50.dp,
    animationSpec = repeatable(
        iterations = 5,
        tween(2000, easing = LinearEasing),
        repeatMode = RepeatMode.Reverse
    )
)

这段代码使用InfiniteRepeatableSpec,无限循环播放动画

val size by animateDpAsState(
    targetValue = if (big) 300.dp else 50.dp,
    animationSpec = infiniteRepeatable(
        tween(2000, easing = LinearEasing),
        repeatMode = RepeatMode.Reverse
    )
)

6. Animatable

AnimatableAndroid Compose动画的底层API

  • animateDpAsState内部使用Animatable实现
  • Animatable具有更高的可定制性
  • 如果animateDpAsState能够满足需求,就用animateDpAsState就行了,否则才去使用Animatable

6.1 Animatable的参数

  • initialValue : 初始值
  • typeConverter : 转换到什么单位 (比如Dp)
  • visibilityThreshold : 当动画到达这个阈值会立即停止

6.2 代码示例

通过调用animatable.animateTo实现动画的过渡,animateTo必须要在LaunchedEffect内部执行
这段代码的动画效果,和使用AnimateDpAsState的动画效果一致。

@Composable
private fun AnimatableTest() {
    var big by remember {
        mutableStateOf(false)
    }
    var size = if (big) 300.dp else 50.dp
    val animatable = remember {
        Animatable(size, Dp.VectorConverter)
    }
    LaunchedEffect(key1 = big) {
        animatable.animateTo(size)
    }
    Box(
        Modifier
            .size(animatable.value)
            .background(Color.Blue)
            .clickable {
                big = !big
            }
    )
}

效果如下所示
在这里插入图片描述

6.3 Compose里的协程 : LaunchedEffect

LaunchedEffectCompose里的协程
为什么要专门为Compose出一个专门的协程呢 ?
因为Compose中每次状态改变,Compose进行重组更改UI的时候,就会去重新执行相应的代码块

  • 如果使用原本的协程,每次Compose的重组都会执行该代码,这肯定是不行的
  • LaunchedEffect专门针对Compose重组的这个特定,做了特定的处理,只有其传入的key值发生变化的时候,才会去执行
LaunchedEffect(key1 = big) {
    animatable.animateTo(size)
}

7. 为什么使用animateDpAsState,就可以实现动画渐变的效果 ?

其实我们点击AnimateDpAsState内部的代码,可以发现其也是调用Animatable来实现的。

val animatable = remember {
    Animatable(size, Dp.VectorConverter)
}
LaunchedEffect(key1 = targetValue) {
    animatable.animateTo(size)
}

也就是说,animateDpAsState内部会去启动一个协程,通过animatable.animateTo会在某一个时间段内,自动完成从当前值到目标值的计算,并且实时将该值通知给Compose重组,从而实现页面界面的更新,达到动画渐变的效果。

而什么时候触发协程呢 ? 就是当协程传入的这个keytargetValue发生变化的时候。

8. 小结

到这里,我们就把Compose的重点API都讲完了,但是要想真正入门Compose动画,还需要上手亲自上手去敲一下代码,真正实践下才能真的掌握。在此基础上,如果想要看更详细的Compose动画,欢迎看我的Compose动画 专栏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/609996.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

6. JVM调优工具详解及调优实战

JVM性能调优 1. 前置启动程序1.1 Jmap1.1.1 Jmap查询内存信息1.1.2 Jmap查询堆信息1.1.3 jmap查询堆内存dump 1.2 Jstack1.3 远程连接jvisualvm 本文是按照自己的理解进行笔记总结&#xff0c;如有不正确的地方&#xff0c;还望大佬多多指点纠正&#xff0c;勿喷。 课程内容&am…

Postman工具使用

Postman 是什么 Postman 是一款 API 开发协作工具&#xff0c;它可以帮助你测试和开发 API&#xff0c;Postman 提供了测试 API 的友好界面和功能&#xff0c;使用简单便捷&#xff0c;安全可靠。Postman 是每一位前后端开发者必掌握的开发工具。 如何安装 Postman 官网安装…

【Linux】Apache 网页安全优化与日志分割

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 Apache 网页安全优化与日志分割 一、网页安全优化1.Apache 网页压缩2.Apache 网页缓存3.Apache 隐藏版本信息4.Apache 防盗链 二、rotatelogs 分割1.修改apache服务的主配置文…

chatgpt赋能python:Python图片保存及优化SEO的方法

Python图片保存及优化SEO的方法 对于一个网站&#xff0c;图片占据了网页中的很大一部分&#xff0c;因此如何在保证图片质量不失真的情况下&#xff0c;保证网页加载速度的快速呈现&#xff0c;是很多网站开发者需要考虑的一点。 在使用Python进行网站开发时&#xff0c;如何…

git基础教程(18)git stash暂存本地修改

文章目录 git stash pushgit stash listgit stash popgit stash applygit stash showgit stash drop 序号git stash cleargit stash pop冲突解决git stash 命令是 Git 中一个非常实用的命令,它可以让开发者暂时存储当前工作区的内容而不必提交代码或创建新分支,得到一个干净的…

Redisson分布式锁使用

一、使用 RedisTemplate 操作分布式锁 原子加锁&#xff08;uuid Value过期时间&#xff09;原子解锁(lua脚本)加长过期时间 UUID &#xff1a;让锁区分&#xff0c;防止错删其他锁设置锁过期时间&#xff1a;当业务异常导致无法释放锁形成锁死&#xff0c;设置过期时间自动释…

【机器学习】集成学习(实战)

集成学习&#xff08;实战&#xff09; 目录 一、准备工作&#xff08;设置 jupyter notebook 中的字体大小样式等&#xff09;二、集成算法的基本思想三、集成算法的简单实现&#xff1a;硬投票与软投票1、构建测试数据集2、硬投票3、软投票 四、集成学习&#xff1a;Bagging …

chatgpt赋能python:Python图片定位:优化SEO的关键技术

Python图片定位&#xff1a;优化SEO的关键技术 在现代搜索引擎优化中&#xff0c;图片已成为吸引用户眼球和提升网站排名的关键元素之一。而Python作为一种功能强大的编程语言&#xff0c;在图片定位和SEO优化方面也具备很大的优势。 Python图片定位的基本原理 在Python中&a…

JAVA中.equals()与 ==的区别

1. “”是运算符&#xff0c;如果是基本数据类型&#xff0c;则比较存储的值&#xff1b;如果是引用数据类型&#xff0c;则比较所指向对象的地址值。 2..equals() equals是Object的方法&#xff0c;比较的是所指向的对象的地址值&#xff0c;一般情况下&#xff0c;重写之后比…

(五)Langchain PGVector 补充智能客服匹配式问答

文章目录 资料背景目标FAQ-匹配式问答安装PGVector 向量数据库text2vec-large-chineseLangchain-PGVector测试向量化文本写入QA问答对特别注意问题langchain pgvector表和数据相识性问题匹配 结论 资料 NLP领域五大QA问答场景总结&#xff1a;FQA、DOC-QA、KBQA、TableQA、Tas…

html+css+js实现的web小案例

上完了Web开发基础的课程之后&#xff0c;做了这么一个小案例作为课程的大作业。这个案例是静态页面&#xff0c;使用的技术有htmlcssjs&#xff0c;主要的知识点有页面的打开关闭、页面跳转、超链接、图片和音频的使用、轮播图、js函数等等。本案例的主要内容是注册、登陆之后…

PostgreSQL清理dead tuples(vacuum和vacuum full)

前言 PostgreSQL的并发控制&#xff0c;采用MVCC&#xff08;多版本并发控制&#xff09;进行处理。这种机制有一个缺点&#xff1a;随着时间的推移&#xff0c;数据文件中积累的dead tuples&#xff08;死数据/死元组&#xff09;会越来越多。 在清理dead tuples时&#xff…

redis第五第六章-redis并发缓存架构和性能优化

一、缓存设计 1.缓存穿透 缓存穿透是指查询一个根本不存在的数据&#xff0c; 缓存层和存储层都不会命中&#xff0c; 通常出于容错的考虑&#xff0c; 如果从存储层查不到数据则不写入缓存层。 缓存穿透将导致不存在的数据每次请求都要到存储层去查询&#xff0c; 失去了缓存…

高通 Camera HAL3:添加一个Feature

一.概述 以HDR Feature为例&#xff0c;在CamX中添加一个新的Feature 从上游stream(upstream) feature获取格式P010的多帧数据作为inputpipeline(SWMFMergeYuv)运行算法生成同样格式的单帧数据作为输出&#xff0c;发送给下游stream(down stream) feature 二.添加 2.1 从Feat…

自动计算图片的宽度和高度拉伸至全屏附CSS及VUE简单实现

1、简介 场景&#xff1a;比如说APP封面等特点&#xff1a;能够自动适应设备的宽高&#xff0c;保证图片100%显示&#xff0c;不会缺失或者留边&#xff0c;但有可能会变形。 2、实现 注意以下代码尽管是在Vue环境下实现的&#xff0c;但与Vue关系不大&#xff0c;代码本身也足…

【Python】列表 List ④ ( 插入操作 / 追加操作 | 列表插入操作 List#insert | 列表追加元素操作 List#append )

文章目录 一、列表插入操作1、List#insert 函数简介2、代码示例 - 列表插入元素 二、列表追加元素操作1、List#append 函数简介2、代码示例 - 列表追加元素 一、列表插入操作 1、List#insert 函数简介 Python 列表 通过调用 List#insert 函数 插入元素 , 该函数需要传入两个参数…

chatgpt赋能python:Python商场打折问题详解

Python商场打折问题详解 在商场营销策略中&#xff0c;打折是一种常见的手段。而Python作为一种广泛应用于数据科学和机器学习的编程语言&#xff0c;也可以用来解决商场打折问题。本文将介绍Python实现商场打折的方法和步骤&#xff0c;希望能对广大商场营销人员和数据科学家…

C语言——数据在内存中的存储(未完待续)

文章目录 一、数据类型的介绍1.0、有符号跟无符号计算原理在这里插入图片描述1.1、数据类型的基本分类 二、整形与浮点型在内存中的存储1.整型1.0、大小端的介绍 2.原码、反码、补码2.0、原码2.1、反码2.2、补码2.3、补码转原码第二种方法 三 、浮点型3.0、浮点数存储的例题&am…

20230604给coolpi4b开发板刷机的LOG

20230604给coolpi4b开发板刷机的LOG 2023/6/4 23:49 IMG I:\BaiduNetdiskDownload\20230317[在线刷机]-RKDevTool专用镜像 I:\BaiduNetdiskDownload\20230317[在线刷机]-RKDevTool专用镜像\Windows驱动程序\DriverAssitant_v5.12.zip I:\BaiduNetdiskDownload\20230317[在线…

WGCNA | 不止一个组的WGCNA怎么分析嘞!?~(三)(共识网络分析-第三步-共识模块与特异模块相关联)

1写在前面 有小伙伴子留言问最近介绍的WGCNA共识网络的意义是什么&#xff0c;保守性吗&#xff01;&#xff1f;&#x1f9d0; 与把雄性小鼠和雌性小鼠的数据merge在一起&#xff0c;一起构建网络、确定模块的方式有什么区别呢&#xff01;&#xff1f;&#x1f617; 其实区别…