Android中分析Jetpack Compose动画内部的实现原理

 更新时间:2022年09月01日 08:48:09   作者:李小白lt​​​​​​​  
这篇文章主要介绍了Android中分析Jetpack Compose动画内部的实现原理,文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的小伙伴可以参考一下

前言

Compose的动画Api用起来很简单,效果看起来很神奇,那么它内部到底是如何运转的呢?

使用动画的代码示例:

var isOffset by remember { mutableStateOf(false) }
val offsetAnimation by animateDpAsState(targetValue = if (isOffset) 100.dp else 0.dp)
Button(
    onClick = { isOffset = !isOffset },
    modifier = Modifier.offset(0.dp, offsetAnimation)
) {
    Text(text = "点我进行位移")
}

看到有一个Boolean类型的isOffset状态,控制着offsetAnimation动画,然后动画又控制着Button的offset,最终实现了动画效果

正文

我们主要就看一下animateDpAsState(animate*AsState)做了什么

跟一下animateDpAsState最后会走进animateValueAsState方法中:

方法内部创建了一个Animatable动画类

然后我们跟着上图的箭头看,在targetValue发生变化后,会通过channel来发送targetValue值的变化,然后launch启动一个协程,并在其中调用了animatable的animateTo方法

接着我们就看看Animatable类是如何做动画的:

主要就是内部持有一个AnimationState类型(State)的internalState 变量,然后我们接着上面的代码跟一下animateTo方法:

没什么好说的,包了一下targetValue,接着就调用了runAnimation方法,接着往下跟(截不下分成两张图):

主要逻辑就是endState.animate()
endState就是copy的Animatable中的AnimationState对象internalState,也就是动画的初始值

然后animation就是在animateTo方法中包装了动画目标值的对象

接着跟animate方法:

这个方法主要分为两部分,第一部分就是构建了一个AnimationScope,一个数据结构,用来存放动画需要的一些信息

第二个部分就是真正动画计算和生效的地方,doAnimationFrame

首先会在第一次创建AnimationScope的时候执行一次(或再一下帧执行一次,通过callWithFrameNanos方法)

然后会判断如果动画还未执行完毕,就一直循环(while),一帧一帧执行doAnimationFrame计算动画的值

doAnimationFrame方法代码:

其中通过时间等参数计算当前动画应该设置的值,包括lastFrameTimeNanos和value等属性,然后再最后调用updateState方法去将AnimationScope的值更新到AnimationState中

updatState方法代码:

这个state对象其实也就是animateDpAsState中的Animatable动画类中的AnimationState对象internalState

所以上面其实就是Compose中动画的简化流程

总结

由于AnimationState是一个State,在Compose中使用会自动监听其变化,只要其value变化了,就会导致相应位置重组,然后Composable就会使用新的值来展示不同的效果,

比如最开始的示例代码:

var isOffset by remember { mutableStateOf(false) }
val offsetAnimation by animateDpAsState(targetValue = if (isOffset) 100.dp else 0.dp)
Button(
    onClick = { isOffset = !isOffset },
    modifier = Modifier.offset(0.dp, offsetAnimation)
) {
    Text(text = "点我进行位移")
}

在修改了isOffset后,animateDpAsState中的值就会因为动画的计算和修改内部state的value,导致Button的offset函数一直被重新调用,使Button不停的向下移动

其实Compose中的动画如果不考虑那么多东西的话,可以简化为如下代码:

    /**
     * creator: lt
     * effect : 自定义的动画播放器,逻辑更简单
     * warning:
     * [initialValueWithState]动画要改变的状态,起始动画值为其value值
     * [targetValue]要通过动画转化到的目标值
     * [duration]动画的持续时间
     */
    @OptIn(ExperimentalComposeApi::class)
    suspend fun animateWithFloat(
        initialValueWithState: MutableState<Float>,
        targetValue: Float,
        duration: Int = AnimationConstants.DefaultDurationMillis,
    ) {
        //动画起始值,目标差值
        val startValue = initialValueWithState.value
        val valueToBeTransformed = targetValue - startValue
        //动画起始时间,持续时间
        val startTime = System.nanoTime()
        val duration = duration * 1000000L
        //通过循环在下一帧计算动画的值
        val frameClock = coroutineContext.monotonicFrameClock
        while (System.nanoTime() <= startTime + duration) {
            frameClock.withFrameNanos {
                //计算动画的值,并设置值给状态
                val progress = minOf(it - startTime, duration).toFloat() / duration
                val increase = progress * valueToBeTransformed
                initialValueWithState.value = startValue + increase
            }
        }
    }

使用方式如下,效果跟示例差不多:

ps:不建议线上项目用这个api,还是用系统的比较好,如果想使用也可以参考(欢迎star): ComposeViews/MAnimator.kt at main · ltttttttttttt/ComposeViews (github.com)

到此这篇关于Android中分析Jetpack Compose动画内部的实现原理的文章就介绍到这了,更多相关Android Jetpack Compose内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 基于Android6.0实现弹出Window提示框

    基于Android6.0实现弹出Window提示框

    这篇文章主要为大家详细介绍了基于Android6.0实现弹出Window提示框,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-10-10
  • Android源码探究之BaseDexClassLoader的使用

    Android源码探究之BaseDexClassLoader的使用

    今天解决一个插件化问题的时候,竟然发现SO没有正常加载,很怪异,最终排查下来发现竟然是参数传入错误导致的。这就扯到了本文的标题上了,BaseDexClassLoader中的4个参数该如何传入,传入的又是什么呢
    2022-08-08
  • Flutter 网络请求框架封装详解

    Flutter 网络请求框架封装详解

    这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-03-03
  • Android仿QQ可拉伸头部控件

    Android仿QQ可拉伸头部控件

    这篇文章主要为大家详细介绍了Android仿QQ可拉伸头部控件,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-11-11
  • Android使用OKHttp包处理HTTP相关操作的基本用法讲解

    Android使用OKHttp包处理HTTP相关操作的基本用法讲解

    这篇文章主要介绍了Android使用OKHttp包处理HTTP相关操作的基本用法讲解,包括操作如何利用OKHttp操作HTTP请求与处理缓存等内容,需要的朋友可以参考下
    2016-07-07
  • Android签名机制介绍:生成keystore、签名、查看签名信息等方法

    Android签名机制介绍:生成keystore、签名、查看签名信息等方法

    这篇文章主要介绍了Android签名机制介绍:生成keystore、签名、查看签名信息等方法,本文讲解了升级App、权限检查、生成keystore、对apk进行签名、查看签名信息等内容,需要的朋友可以参考下
    2015-04-04
  • 利用Android实现比较炫酷的自定义View

    利用Android实现比较炫酷的自定义View

    自定义View、多线程、网络,被认为是Android开发者必须牢固掌握的最基础的三大基本功,这篇文章主要给大家介绍了关于如何利用Android实现比较炫酷的自定义View的相关资料,需要的朋友可以参考下
    2021-07-07
  • Android实现调用摄像头进行拍照功能

    Android实现调用摄像头进行拍照功能

    这篇文章主要为大家详细介绍了Android实现调用摄像头进行拍照功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-04-04
  • Android MediaPlayer 音频倍速播放 调整播放速度问题

    Android MediaPlayer 音频倍速播放 调整播放速度问题

    这篇文章主要介绍了Android MediaPlayer 音频倍速播放,调整播放速度,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-09-09
  • android获取手机唯一标识的方法

    android获取手机唯一标识的方法

    这篇文章主要介绍了获取安卓的手机或者平板的唯一标识的方法,需要的朋友可以参考下
    2014-02-02

最新评论