《在《线性贝塞尔曲线的简单说明》这篇博文中简单介绍了贝塞尔曲线在android里简单的应用,文章开头说这些有什么用呢?跟属性动画完全不搭边啊!昨天晚上自己仔细研究了下属性动画KeyFrame的源码,发现基本的算法跟《贝塞尔曲线简单说明》这篇博客写的差不多,所以对有关具体算法的说明还请读者参考《贝塞尔曲线的简单说明》这篇博文。
至于为什么会想到阅读属性动画的源码,其实原因也很简单:在自己重构代码的过程中,总感觉自己项目架构写的不是很优雅,扩展性不是很强,而且结构混乱,就想分析分析这个源码看看别人是怎么组织项目架构的,以前研究Gson的源码也是如此原因。通过分析属性动画的源码,确实也体会到了不少的东西,甚至有的没办法用语言描述出来。扯得有点多了,闲言少叙,书归正传.
属性动画的核心原理也很简单:
1)传入你要操作的对象,即目标对象,也即是Target.
2) 传入你要操作的属性propertyName
3) 通过反射机制,不断的调用Target对象的setPropertyName方法:当然setPropertyName方法的具体实现完全由咱们IT Monkey自己定制。
ObjectAnimation类本身提供了三种构造函数来进行对象的构建,默认函数为public,其余的两个是private的,默认的构造函数在这里暂且不提。如果想要创建ObjectAnimation对象的话,其内部提供了几个静态方法来创建:比如ofObject,ofInt等,鉴于本人在开发中的使用情况就从ofObject这个方法开始说起:
public static ObjectAnimator ofObject(Object target, String propertyName,
TypeEvaluator> evaluator, Object... values) {
//调用私有构造器
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
//保存属性动画客户端传来的序列值
anim.setObjectValues(values);
//设置估值器
anim.setEvaluator(evaluator);
return anim;
}
1)调用了私有构造器来初始化ObjectAnimator 对象,这个构造器也很简单,就是ofObject参数中传入的target和propertyName分别赋予ObjectAnimator的mTarget和mPropertyName,并将mInitialized这个表示是否初始化完成的boolean变量设置为false,该变量的作用就是用来标记动画是否可以开始!
2)既然propertyName和target都已经复制完毕,那么很简单接下来就是处理ofObject方法传来的values了:交给setObjectValues处理
public void setObjectValues(Object... values) {
if (mValues == null || mValues.length == 0) {
if (mProperty != null) {
//省略部分代码
} else {//调用的此处
//将values序列交给 PropertyValuesHolder的ofObject处理
setValues(PropertyValuesHolder.ofObject(mPropertyName,
(TypeEvaluator) null, values));
}
} else {
super.setObjectValues(values);
}
}
最终会发现传入的values序列值交给了PropertyValuesHolder的静态方法ofObject,通过该方法的处理,生成了PropertyValuesHolder对象,作为参数传入了ObjectAnmiaton对象的setValues方法。为了文章的连贯性,把PropertyValuesHolder的说明暂时阁下,先简单看一个setValues方法:
//方法参数是PropertyValuesHolder,说明你可以用ObjectAnmation的setValues方法传入若干个PropertyValuesHolder对象
public void setValues(PropertyValuesHolder... values) {
int numValues = values.length;
mValues = values;
//key:属性名,value:属性对应的PropertyValuesHolder
mValuesMap = new HashMap(numValues);
//把传入的PropertyValuesHolder对象添加到mValuesMap中
for (int i = 0; i < numValues; ++i) {
PropertyValuesHolder valuesHolder = (PropertyValuesHolder) values[i];
mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
}
// New property/values/target should cause re-initialization prior to
// starting
mInitialized = false;
}
setValues方法很简单,具体来说就是做了如下工作:
1)将方法参数复制给ObjectAnimation对象的PropertyValuesHolder mValues[]数组;
2)将PropertyValuesHolder对象数组添加到以propertyName为key,以PropertyValuesHolder对象为value的map中。
到此为止,从流程上来说ObjectAnimation的ofObject方法已经说名完毕,但是根据前文的说明,ofObject方法传入的values是交给了PropertyValuesHolder,我们现在看看PropertyValuesHolder这个类具体干了什么好事儿。
PropertyValuesHolder:顾名思义,就是持有propertyName所对应的values序列值的对象,一个PropertyValuesHolder对象持有一个propertyName及其对应的values序列,只不过这个序列是通过KeyFramseSet持有,前面通过ofObject将propertyName和values对象传入进来:
public static PropertyValuesHolder ofObject(String propertyName,
@SuppressWarnings("rawtypes") TypeEvaluator evaluator, Object... values) {
//设置给propertyName方法
PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
//把vlaue方法最终传给PVH
pvh.setObjectValues(values);
pvh.setEvaluator(evaluator);
return pvh;
}
values交给setObjectValues来处理:
public void setObjectValues(Object... values) {
mValueType = values[0].getClass();
//把values最终传给mKeyFrameSet方法
mKeyframeSet = KeyframeSet.ofObject(values);
}
通过代码可知最终values被传到了KeyFrameSet的ofObject里面,经过处理生成一个KeyFrameSet对象:
public static KeyframeSet ofObject(Object... values) {
int numKeyframes = values.length;
//ObjectKeyframe最少两帧动画
ObjectKeyframe keyframes[] = new ObjectKeyframe[Math.max(numKeyframes,
2)];
if (numKeyframes == 1) {//如果传过来的是一个值
//动画第一帧其其实fraaction=0,该帧对应的值是0
keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f);
//动画第二个fraction=1,该帧对应的值就是你传入的那个值
keyframes[1] = (ObjectKeyframe) Keyframe.ofObject(1f, values[0]);
} else {
keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f, values[0]);
//把传入的其余的值根据传入的序列对i/(n-1)-->values[i]进行映射
for (int i = 1; i < numKeyframes; ++i) {
keyframes[i] = (ObjectKeyframe) Keyframe.ofObject((float) i
/ (numKeyframes - 1), values[i]);
}
}
//返回帧集合:传入多少个值就传多少个有多少帧
return new KeyframeSet(keyframes);
}
public KeyframeSet(Keyframe... keyframes) {
mNumKeyframes = keyframes.length;
//帧序列集合
mKeyframes = new ArrayList();
mKeyframes.addAll(Arrays.asList(keyframes));
//记录第一帧
mFirstKeyframe = mKeyframes.get(0);
//记录最后一帧
mLastKeyframe = mKeyframes.get(mNumKeyframes - 1);
mInterpolator = mLastKeyframe.getInterpolator();
}
上面的算法很简单,跟《线性贝塞尔曲线简单说明》这篇博文算法思路完全一样!!!在这里稍微总结一下:
1)客户端通过ObjectAnimation的ofObject()方法传入多少个value(一个除外)就有多少个KeyFrame对象,每个KeyFrame对象持有与KeyFrame对象创建顺序对应的value
2)KeyFrame有一个mFraction变量,该变量的初始化计算算法为:
mFraction = (float)i/(values.length-1)
简单的用图表来表示values和KeyFrame之间的关系就是:
到此为止,算是完成了对ObjectAnimation对象的初始化工作,再次简单梳理下,分成大致几个点:
1)通过ObjectAnimation的ofXXX方法,设置propertyName和values。
2)将propertyName和values封装成PropertyValueHolder对象:每个PropertyValueHolder对象持有values组成的帧序列对象KeyFrameSet对象;
3)将步骤2创建的PropertyValueHolder对象用ObjectAnimation的mValues 数组保存起来;然后将propertyName作为key,PropertyValueHolder作为value保存到mValuesMap中。
总体上,ObjectAnimation的创建流程简单分析完毕,至于属性动画的执行流程,篇幅有限,下篇博文《属性动画的简单分析二》再分析,不足之处欢迎批评指正。