1、概述
Android中想做很炫酷的动画效果,相信在很多时候你都可以选择使用属性动画,关于属性动画如何使用,我们已经很详细的写过两篇博客讲解。如果你还不了解,请参考:
Android 属性动画(Property Animation) 完全解析 (上)
Android 属性动画(Property Animation) 完全解析 (下)
本篇博客将分析属性动画的实现源码,带你深入的了解Android属性动画的内部实现机制。如果你经常用属性动画,但又一直没有去查看其源码实现,没关系,请往下看。
2、分析前的猜想
在源码分析之前,我们需要有一个明确的思路,例如:源码的入口的选择、甚至对其实现进行简单的猜测,源码分析相当于一个验证的过程,带着一个目标去看源码,这样的话,分析和理解起来更为方便。
对于实现属性动画,最常用的类就是ObjectAnimator了,只需要简单的设置目标view,属性,以及目标值等必要属性,调用一下start();我们的动画就完成了。
类似如下代码:
1 2 3 4 5 6 |
ObjectAnimator .ofInt(target,propName,values[]) .setInterpolator(LinearInterpolator) .setEvaluator(IntEvaluator) .setDuration(500) .start(); |
上述代码很好理解吧,设置动画作用的view,作用的属性,动画开始、结束、以及中间的任意个属性值;
然后是设置插值器,当然了插值器这个词比较难理解,我要是说例如:AccelerateInterpolator、LinearInterpolator
然后设置估值算法,这个看名字挺高端,其实内部实现尤其简单: return (int)(startInt + fraction * (endValue – startInt)); 开始值,加上当前的属性改变的百分比*(结束-开始)
当然了,这个百分比是fraction ,其实就是上面的插值器算出来的。比如线性插值器:fraction 值就是currentTime – mStartTime) / mDuration,动画的运行时间/总设置时间。
然后是设置动画事件,
最后start()。
好了,现在我想问个问题,根据上面这些参数,如果我要你设计个属性动画框架,你怎么做?
这个嘛,好整,拿到上述参数之后,start()中,开启一个定时器,去执行一个任务;在任务内部,根据Interpolator计算出来的fraction,交给Evaluator,得到属性当前应该设置的值,然后反射设置tagert的指定属性,ok,奏事这么简单。嗯,大体上应该就是这样,当然了,源码的实现肯定复杂很多,但是万变不离其宗,所以接下来的源码阅读,就是去验证我们的这个答案。
3、源码分析
好了,猜想完了,我们就得进入验证阶段了~~
那么,我们源码的入口就是上述代码了,不过貌似上述代码调用了好几个方法,but,我觉得start之前的代码,无法是初始化实例,设置一些成员变量。
首先我们看ofInt,这里为了简单,我们的ofInt中的values参数,默认就一个,类似 .ofInt(view, “translationX”, 300) ;
1、ofInt
1 2 3 4 5 |
public static ObjectAnimator ofInt(Object target, String propertyName, int... values) { ObjectAnimator anim = new ObjectAnimator(target, propertyName); anim.setIntValues(values); return anim; } |
首先调用ObjectAnimator的构造方法传入了一个target和propName,估计就是创建对象,然后旧路下target和propName,简单看下
1 2 3 4 5 6 7 8 9 |
private ObjectAnimator(Object target, String propertyName) { mTarget = target; setPropertyName(propertyName); } public void setPropertyName(String propertyName) { //... mPropertyName = propertyName; mInitialized = false; } |
记录完成target,propName以后,调用setIntValues
1 2 3 4 |
@Override public void setIntValues(int... values) { setValues(PropertyValuesHolder.ofInt(mPropertyName, values)); } |
可以看到,把我们的propName,和values传入到了一个PropertyValuesHolder的ofInt方法中,去构造一个PropertyValuesHolder对象,这个对象是干什么的呢?
从字面上看,是保存view在动画期间的属性和值,记住是动画期间的。继续往下看:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public static PropertyValuesHolder ofInt(String propertyName, int... values) { return new IntPropertyValuesHolder(propertyName, values); } public IntPropertyValuesHolder(String propertyName, int... values) { mPropertyName = propertyName; setIntValues(values); } @Override public void setIntValues(int... values) { mValueType = int.class; mKeyframeSet = KeyframeSet.ofInt(values); mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet; } |
可以看到在IntPropertyValuesHolder内部存储了我们的propertyName;,然后又调用了setIntValues,存储了我们的mValueType ,此外还存了一个mIntKeyframeSet。
这里又出现一个新名词,叫做mKeyframeSet,这个是由 KeyframeSet.ofInt(values);得到的。
那么这个KeyframeSet是什么呢?单纯的理解是,Keyframe的集合,而Keyframe叫做关键帧,为一个动画保存time/value(时间与值)对。
那么我们去看看它是如何通过KeyframeSet.ofInt(values);去构造与保存的: