属性动画简单分析(一)

《在《线性贝塞尔曲线的简单说明》这篇博文中简单介绍了贝塞尔曲线在android里简单的应用,文章开头说这些有什么用呢?跟属性动画完全不搭边啊!昨天晚上自己仔细研究了下属性动画KeyFrame的源码,发现基本的算法跟《贝塞尔曲线简单说明》这篇博客写的差不多,所以对有关具体算法的说明还请读者参考《贝塞尔曲线的简单说明》这篇博文。

至于为什么会想到阅读属性动画的源码,其实原因也很简单:在自己重构代码的过程中,总感觉自己项目架构写的不是很优雅,扩展性不是很强,而且结构混乱,就想分析分析这个源码看看别人是怎么组织项目架构的,以前研究Gson的源码也是如此原因。通过分析属性动画的源码,确实也体会到了不少的东西,甚至有的没办法用语言描述出来。扯得有点多了,闲言少叙,书归正传.
属性动画简单分析(一)_第1张图片

属性动画的核心原理也很简单:
1)传入你要操作的对象,即目标对象,也即是Target.
2) 传入你要操作的属性propertyName
3) 通过反射机制,不断的调用Target对象的setPropertyName方法:当然setPropertyName方法的具体实现完全由咱们IT Monkey自己定制。

简单原理叙说完毕,那么就开始开车了:
属性动画简单分析(一)_第2张图片

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之间的关系就是:
属性动画简单分析(一)_第3张图片

到此为止,算是完成了对ObjectAnimation对象的初始化工作,再次简单梳理下,分成大致几个点:
1)通过ObjectAnimation的ofXXX方法,设置propertyName和values。
2)将propertyName和values封装成PropertyValueHolder对象:每个PropertyValueHolder对象持有values组成的帧序列对象KeyFrameSet对象;
3)将步骤2创建的PropertyValueHolder对象用ObjectAnimation的mValues 数组保存起来;然后将propertyName作为key,PropertyValueHolder作为value保存到mValuesMap中。

最终简单的创建骨架可以用如下图来作为总结:
属性动画简单分析(一)_第4张图片

总体上,ObjectAnimation的创建流程简单分析完毕,至于属性动画的执行流程,篇幅有限,下篇博文《属性动画的简单分析二》再分析,不足之处欢迎批评指正。

你可能感兴趣的:(android)