×

Android属性动画源码分析(三)

96
古龙小蓝瓶
2017.06.20 15:51* 字数 1106

上一篇文章中,我们对ObjectAnimator.ofFloat方法进行了分析,其中生成PropertyValuesHolder对象的方法PropertyValuesHolder.ofFloat("", values)我们并没有进去看,这一篇我们就看看这个"holder"生成的时候究竟做了什么事。
  首先我们进入ObjectAnimator.ofFloat方法:

//PropertyValuesHolder.java
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
        return new FloatPropertyValuesHolder(propertyName, values);
    }

这里新建了一个FloatPropertyValuesHolder,类似于工厂模式,对不同的需要产生不同类型的PropertyValuesHolder对象,可以是int,float或者其他对象类型(提供了转换器),我们这里不做细致研究,还是沿着主线路往下看:

//FloatPropertyValuesHolder.java
 public FloatPropertyValuesHolder(String propertyName, float... values) {
            super(propertyName);
            setFloatValues(values);
  }

...
//PropertyValuesHolder.java  
//super(propertyName)进了这里
private PropertyValuesHolder(String propertyName) {
        mPropertyName = propertyName;
    }
...
//FloatPropertyValuesHolder.java 
//setFloatValues(values)走了这里
@Override
        public void setFloatValues(float... values) {
            super.setFloatValues(values);
            mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
        }

这里主要讲属性名保存了起来,并且设置了一个"KeyFrameSet",即关键帧集合,很明显,关键帧的生成代码就在super.setFloatValues(values)里,我们进去在一探究竟:

    //PropertyValuesHolder.java
    public void setFloatValues(float... values) {
        mValueType = float.class;
        mKeyframes = KeyframeSet.ofFloat(values);
    }

这个keyframeSet的构建形式和PropertyValuesHolder的形式看起来类似,当然,实际也是如此:

//KeyFrameSet.java
 public static KeyframeSet ofFloat(float... values) {
        boolean badValue = false;
        int numKeyframes = values.length;
        FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
        if (numKeyframes == 1) {
            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
            keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
            if (Float.isNaN(values[0])) {
                badValue = true;
            }
        } else {
            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
            for (int i = 1; i < numKeyframes; ++i) {
                keyframes[i] =
                        (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
                if (Float.isNaN(values[i])) {
                    badValue = true;
                }
            }
        }
        if (badValue) {
            Log.w("Animator", "Bad value (NaN) in float animator");
        }
        return new FloatKeyframeSet(keyframes);
    }

  我们看到这段代码的大体作用是根据规则生成了一系列的KeyFrames()关键帧),并且将其添加到了KeyFrameSet(即关键帧集合),那么这个关键帧是什么呢,参照官方说法:

//KeyFrames.java
/**
 * This class holds a time/value pair for an animation. The Keyframe class is used
 * by {@link ValueAnimator} to define the values that the animation target will have over the course
 * of the animation. As the time proceeds from one keyframe to the other, the value of the
 * target object will animate between the value at the previous keyframe and the value at the
 * next keyframe.
 *...
 */

  按照官方的说法,KeyFrames是一个保存动画中时间/值的键值对,用于定义动画目标进行过程中的过程值。要进行动画的目标将会依照这些KeyFrames进行动画。
  通俗一点的说,比如最终有三个KeyFrames,第一个是0.0,值是100,第二个是0.5,值是200,第三个是是1.0,值是300;动画目标就会根据这个KeyFrames在最开始的时候得到100的值,在时间进行到一半的时候得到200的值,在时间结束的时候获得300的值。其实KeyFrames就是代表了动画进行的关键帧数,为动画的实际执行提供了“变化的方向”, 而KeyFramesSet代表了动画进行的关键帧数集合。了解了这些,我们在回来分析下KeyFrameSet.ofFloat的代码:
  首先我们知道传入的参数是我们之前的变化的一系列值,例如在最开始的示例中,我们传入的是0.8f,1.0f;然后int numKeyframes = values.length;记录了传入值的数量,接下来生成了一个KeyFrames的数组(最终这个数组用于初始化KeyFrameSet),该数组的最小值为2,也就代表着最小要有两个值来控制这次变化。之后是一个判断,用于判断当前传入值的数量是1还是大于1,如果是1的话
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
就通过这两句生成一个从0到values[0]变化的帧,如果大于1(小于1不会到这里),则根据numKeyframes ,来生成numKeyFrames个帧,每个帧的进度都是通过当前帧的位置/帧的总数来平均分:
keyframes[i] =(FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
(在这个过程中,如果传入的值不符合条件,会记录一个badValues标志代表本次的值有问题)
if (Float.isNaN(values[i])) {
   badValue = true;
}

最终,将生成的所有KeyFrames放到KeyFramesSet中进行保存,然后将这个set存放到mFloatKeyframes中。
我们在贴出Keyframe.ofFloat();的实现,也是个初始化的过程:

FloatKeyframe(float fraction, float value) {
            mFraction = fraction;
            mValue = value;
            mValueType = float.class;
            mHasValue = true;
        }

  至此,我们就把属性动画的初始化过程看完了,总体理下来我们可以看出大体的初始化过程如下:

  • ObjectAnimation.ofFloat->传递要进行动画的对象,要改变的属性名,一系列的属性值
  • 属性名+属性值存放到了PropertyValueHolder中,这个用来代表一个属性的变化情况
  • 如果初始化时使用ObjectAnimation.ofPropertyValuesHolder则相当于多个属性的变化情况(类似于动画集合的效果)
  • PropertyValues.setValues()->进行变化值的设置,生成关于这系列值的关键帧
  • mKeyframes ->该PropertyValues中变化值生成的关键帧集合
      下一篇文章我们将开始分析属性动画start的过程。
日记本
Web note ad 1