通过源码,手把手带你学属性动画(四) - 理解插值器(附神器)
2016-09-24 15:11
288 查看
转载请注明出处:http://blog.csdn.net/my_truelove/article/details/52619091
访问 ruicb.com,一键抵达我的博客!
这已经是系列文章第四篇了,算是基础知识的最后一篇了,讲完这篇就开始分析源码、写动效了,我感觉我能写到十,哈哈!感兴趣的可以看一下前几篇文章:
《通过源码,手把手带你学属性动画(一) - 相关类总览》
《通过源码,手把手带你学属性动画(二) - ValueAnimator基础》
《通过源码,手把手带你学属性动画(三) - ValueAnimator进阶》
说起插值器你可能会陌生,因为在之前的几篇文章中我们都没有提及过,不过没关系,本文将一步一步带你全面了解插值器。
细心地读者会发现,在系列文章第一篇的“监听动画每一帧的值”部分,我们打印了动画的值,发现其变化速率并不是匀速变化的,而是呈现“先加速、后减速”的状态。这就是插值器的作用!
下面,跟着源码及官方文档,我们一起探究插值器。
A time interpolator defines the rate of change of an animation. This allows animations to have non-linear motion, such as acceleration and deceleration.
意思是:该时间插值器定义了动画的变化率,允许动画做非线性的运动,比如加速、减速。
这样,插值器的主要作用我们就明白了。接下来,看看这个接口的代码,该接口只有一个接口方法:
不管是系统内置的插值器,还是我们自定义插值器,只需要实现接口并重写该方法,就可以起到插值器的作用。
该方法的作用是什么呢?上图保留了源码中对方法的注释,我就不直译了,说下大概的意思:
方法参数 input 接收 0 和 1.0 之间的值表示动画的当前进度,是线性变化的,其中0表示开始,1.0表示结束;
返回值表示对 input 进行插值之后的值,我们就是在这儿做“手脚”,让返回值不再是线性的,就完成自己定义动画的变化率了。
直接 return fraction,则实际的值依然呈现匀速变化。
这次 return (float)(Math.cos((input 1) * Math.PI) / 2.0f) 0.5f,则实际的值将呈现“先加速、后减速”的变化过程。(具体如何实现先加速后减速,后面再介绍。)
上述总结可能不太好理解,下面举个例子。在40ms内,距离 x 从0过渡到40,结合上述总结,则匀速变化和变速变化的过程分别如下图,很直观:
图片来自官方网站
上面第二张图的效果(先加速后减速),就体现了插值器的作用,其是系统内置的AccelerateDecelerateInterpolator插值器。下面,带你着重分析一下系统内置插值器:AccelerateDecelerateInterpolator,看其是如何实现动画“先加速、后减速”变化的。
这么多插值器,数都数不过来,就不一一介绍了,具体效果大家可以自行看一下,就只看关键的getInterpolation()方法就好了。
下面着重分析插值器 AccelerateDecelerateInterpolator,其是属性动画默认采用的插值器,我们看一下其内部 getInterpolation() 方法的实现:
就一行代码,这个表达式有没有觉得似曾相识呢?对,就是在上面举例子用的表达式,那么其如何实现先加速后减速的效果呢?我们分析一下:
输入:input,值即为fraction,从0~1
计算:cos((input+1) * Math.PI) 即表示cos(π)到cos(2π),该区间函数值范围为-1~1,再除以2,则为-0.5~0.5,最后再加上0.5
输出:范围0~1。
我们对比一下直接输出input和对input进行变换后输出的效果图:
很明显,绿色线条表示直接输出,其表示随时间推移的匀速变化,而红色的曲线,是对input经过三角变换后的输出,分析其切线,能明显跟决出其先加速,在 input 为 0.5 处速度达到最大,而后减速的过程。
其他插值器的原理类似,大家可以根据上述分析思路,查看其他插值器的getInterpolation()方法。下面,我们来看看如何自定义插值器!
系统内置插值器已经能满足我们的大多数需求,但有时候难免会需要根据特殊业务自定义。出于演示,来一个系统没有的效果 – “先减速后加速”。
自定义一个类叫 MyDecelerateAccelerateInterpolator,然后重写getInterpolation()方法即可,我直接上代码:
根据上面分析AccelerateDecelerateInterpolator的逻辑,分析MyDecelerateAccelerateInterpolator 对你来说不是问题,我就不再分析了,下面直接看变换前后的对比效果:
上图的效果不难理解吧。
经过前面的讲解,相信你已经掌握了有关插值器的原理,以及如何自定义插值器。自定义这块,一般都会用到函数的知识,像上面的三角函数就是。如果不会,你应该找高中体育老师去嘿嘿!
具体的效果可自行打印查看,在后面介绍 ObjectAnimator 并对 view 进行动画时,再演示效果。
返回值 = 开始值 + (终点值-开始值) * 进度
如果你认为此处的“进度”就是动画运行的时间进度,那就错了,这样的话要插值器还怎么派上用场?
实际上,该“进度”指的是时间进度经过插值器计算后得到的动画进度。用 fraction 表示动画运行时间占总的动画时间比例,即时间进度,那么上述表达式应该扩展为:
返回值 = 开始值 + (终点值-开始值) * getInterpolation(fraction)
记住这一点就好了,在下节分析源码时我们会证明这一点。
本文就到这儿了,文中若有任何不妥之处,还望留言指正;若对你有帮助,还望点个赞!
下篇分析源码,看 ValueAnimator 动画实现的机制及流程,敬请期待!不想错过了可以关注我的公众号哈!
扫描下方二维码,关注我的公众号,及时获取最新文章推送!
访问 ruicb.com,一键抵达我的博客!
这已经是系列文章第四篇了,算是基础知识的最后一篇了,讲完这篇就开始分析源码、写动效了,我感觉我能写到十,哈哈!感兴趣的可以看一下前几篇文章:
《通过源码,手把手带你学属性动画(一) - 相关类总览》
《通过源码,手把手带你学属性动画(二) - ValueAnimator基础》
《通过源码,手把手带你学属性动画(三) - ValueAnimator进阶》
说起插值器你可能会陌生,因为在之前的几篇文章中我们都没有提及过,不过没关系,本文将一步一步带你全面了解插值器。
细心地读者会发现,在系列文章第一篇的“监听动画每一帧的值”部分,我们打印了动画的值,发现其变化速率并不是匀速变化的,而是呈现“先加速、后减速”的状态。这就是插值器的作用!
下面,跟着源码及官方文档,我们一起探究插值器。
1. 插值器介绍
插值器只是一个概念,系统中与之相关的类叫做 TimeInterpolator ,其只是一个接口,准确来说叫做“时间插值器”。该接口的注释为:A time interpolator defines the rate of change of an animation. This allows animations to have non-linear motion, such as acceleration and deceleration.
意思是:该时间插值器定义了动画的变化率,允许动画做非线性的运动,比如加速、减速。
这样,插值器的主要作用我们就明白了。接下来,看看这个接口的代码,该接口只有一个接口方法:
/** * @param input A value between 0 and 1.0 indicating our current point * in the animation where 0 represents the start and 1.0 represents * the end * @return The interpolation value. This value can be more than 1.0 for * interpolators which overshoot their targets, or less than 0 for * interpolators that undershoot their targets. */ float getInterpolation(float input);
不管是系统内置的插值器,还是我们自定义插值器,只需要实现接口并重写该方法,就可以起到插值器的作用。
该方法的作用是什么呢?上图保留了源码中对方法的注释,我就不直译了,说下大概的意思:
方法参数 input 接收 0 和 1.0 之间的值表示动画的当前进度,是线性变化的,其中0表示开始,1.0表示结束;
返回值表示对 input 进行插值之后的值,我们就是在这儿做“手脚”,让返回值不再是线性的,就完成自己定义动画的变化率了。
2. 插值器原理分析
系统会根据动画当前时间和动画总时长,计算时间进度fraction,fraction为float类型,值范围为0f~1f,该值是随着时间流逝匀速变化的。我们将 fraction 作为参数input传入getInterpolation(float input)方法:直接 return fraction,则实际的值依然呈现匀速变化。
这次 return (float)(Math.cos((input 1) * Math.PI) / 2.0f) 0.5f,则实际的值将呈现“先加速、后减速”的变化过程。(具体如何实现先加速后减速,后面再介绍。)
上述总结可能不太好理解,下面举个例子。在40ms内,距离 x 从0过渡到40,结合上述总结,则匀速变化和变速变化的过程分别如下图,很直观:
图片来自官方网站
上面第二张图的效果(先加速后减速),就体现了插值器的作用,其是系统内置的AccelerateDecelerateInterpolator插值器。下面,带你着重分析一下系统内置插值器:AccelerateDecelerateInterpolator,看其是如何实现动画“先加速、后减速”变化的。
3.AccelerateDecelerateInterpolator
系统内置了许多实现了TimeInterpolator接口的插值器,让我们看官方文档:这么多插值器,数都数不过来,就不一一介绍了,具体效果大家可以自行看一下,就只看关键的getInterpolation()方法就好了。
下面着重分析插值器 AccelerateDecelerateInterpolator,其是属性动画默认采用的插值器,我们看一下其内部 getInterpolation() 方法的实现:
public float getInterpolation(float input) { return (float)(Math.cos((input+1) * Math.PI) / 2.0f) + 0.5f; }
就一行代码,这个表达式有没有觉得似曾相识呢?对,就是在上面举例子用的表达式,那么其如何实现先加速后减速的效果呢?我们分析一下:
输入:input,值即为fraction,从0~1
计算:cos((input+1) * Math.PI) 即表示cos(π)到cos(2π),该区间函数值范围为-1~1,再除以2,则为-0.5~0.5,最后再加上0.5
输出:范围0~1。
我们对比一下直接输出input和对input进行变换后输出的效果图:
很明显,绿色线条表示直接输出,其表示随时间推移的匀速变化,而红色的曲线,是对input经过三角变换后的输出,分析其切线,能明显跟决出其先加速,在 input 为 0.5 处速度达到最大,而后减速的过程。
其他插值器的原理类似,大家可以根据上述分析思路,查看其他插值器的getInterpolation()方法。下面,我们来看看如何自定义插值器!
4. 自定义插值器
只知道用是不够的,还要会自定义!系统内置插值器已经能满足我们的大多数需求,但有时候难免会需要根据特殊业务自定义。出于演示,来一个系统没有的效果 – “先减速后加速”。
自定义一个类叫 MyDecelerateAccelerateInterpolator,然后重写getInterpolation()方法即可,我直接上代码:
public class MyDecelerateAccelerateInterpolator implements TimeInterpolator { @Override public float getInterpolation(float input) { if (input <= 0.5) { return (float) (Math.sin(Math.PI * input)) / 2; } else { return (float) (2 - Math.sin(Math.PI * input)) / 2; } } }
根据上面分析AccelerateDecelerateInterpolator的逻辑,分析MyDecelerateAccelerateInterpolator 对你来说不是问题,我就不再分析了,下面直接看变换前后的对比效果:
上图的效果不难理解吧。
经过前面的讲解,相信你已经掌握了有关插值器的原理,以及如何自定义插值器。自定义这块,一般都会用到函数的知识,像上面的三角函数就是。如果不会,你应该找高中体育老师去嘿嘿!
5. 使用插值器
说了这么多,对插值器应该理解了吧,下面说说使用。我们可以通过setInterpolator()方法使用插值器,这个真没啥要强调的,你可以把其他插值器都试试:valueAnimator.setInterpolator(new MyDecelerateAccelerateInterpolator());
具体的效果可自行打印查看,在后面介绍 ObjectAnimator 并对 view 进行动画时,再演示效果。
6. 补充
还记得在上节介绍 TypeEvaluator 时,阐明其作用是:告诉属性动画系统如何从开始值过渡到结束值。并且,本质可以用如下表达式概括:返回值 = 开始值 + (终点值-开始值) * 进度
如果你认为此处的“进度”就是动画运行的时间进度,那就错了,这样的话要插值器还怎么派上用场?
实际上,该“进度”指的是时间进度经过插值器计算后得到的动画进度。用 fraction 表示动画运行时间占总的动画时间比例,即时间进度,那么上述表达式应该扩展为:
返回值 = 开始值 + (终点值-开始值) * getInterpolation(fraction)
记住这一点就好了,在下节分析源码时我们会证明这一点。
7. 福利
文章的最后,送给大家一个理解插值器的神器,效果如下,自己使用感受一下!本文就到这儿了,文中若有任何不妥之处,还望留言指正;若对你有帮助,还望点个赞!
下篇分析源码,看 ValueAnimator 动画实现的机制及流程,敬请期待!不想错过了可以关注我的公众号哈!
扫描下方二维码,关注我的公众号,及时获取最新文章推送!
相关文章推荐
- 通过源码,手把手带你学属性动画(二) - ValueAnimator基础
- 通过源码,手把手带你学属性动画(一) - 相关类总览
- 通过源码,手把手带你学属性动画(三) - ValueAnimator进阶
- 从源码角度理解android动画Interpolator类的使用
- 通过c# 实现自定义属性改变触发自定义事件 ,理解自定义事件及其触发过程
- 神器 VisuAlgo:通过动画学习算法和数据结构
- 神器 VisuAlgo:通过动画学习算法和数据结构
- Android源码分析—属性动画的工作原理
- Swift - 通过设置视图的transform属性实现动画
- Android 属性动画 源码解析 深入了解其内部实现
- Android L(5.0)源码之图形与图像处理之动画——Frame、Tween、属性动画、SurfaceView
- css3中变形函数(同样是对元素来说的)和元素通过改变自身属性达到动画效果
- Flex中如何通过mode属性,设置进度条ProgressBar动画状态
- 安卓 属性动画 ValueAnimator ObjectAnimator 源码分析 关键处
- Android动画中属性fillafter和fillbefore的正确理解
- Android 属性动画(Property Animation) 源码解析 深入了解其内部实现
- Android源码分析—属性动画的工作原理
- 源码变换 超文本标记语言(即HTML),是用于描述网页文档的一种标记语言。 HTML通过文本来描述文档显示出来应该具有的“样子”。它主要通过标签来定义对象的显示属性或行为。
- Android 属性动画 源码解析 深入了解其内部实现
- 安卓属性动画源码解析