属性动画简单分析(一)

来源:互联网 发布:forespider爬虫软件 编辑:程序博客网 时间:2024/05/28 22:13

《在《线性贝塞尔曲线的简单说明》这篇博文中简单介绍了贝塞尔曲线在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<String, PropertyValuesHolder>(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<Keyframe>();        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的创建流程简单分析完毕,至于属性动画的执行流程,篇幅有限,下篇博文《属性动画的简单分析二》再分析,不足之处欢迎批评指正。

1 0