zoukankan      html  css  js  c++  java
  • Android

    Android - Animation(一) 一文总结了Android中的补间动画View Animation/Tween Animation)和帧动画Drawable Animation/Frame Animation)的使用

    本篇文章主要解析属性动画(Property Animation,android3.0引入)的实现原理


                                                  下篇   属性动画的实现原理


    先来看属性动画的最简单实现:

    第一种方式:先在 /res/animator/文件夹下创建translate.xml文件定义动画。再在Java文件里引用并开启
    [java] view plain copy
    1. // 首先在/res/animator/文件夹下创建translate.xml文件。内容例如以下:  
    2. <?

      xml version="1.0" encoding="utf-8"?

      >    

    3. <objectAnimator     
    4.     xmlns:android="http://schemas.android.com/apk/res/android"     
    5.     android:propertyName="translationX"  //设置动画须要改变的属性名称  
    6.     android:duration="2000"  //设置动画的持续时间  
    7.     android:valueFrom="0.0"  //设置动画的初始值(相应上边设置的属性)  
    8.     android:valueTo="20.0">  //设置动画的结束值(相应上边设置的属性)  
    9. </objectAnimator>  
    10.   
    11. //然后在Java文件里进行引用。代码例如以下:  
    12. mButton1 = (Button) findViewById(R.id.button1);  
    13. mButton1.setOnClickListener(this);  
    14. //载入xml文件里的动画资源  
    15. mObjectAnimator = AnimatorInflater.loadAnimator(this, R.animator.translate);  
    16. mObjectAnimator.setTarget(mButton1);//设置动画的作用对象  
    17. public void onClick(View v) {  
    18.     switch(v.getId()){  
    19.     case R.id.button1:  
    20.         mObjectAnimator.start();  //开启动画  
    21.         break;    
    22.     }  
    23. }  
    另外一种方式:直接在Java文件里创建并使用动画
    [java] view plain copy
    1. mButton1 = (Button) findViewById(R.id.button1);  
    2. mButton1.setOnClickListener(this);  
    3. public void onClick(View v) {  
    4.     //利用ofFloat()方法创建动画、指定须要改变的属性的名称等并开启动画  
    5.     ObjectAnimator.ofFloat(mButton1, "translationX"0.0f,20.0f).setDuration(3000).start();  
    6. }  

    简单地说,属性动画就是在指定的时间内改变对象的属性值。

    上述的样例。从代码上看,设置了动画运行的时间、作用的目标对象mButton1及其属性translationX以及属性值的初始值和终于值,但从效果上看,mButton1在2秒钟之内在屏幕上匀速的移动了一段距离。于是我们能够猜想:

    在start()方法运行之后。是不是会不断地计算出一个值并赋给目标对象的属性?
    在属性动画中。是不是也有和补间动画里类似的插值器来改变动画的运行速率?
    假设有这种一个插值器的话,须要赋给目标对象的属性的那个值的计算是不是也和这个插值器有关?
    ... ...
    带着这些猜想,我们就以 在Java文件里创建动画的方式 为例来梳理属性动画的实现原理。


    首先是ObjectAnimator类的静态方法ofFloat
    /*
    Constructs and returns an ObjectAnimator that animates between float values. A single value implies that that value is the one being animated to. Two values imply a starting and ending values. More than two values imply a starting value, values to animate through along the way, and an ending value (these values will be distributed evenly across the duration of the animation).
    创建并返回 一个基于float 类型数值的ObjectAnimator 对象,一个value值代表动画的终点,两个value 值,则一个是起点,
    还有一个是终点,假设是多个值,则中间的值代表动画将要经过的点 ,而且这些点会均匀地分布在动画的运行过程中
    */
    public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
            ObjectAnimator anim = newObjectAnimator(target, propertyName);
            ❶ anim.setFloatValues(values);
            return anim;
    }

    ObjectAnimator的构造函数:
    [java] view plain copy
    1. private ObjectAnimator(Object target, String propertyName) {  
    2.     //为ObjectAnimator的成员变量private Object mTarget赋值,mTarget的凝视为:  
    3.     //The target object on which the property exists, set in the constructor  
    4.     mTarget = target;  
    5.     setPropertyName(propertyName);  
    6. }  
    setPropertyName方法:
    [java] view plain copy
    1. public void setPropertyName(String propertyName) {  
    2.     // mValues could be null if this is being constructed piecemeal. Just record the   
    3.     // propertyName to be used later when setValues() is called if so.  
    4.     //mValues是ObjectAnimator的父类ValueAnimator的成员变量PropertyValuesHolder[] mValues;  
    5.     //第一次运行为null  
    6.     if (mValues != null) {  
    7.         PropertyValuesHolder valuesHolder = mValues[0];  
    8.         String oldName = valuesHolder.getPropertyName();  
    9.         valuesHolder.setPropertyName(propertyName);  
    10.         mValuesMap.remove(oldName);  
    11.         mValuesMap.put(propertyName, valuesHolder);  
    12.     }  
    13.     //为ObjectAnimator的成员变量private String mPropertyName赋值  
    14.     mPropertyName = propertyName;  
    15.     // New property/values/target should cause re-initialization prior to starting  
    16.     //mInitialized 是ObjectAnimator的父类ValueAnimator的成员变量boolean mInitialized;  
    17.     //它的凝视为:Flag that denotes whether the animation is set up and ready to go.   
    18.     //Used to set up animation that has not yet been started.  
    19.     //一个Flag用于标示动画是否開始准备运行,它被用于动画还没有正式開始start之前。

      默认值为false  

    20.     mInitialized = false;  
    21. }  
    所以。第一次运行ObjectAnimator anim = new ObjectAnimator(target, propertyName);仅仅做了两件事情:
    1、为ObjectAnimator 的成员变量mTarget和mPropertyName赋值
    2、将mInitialized(定义在ObjectAnimator 的父类 ValueAnimator 中)的值设为false
    接下来是上文中处的anim.setFloatValues(values)方法:
    @Override
    public void setFloatValues(float... values) {
            if (mValues == null || mValues.length == 0) { //第一次运行mValues == null
            // No values yet - this animator is being constructed piecemeal. Init the values with whatever the current propertyName is
            // mValues眼下尚未赋值——当前的animator 正在构建中,将通过传入的values初始化mValues
                     if (mProperty != null) {
                                //mProperty 为ObjectAnimator的成员变量 private Property mProperty,第一次运行时也为null
                                setValues(PropertyValuesHolder.ofFloat(mProperty, values));
                      } else {
                               // 第一次运行时。下列函数将被调用:
    setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
                      }
             } else {
                   super.setFloatValues(values);
             }
    }

    在分析setValues()方法之前先来看PropertyValuesHolder的ofFloat()方法:

    /**
       * Constructs and returns a PropertyValuesHolder with a given property name and set of float values.
       */
       // 通过给定的propertyName 和 values创建并返回一个PropertyValuesHolder 对象
    public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
            return new FloatPropertyValuesHolder(propertyName, values);
    }
    接着FloatPropertyValuesHolder的构造函数:

    public FloatPropertyValuesHolder(String propertyName, float... values) {
            super(propertyName);
             setFloatValues(values);
    }
    首先运行的是FloatPropertyValuesHolder 的父类 PropertyValuesHolder的构造函数:

    [java] view plain copy
    1. private PropertyValuesHolder(String propertyName) {  
    2.     // 为成员变量String mPropertyName(定义在PropertyValuesHolder类中)赋值  
    3.     mPropertyName = propertyName;  
    4. }  

    我们注意到,FloatPropertyValuesHolder 、IntPropertyValuesHolder都是PropertyValuesHolder的静态内部类
    来看一下PropertyValuesHolder的类定义:
    [java] view plain copy
    1. /** 
    2.  * This class holds information about a property and the values that that property should take on during an animation.  
    3.  * PropertyValuesHolder objects can be used to create animations with ValueAnimator or ObjectAnimator that operate on several     
    4.  * different properties in parallel. 
    5.  */  
    6.   //这个类维持着一个property 和它的 值,用以在动画中呈现出来,  
    7.   //我们能够觉得它是为实现属性动画而对property及其值做的一个包装  
    8. public class PropertyValuesHolder implements Cloneable { }  
    接下来是上文中处setFloatValues()方法:

    @Override
    public void setFloatValues(float... values) {
                 super.setFloatValues(values);
                mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
    }
    首先运行的又是父类的方法:

    public void setFloatValues(float... values) {
            // 为成员变量Class mValueType(定义在父类PropertyValuesHolder中)赋值
            mValueType = float.class; 
            mKeyframeSet = KeyframeSet.ofFloat(values);
    }

    然后,mKeyframeSet = KeyframeSet.ofFloat(values),先来看KeyframeSet类的定义:

    [java] view plain copy
    1. /** 
    2.  * This class holds a collection of Keyframe objects and is called by ValueAnimator to calculate 
    3.  * values between those keyframes for a given animation. The class internal to the animation 
    4.  * package because it is an implementation detail of how Keyframes are stored and used. 
    5.  */  
    6.  //这个类维持着一个Keyframe 对象的集合,  
    7.  //ValueAnimator使用它来为一个动画计算那些 keyframe 之间的值 ... ...  
    8. class KeyframeSet { }  

    对KeyframeSet有一个大概了解之后。再来看一下Keyframe类的定义:

    [java] view plain copy
    1. /** 
    2.  * This class holds a time/value pair for an animation. The Keyframe class is used by {@link ValueAnimator} to define the values that 
    3.  *  the animation target will have over the course of the animation. As the time proceeds from one keyframe to the other, the value of 
    4.  *  the target object will animate between the value at the previous keyframe and the value at the next keyframe. Each keyframe also  
    5.  * holds an optional {@link TimeInterpolator} object, which defines the time interpolation over the intervalue preceding the keyframe. 
    6.  */  
    7.  //简单说。这就是个 对动画过程中的一帧的时间和属性的值进行封装的类  
    8. public abstract class Keyframe implements Cloneable { }  

    来看上文中处的KeyframeSet的ofFloat(values)方法:

    public static KeyframeSet ofFloat(float... values) {
            boolean badValue = false;//初始化一个标示。之后用于标示values[i]是不是一个数字
            int numKeyframes = values.length;//获取传入的參数的个数
            //初始化一个FloatKeyframe 类型的数组,数组的长度为numKeyframes和2之间的较大者,这个比較easy理解
            FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
            if (numKeyframes == 1) {
            //假设我们仅仅传入了一个參数,那么这个參数将用于构建keyframes[1]。keyframes[0]的值则由ofFloat(0f)来构建。例如以下:
                  keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
    // Constructs a Keyframe object with the given time. The value at this time will be derived from the target object when the animation first starts ... ...
    // 使用给定的time构造一个Keyframe 对象。在动画第一次运行时。这个给定的时间相应的value将利用动画的目标对象去获得 ... ...
    public static Keyframe ofFloat(float fraction) {
            //FloatKeyframe和IntKeyframe都是Keyframe 的静态内部类。这个和PropertyValuesHolder结构是类似的
            return new FloatKeyframe(fraction);
    FloatKeyframe(float fraction) {
     // 为成员变量float mFraction(定义在Keyframe 中)赋值, 注意,在此时。成员变量mValue的值还没有设置 
    mFraction = fraction;
     // 为成员变量Class mValueType(定义在Keyframe 中)赋值
     mValueType = float.class;
    }
    }       
                  keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
    /**
       * Constructs a Keyframe object with the given time and value. The time defines the time, as a proportion of an overall 
       * animation's duration, at which the value will hold true for the animation ... ...
     // 使用给定的time和value构造一个Keyframe 对象,time作为整个动画运行过程中的一个时间比例。是一个0到1之间的值 ... ... 
    public static Keyframe ofFloat(float fraction, float value) {
            return new FloatKeyframe(fraction, value);
    FloatKeyframe(float fraction, float value) {
                mFraction = fraction;
                mValue = value;
                mValueType = float.class;
                mHasValue = true;
    }
    }
              if (Float.isNaN(values[0])) {
                      badValue = true; // 假设传入的值不是一个数值,将标示badValue改为true
               }
    //从以上分析能够看到。当我们仅仅传入一个值时,将用1(代表时间终点)和这个值构建出动画运行的最后一帧的Keyframe
    // 对象。而动画的第一帧相应的Keyframe 对象,则默认由0(代表时间的起点)来构建,这一帧相应的值将在动画第一次
    //运行时由动画作用的对象来获得,假设我们传入的參数大于1个,比方2个或者多个。则运行下边的逻辑:
               } else {
    //逻辑比較简单,假设传入的參数大于1,则,用0f和values[0]构建出动画的第一帧相应的Keyframe对象并赋值给keyframes[0],
    //代表第一个value相应动画的起始值
                     keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
    // 然后。进行遍历,利用values[i] 和 其所相应的帧在整个动画运行过程中应该处于的时间比例—— 
    // (float) i / (numKeyframes - 1) 来构建每个Keyframe对象。一般我们传入的參数是两个。所以第二个參数就相应了
    // 动画运行的最后一帧的属性值。事实上,在这里,属性动画实现的原理已经開始有所体现了。
                     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;// 同上,假设传入的值不是一个数值,将标示badValue改为true
                             }
                     }
              }
              if (badValue) {
                      Log.w("Animator", "Bad value (NaN) in float animator");// 假设传入的值不是一个数值,运行此逻辑
               }
    //以上逻辑主要就是创建keyframes数组。该数组中放的是依据传入的value值创建出来的动画运行过程中的关键帧对象
    //即(主要是)将一个mKeyframes 成员变量完毕初始化的FloatKeyframeSet对象返回
              return new FloatKeyframeSet(keyframes);
    public FloatKeyframeSet(FloatKeyframe... keyframes) {
            // 走的是父类的构造函数
            super(keyframes);
    public KeyframeSet(Keyframe... keyframes) {
            //这个逻辑就比較简单了
            mNumKeyframes = keyframes.length;//为成员变量int mNumKeyframes()赋值
            mKeyframes = new ArrayList<Keyframe>();
            //将接收到的keyframes数组中的元素加入到成员变量ArrayList<Keyframe> mKeyframes集合中
            mKeyframes.addAll(Arrays.asList(keyframes));
            mFirstKeyframe = mKeyframes.get(0);// 初始化第一帧相应的成员变量Keyframe mFirstKeyframe
            mLastKeyframe = mKeyframes.get(mNumKeyframes - 1); // 初始化最后一帧相应的成员变量Keyframe mLastKeyframe
            // 初始化插值器相应的成员变量TimeInterpolator mInterpolator,眼下为null
            mInterpolator = mLastKeyframe.getInterpolator();
    private TimeInterpolator mInterpolator = null;
    public TimeInterpolator getInterpolator() {
            return mInterpolator;
    }
    }
    }
    }

    至此,KeyframeSet的ofFloat(values)方法完毕了,基本的逻辑是:

    依据传入的value值创建出动画运行过程中的关键帧对象,将这些对象放在一个数组中,new一个FloatKeyframeSet对象,然后将数组中的这些元素,放在FloatKeyframeSet对象的成员变量ArrayList<Keyframe> mKeyframes中,并将FloatKeyframeSet对象返回。

    上边第步将这个FloatKeyframeSet对象赋值给PropertyValuesHolder的成员变量KeyframeSet mKeyframeSet,于是。第步也运行完了,接着。在setFloatValues()方法中。运行完第步后:
    [java] view plain copy
    1. mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;  
    2. //将mKeyframeSet向下转型为FloatKeyframeSet赋值给FloatPropertyValuesHolder  
    3. //的成员变量FloatKeyframeSet mFloatKeyframeSet  

    于是。第步也运行完了,第步把完毕初始化的FloatPropertyValuesHolder对象返回,第步将利用第步返回的对象作为參数。运行setValues()方法。(该方法在ObjectAnimator的父类ValueAnimator类中定义),详细逻辑例如以下:

    [java] view plain copy
    1. public void setValues(PropertyValuesHolder... values) {  
    2.     int numValues = values.length;  
    3.     // 为PropertyValuesHolder[] mValues(在ObjectAnimator父类ValueAnimator中定义)赋值  
    4.     mValues = values;  
    5.     // 为成员变量HashMap<String, PropertyValuesHolder> mValuesMap赋值,  
    6.     //下面是关于mValuesMap的凝视:  
    7.     //A hashmap of the PropertyValuesHolder objects. This map is used to lookup   
    8.     //animated values by property name during calls to getAnimatedValue(String).  
    9.     //一个用于存储PropertyValuesHolder对象的集合。在调用getAnimatedValue(String)方法  
    10.     //时通过property name来查找values   
    11.     mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);  
    12.     for (int i = 0; i < numValues; ++i) {  
    13.         PropertyValuesHolder valuesHolder = values[i];  
    14.         mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);  
    15.     }  
    16.     // New property/values/target should cause re-initialization prior to starting  
    17.     mInitialized = false;  
    18. }  


    至此,用于创建属性动画对象的ObjectAnimator类的静态方法ofFloat的大体逻辑分析完毕了。

    简单总结一下:

    ObjectAnimator.ofFloat(Object target, String propertyName, float... values) 创建了一个ObjectAnimator对象,而且:
    1、将 target 赋给 ObjectAnimator 的成员变量 private Object mTarget
    2、将 propertyName 赋给 ObjectAnimator 的成员变量 private String mPropertyName
    3、创建一个 FloatPropertyValuesHolder 对象
    3.1、将 propertyName 赋给 FloatPropertyValuesHolder 的成员变量 String mPropertyName(在FloatPropertyValuesHolder 的父类 PropertyValuesHolder中定义
    3.2、将 float.class 赋给 FloatPropertyValuesHolder 的成员变量 Class mValueType(在FloatPropertyValuesHolder 的父类 PropertyValuesHolder中定义
    3.3、创建一个 FloatKeyframeSet对象
    3.3.1、创建一个 FloatKeyframe 类型的数组 keyframes 
    3.3.2、依据传入的 values 构造出 keyframes 数组中的每一项(关键帧对象Keyframe
    3.3.3、将 keyframes 数组中的每一项加入到 FloatKeyframeSet 对象的成员变量 
       ArrayList<Keyframe>mKeyframes(在FloatKeyframeSet 的父类KeyframeSet中定义)中
    3.4、将 FloatKeyframeSet 对象赋给 PropertyValuesHolder 的成员变量 KeyframeSet mKeyframeSet
    3.5、将 mKeyframeSet 向下转型为 FloatKeyframeSet 类型赋给 
    FloatPropertyValuesHolder 的成员变量 FloatKeyframeSet mFloatKeyframeSet
    4、将 FloatPropertyValuesHolder 对象 赋给ObjectAnimator 的成员变量 PropertyValuesHolder[] mValues
       (在ObjectAnimator 的父类ValueAnimator中定义
    5、利用已完毕初始化的FloatPropertyValuesHolder 对象及其mPropertyName属性。
         完毕成员变量HashMap<String, PropertyValuesHolder> mValuesMap(在ValueAnimator中定义)的初始化

    动画開始运行之前。另一个关键的方法 — setDuration(long)
    public ObjectAnimator setDuration(long duration) {
    // 运行的是父类 ValueAnimator 的setDuration()方法
    super.setDuration(duration);
    private static float sDurationScale = 1.0f;
    // How long the animation should last in ms 默认时间是300毫秒
    private long mDuration = (long)(300 * sDurationScale);
    private long mUnscaledDuration = 300;
    public ValueAnimator setDuration(long duration) {
            if (duration < 0) {
                // 若传入的值小于零,抛出异常
                   throw new IllegalArgumentException("Animators cannot have negative duration: " +  duration);
            }
            mUnscaledDuration = duration;
            mDuration = (long)(duration * sDurationScale);
            return this;
    }
            return this;
    }

    另外,在 setDuration( )方法中,我们能够看到 成员变量 mDuration 的值终于是由我们调用 setDuration( )方法时传入的 duration 乘以 sDurationScale 得出的,sDurationScale 默认值为 1.0f ; 而且 ValueAnimator 类中提供了静态方法 setDurationScale() 供我们使用
    public static void setDurationScale(float durationScale) {
            sDurationScale = durationScale;
    }
    我们能够利用这种方法改变动画的速度


    下边来看属性动画的运行过程 —— start( )方法

    从上文的分析,我们注意到,使用ObjectAnimator类的静态方法ofFloat来创建动画对象的过程中,ObjectAnimator类仅仅是复写了父类ValueAnimator的一部分方法,相同也仅仅拥有部分仅仅属于自己的成员变量,其实。我们在使用属性动画时。所涉及到的类的继承关系例如以下:
    [java] view plain copy
    1. /** 
    2.  * This is the superclass for classes which provide basic support for animations which can be 
    3.  * started, ended, and have <code>AnimatorListeners</code> added to them. 
    4.  */  
    5.  //这是那些为动画提供最基础的支持以让动画能被开启、结束和被添加监听的类的超类  
    6. public abstract class Animator implements Cloneable { }  
    Animator 是属性动画体系的超类,它定义了诸如 start()、cancel()、end()、setDuration(long duration)、setInterpolator(TimeInterpolator value)、isRunning()、addListener(AnimatorListener listener)、removeAllListeners() 等方法
    AnimatorSet 在属性动画中的使用方法和在补间动画中类似,不细讲了
    ValueAnimator 和 ObjectAnimator 是属性动画的实现类,它们的差别在哪里?分析完start( ) 方法,再结合上边的 ofFloat( ) 方法进行总结

    ObjectAnimator 的 start() 方法:
    [java] view plain copy
    1. public void start() {  
    2.     // See if any of the current active/pending animators need to be canceled  
    3.     AnimationHandler handler = sAnimationHandler.get();  
    4.     if (handler != null) { ... ... } // 第一次进入这里 handler 应该是 null  
    5.     super.start(); // 运行父类的start()方法  
    6. }  
    ValueAnimator 的 start() 方法:
    public void start() {
            start(false);
    }
    /**
         * Start the animation playing. This version of start() takes a boolean flag that indicates whether the animation should play in 
         * reverse. The flag is usually false, but may be set to true if called from the reverse() method.
         * <p>The animation started by calling this method will be run on the thread that called this method. This thread should have
         * a Looperon it (a runtime exception will be thrown if this is not the case). Also, if the animation will animate properties of 
         * objects in the view hierarchy, then the calling thread should be the UI thread for that view hierarchy.</p>
         */
         // 開始运行动画,这个start() 方法 有一个boolean型的參数用于标示该动画是否须要反转,该參数一般为false。可是也可能
        // 被设置为true假设start() 方法是从 reverse() 方法中调用的,该动画将执行在调用 start() 方法的线程里,这个线程须要拥有
         // 一个Looper 对象,否则会发生异常 ... ...
    private void start(boolean playBackwards) {
          if (Looper.myLooper() == null) {
                  throw new AndroidRuntimeException("Animators may only be run on Looper threads");
            }
            mPlayingBackwards = playBackwards; // 动画是否反转的标示
    // This variable tracks the current iteration that is playing. When mCurrentIteration exceeds the  repeatCount
    //  (if repeatCount!=INFINITE), the animation ends 这个变量用于记录当前动画的循环次数。当mCurrentIteration 超过了
    // repeatCount(假设repeatCount 不等于 -1),动画将被终止,该变量默认值为0
            mCurrentIteration = 0; 
            mPlayingState = STOPPED;//标示动画的状态,下面是ValueAnimator中对动画状态的定义:
    /**
         * Values used with internal variable mPlayingState to indicate the current state of an animation.
         */
         // 变量mPlayingState 使用这些值来标示动画的状态
        static final int STOPPED    = 0; // Not yet playing 尚未開始
        static final int RUNNING    = 1; // Playing normally 正常进行中
        static final int SEEKED     = 2; // Seeked to some time value
            mStarted = true; 
    // Tracks whether a startDelay'd animation has begun playing through the startDelay.
            mStartedDelay = false;
    // Whether this animator is currently in a paused state.
            mPaused = false;
            AnimationHandler animationHandler = getOrCreateAnimationHandler();
    private static AnimationHandler getOrCreateAnimationHandler() {
            AnimationHandler handler = sAnimationHandler.get();
            if (handler == null) {// 第一次运行,走下边的逻辑
                  handler = new AnimationHandler();
                  sAnimationHandler.set(handler);// 此处涉及ThreadLocal的使用,暂不细说
            }
            return handler;
    }
          // 将此动画对象加入到AnimationHandler的mPendingAnimations集合中
    animationHandler.mPendingAnimations.add(this);
            if (mStartDelay == 0) {
       // The amount of time in ms to delay starting the animation after start() is called 调用start()方法之后延迟多少时间播放动画
        private long mStartDelay = 0;// 默觉得零,运行下边的逻辑
              // This sets the initial value of the animation, prior to actually starting it running
                 setCurrentPlayTime(0);
                 mPlayingState = STOPPED;
                 mRunning = true;
                 notifyStartListeners();
            }
            animationHandler.start();
    }
    在ValueAnimator的start( )方法中,须要重点分析的就是setCurrentPlayTime(0)和animationHandler.start()这两个方法
    setCurrentPlayTime(0)方法:
    public void setCurrentPlayTime(long playTime) {
              initAnimation();
            long currentTime = AnimationUtils.currentAnimationTimeMillis();
            if (mPlayingState != RUNNING) {
                  mSeekTime = playTime;
                  mPlayingState = SEEKED;
            }
            mStartTime = currentTime - playTime;
              doAnimationFrame(currentTime);
    }
    上边处的initAnimation()方法:
    // ObjectAnimator 复写了父类的initAnimation()方法
    void initAnimation() {
            if (!mInitialized) { // 此时mInitialized的值为false,运行下边逻辑
                // mValueType may change due to setter/getter setup; do this before calling super.init(),
                // which uses mValueType to set up the default type evaluator.
                int numValues = mValues.length;
                for (int i = 0; i < numValues; ++i) { 
                       mValues[i].setupSetterAndGetter(mTarget);
                }
                 super.initAnimation();
            }
        }
    上边第步运行的是PropertyValuesHolder的setupSetterAndGetter()方法。来看详细逻辑:
    [java] view plain copy
    1. void setupSetterAndGetter(Object target) {  
    2.     if (mProperty != null) {  
    3.         // check to make sure that mProperty is on the class of target  
    4.         try {  
    5.             Object testValue = mProperty.get(target);  
    6.             for (Keyframe kf : mKeyframeSet.mKeyframes) {  
    7.                 if (!kf.hasValue()) {  
    8.                     kf.setValue(mProperty.get(target));  
    9.                 }  
    10.             }  
    11.             return;  
    12.         } catch (ClassCastException e) {  
    13.             Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +  
    14.                     ") on target object " + target + ". Trying reflection instead");  
    15.             mProperty = null;  
    16.         }  
    17.     }  
    18.     // 从上文中属性动画的创建的分析来看,此时的mProperty 为null,从下边開始运行  
    19.     Class targetClass = target.getClass();  
    20.     if (mSetter == null) {  
    21.         // PropertyValuesHolder 的成员变量 Method mSetter,默觉得null  
    22.         setupSetter(targetClass);  
    23.     }  
    24.     for (Keyframe kf : mKeyframeSet.mKeyframes) {  
    25.         if (!kf.hasValue()) {  
    26.             if (mGetter == null) {  
    27.                 setupGetter(targetClass);  
    28.                 if (mGetter == null) {  
    29.                     // PropertyValuesHolder 的成员变量 private Method mGetter,默觉得null  
    30.                     // Already logged the error - just return to avoid NPE  
    31.                     return;  
    32.                 }  
    33.             }  
    34.             try {  
    35.                 kf.setValue(mGetter.invoke(target));  
    36.             } catch (InvocationTargetException e) {  
    37.                 Log.e("PropertyValuesHolder", e.toString());  
    38.             } catch (IllegalAccessException e) {  
    39.                 Log.e("PropertyValuesHolder", e.toString());  
    40.             }  
    41.         }  
    42.     }  
    43. }  
    setupSetterAndGetter()方法中,重点分析setupSetter(targetClass)、setupGetter(targetClass)以及kf.setValue(mGetter.invoke(target))方法
    setupSetter(targetClass)方法
    [java] view plain copy
    1. void setupSetter(Class targetClass) {  
    2.     // 能够看到,该方法的主要作用就是给PropertyValuesHolder 的成员变量 Method mSetter 赋值  
    3.     mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType);  
    4. }  
    setupGetter(Class targetClass)方法
    [java] view plain copy
    1. private void setupGetter(Class targetClass) {  
    2.     // 能够看到,该方法的主要作用就是给 PropertyValuesHolder 的成员变量 Method mGetter 赋值  
    3.     mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get"null);  
    4. }  
    setupSetter(targetClass) 和 setupGetter(targetClass) 方法都调用了setupSetterOrGetter 方法,仅仅是參数有所不同,第一个和第三个好理解,第二个參数是一个集合,其定义例如以下:
    [java] view plain copy
    1. //These maps hold all property entries for a particular class. This map is used to speed up property/setter/getter lookups for a given   
    2. //class/property combination. No need to use reflection on the combination more than once. 也比較简单明了  
    3. private static final HashMap<Class, HashMap<String, Method>> sSetterPropertyMap =  
    4.          new HashMap<Class, HashMap<String, Method>>();  
    5. private static final HashMap<Class, HashMap<String, Method>> sGetterPropertyMap =  
    6.          new HashMap<Class, HashMap<String, Method>>();   
    第四个參数。对于setupSetter()方法来讲,传入的是mValueType(我们在创建动画对象时已为其赋值),而对于setupGetter()方法来讲,传入的是null,来看setupSetterOrGetter 方法的主要逻辑:
    [java] view plain copy
    1. /** 
    2.  * Returns the setter or getter requested. This utility function checks whether the requested method exists in the propertyMapMap 
    3.  * cache. If not, it calls another utility function to request the Method from the targetClass directly. 
    4.  */  
    5.  // 首先看凝视。检查propertyMapMap集合是否有所请求的 setter or getter 方法。假设有。返回,假设没有。通过targetClass  
    6.  // 获得他们。将它们缓存起来并返回  
    7. private Method setupSetterOrGetter(Class targetClass,  
    8.         HashMap<Class, HashMap<String, Method>> propertyMapMap,  
    9.         String prefix, Class valueType) {  
    10.     Method setterOrGetter = null// 定义暂时变量  
    11.     try {  
    12.         // Have to lock property map prior to reading it, to guard against  
    13.         // another thread putting something in there after we've checked it  
    14.         // but before we've added an entry to it  
    15.         mPropertyMapLock.writeLock().lock();  
    16.         HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);  
    17.         if (propertyMap != null) {// 第一次运行。此处propertyMap 为 null  
    18.             setterOrGetter = propertyMap.get(mPropertyName);  
    19.         }  
    20.         if (setterOrGetter == null) {  
    21.             // Determine the setter or getter function using the JavaBeans convention of setFoo or getFoo for a property   
    22.             // named 'foo'. This function figures out what the name of the function should be and uses reflection to find the Method   
    23.             // with that name on the target object.利用反射获取set和get方法  
    24.             setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);  
    25.             if (propertyMap == null) {  
    26.                 propertyMap = new HashMap<String, Method>();  
    27.                 propertyMapMap.put(targetClass, propertyMap);  
    28.             }  
    29.             // 将获得的方法缓存起来  
    30.             propertyMap.put(mPropertyName, setterOrGetter);  
    31.         }  
    32.     } finally {  
    33.         mPropertyMapLock.writeLock().unlock();  
    34.     }  
    35.     return setterOrGetter;  
    36. }  
    setupSetterAndGetter()方法中,setupSetter(targetClass)、setupGetter(targetClass)方法大致分析完了,它完毕了对mSetter和mGetter的初始化,接下来,对KeyframeSet的成员变量ArrayList<Keyframe> mKeyframes(上文分析过,在属性动画的对象创建时,就以完毕对mKeyframes的初始化。mKeyframes里边放的是依据传入的value构造出的动画运行过程中的帧对象)进行遍历。详细逻辑是:
    for (Keyframe kf : mKeyframeSet.mKeyframes) {
                if (!kf.hasValue()) { // hasValue()方法定义例如以下:
    public boolean hasValue() {
            return mHasValue; // Keyframe的成员变量。boolean mHasValue ,默认是 false
    // 上文我们在讲动画创建过程中依据传入的 values 构造出 keyframes 数组中的每一项(关键帧对象Keyframe)时,已经讲过,
    // 假设我们仅仅传入了一个參数,那么这个參数将用于构建keyframes[1],走下边的第一个构造函数
    // keyframes[0]的值则由ofFloat(0f)来构建,走下边的第二个构造函数,即此关键帧对象的mHasValue为默认值false
    FloatKeyframe(float fraction, float value) {
                mFraction = fraction;
                mValue = value;
                mValueType = float.class;
                mHasValue = true; // 标示一个帧对象是否已有value值
    }
    FloatKeyframe(float fraction) {
                mFraction = fraction;
                mValueType = float.class;
    }
    }
                    if (mGetter == null) {
                            setupGetter(targetClass); // 上文已分析过了
                            if (mGetter == null) {
                                    // Already logged the error - just return to avoid NPE
                                    return;
                            }
                     }
                    try {
                            kf.setValue(mGetter.invoke(target));
    // 该方法利用通过反射获得的get方法为mKeyframes集合中还没有value值的帧对象赋值
    // 上文中,讲到Keyframe 对象的创建时。构造函数 public static Keyframe ofFloat(float fraction)的凝视为:
    // Constructs a Keyframe object with the given time. The value at this time will be derived from the target object when 
    // the animation first starts ... ... 指的就是这个地方
    public void setValue(Object value) {
                if (value != null && value.getClass() == Float.class) {
                    mValue = ((Float)value).floatValue();
                    mHasValue = true;
                }
    }
                     } catch (InvocationTargetException e) {
                            Log.e("PropertyValuesHolder", e.toString());
                     } catch (IllegalAccessException e) {
                            Log.e("PropertyValuesHolder", e.toString());
                     }
              }
    }

    至此,上边第步运行完了,它主要是对动画对象的成员变量PropertyValuesHolder[] mValues做更进一步的初始化,接下来运行上文中的第步,父类ValueAnimator中定义的initAnimation()方法
    /**
         * This function is called immediately before processing the first animation frame of an animation. If there is a nonzero
         *  <code>startDelay</code>, the function is called after that delay ends.It takes care of the final initialization steps for the
         * animation. ...  ...
         */
         // 这种方法在运行动画的第一帧之前被调用。假设有一个不为零的startDelay值,该方法将在对应的延迟时间执后被运行
         // 这是一个动画最后的初始化步骤 ... ...
    void initAnimation() {
            if (!mInitialized) {
                   int numValues = mValues.length;
                   for (int i = 0; i < numValues; ++i) {
                         mValues[i].init();
    /**
         * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used to calculate animated values. 
         */
         // 逻辑非常easy。就是依据mValueType的值设置成员变量TypeEvaluator mEvaluator 的值,用来calculate animated values
    void init() {
    if (mEvaluator == null) {
                   // We already handle int and float automatically, but not their Object
                   // equivalents
                   mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
                        (mValueType == Float.class) ?

    sFloatEvaluator :

                        null;
            }
            if (mEvaluator != null) {
                     // KeyframeSet knows how to evaluate the common types - only give it a custom
                     // evaluator if one has been set on this class
                     mKeyframeSet.setEvaluator(mEvaluator);
             }
    }
                   }
                // 如今,动画最后的初始化已经完毕,就将 mInitialized 的值设为 true 了
                   mInitialized = true;
            }
    }

    到如今。上文中第步也完毕了,我们能够看到,这一步是在做进一步的初始化,当中对set和get方法的初始化和为没有value值得帧对象赋值的操作是在ObjectAnimator中完毕的。而对用来计算动画的value值的TypeEvaluator的初始化则是在ValueAnimator中完毕的

    稍后再来分析上文中第步的doAnimationFrame(currentTime)方法,因此,在ValueAnimator的start( )方法中,须要重点分析的两个方法之中的一个setCurrentPlayTime(0)就到此为止。接着看后边的animationHandler.start():
    animationHandler.start()方法终于会导致AnimationHandler的run方法的运行(此处细节省略):
    [java] view plain copy
    1. public void run() {  
    2.     mAnimationScheduled = false;  
    3.     // 这是AnimationHandler类中的doAnimationFrame()方法  
    4.     doAnimationFrame(mChoreographer.getFrameTime());  
    5. }  
    doAnimationFrame( )方法:
    private void doAnimationFrame(long frameTime) {
                // mPendingAnimations holds any animations that have requested to be started
                // We're going to clear mPendingAnimations, but starting animation may
                // cause more to be added to the pending list (for example, if one animation
                // starting triggers another starting). So we loop until mPendingAnimations is empty.
                while (mPendingAnimations.size() > 0) {
                       ArrayList<ValueAnimator> pendingCopy = (ArrayList<ValueAnimator>) mPendingAnimations.clone();                 
                       mPendingAnimations.clear();
                       int count = pendingCopy.size();
                       for (int i = 0; i < count; ++i) {
                             ValueAnimator anim = pendingCopy.get(i);
                             // If the animation has a startDelay, place it on the delayed list
                             if (anim.mStartDelay == 0) {
                                   anim.startAnimation(this);
           // 事实上。上述代码最基本的就是运行了一句 handler.mAnimations.add(this);
                              } else {
                                    mDelayedAnims.add(anim);
                              }
                       }
                }
                // Next, process animations currently sitting on the delayed queue, adding them to the active animations if they are ready 
                int numDelayedAnims = mDelayedAnims.size();
                for (int i = 0; i < numDelayedAnims; ++i) {
                       ValueAnimator anim = mDelayedAnims.get(i);
                       if (anim.delayedAnimationFrame(frameTime)) {
                             mReadyAnims.add(anim);
                       }
                }
                int numReadyAnims = mReadyAnims.size();
                if (numReadyAnims > 0) {
                       for (int i = 0; i < numReadyAnims; ++i) {
                             ValueAnimator anim = mReadyAnims.get(i);
                             anim.startAnimation(this);
                             anim.mRunning = true;
                             mDelayedAnims.remove(anim);
                       }
                       mReadyAnims.clear();
                }
                //Now process all active animations. The return value from animationFrame() tells the handler whether it should now be ended
                int numAnims = mAnimations.size();
                for (int i = 0; i < numAnims; ++i) {
                       mTmpAnimations.add(mAnimations.get(i));
                }
                for (int i = 0; i < numAnims; ++i) {
                       ValueAnimator anim = mTmpAnimations.get(i);
                       if (mAnimations.contains(anim) &&    anim.doAnimationFrame(frameTime)) {
                             mEndingAnims.add(anim);
                       }
                }
                mTmpAnimations.clear();
                if (mEndingAnims.size() > 0) {
                       for (int i = 0; i < mEndingAnims.size(); ++i) {
                             mEndingAnims.get(i).endAnimation(this);
                       }
                       mEndingAnims.clear();
                }
                // If there are still active or delayed animations, schedule a future call to onAnimate to process the next frame of the animations.
                if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
                    scheduleAnimation();
                }
    }
    能够看到。在AnimationHandler类中。有下面几个集合:
    [java] view plain copy
    1. //The per-thread list of all active animations  
    2. protected final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>();  
    3. //Used in doAnimationFrame() to avoid concurrent modifications of mAnimations  
    4. private final ArrayList<ValueAnimator> mTmpAnimations = new ArrayList<ValueAnimator>();  
    5. //The per-thread set of animations to be started on the next animation frame  
    6. protected final ArrayList<ValueAnimator> mPendingAnimations = new ArrayList<ValueAnimator>();  
    7. protected final ArrayList<ValueAnimator> mDelayedAnims = new ArrayList<ValueAnimator>();  
    8. private final ArrayList<ValueAnimator> mEndingAnims = new ArrayList<ValueAnimator>();  
    9. private final ArrayList<ValueAnimator> mReadyAnims = new ArrayList<ValueAnimator>();  
    在AnimationHandler类的doAnimationFrame( )方法中,会依据动画的属性值的变化。用这些集合来管理动画对象,而且在这个过程中。会调用到最核心的ValueAnimator类的doAnimationFrame()方法(第  步)当mAnimations.contains(anim)而且doAnimationFrame()方法的返回值为true时。就会运行mEndingAnims.add(anim);将动画对象加入到mEndingAnims集合中,接着,遍历mEndingAnims集合,运行 mEndingAnims.get(i).endAnimation(this);主要是将mAnimations、mPendingAnimations、mDelayedAnims集合中的对象清空以及改变一些标示。标示着动画的结束。
    假设doAnimationFrame()方法的返回值为false。
    则在满足条件(!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) 时。运行scheduleAnimation(),即相当于调用animationHandler.start()继续循环。


    那么doAnimationFrame()方法的逻辑是什么?
    又回到了在上文中。当时我们临时放下没有分析的第步中的doAnimationFrame(currentTime)方法上:
    [java] view plain copy
    1. final boolean doAnimationFrame(long frameTime) {  
    2.     if (mPlayingState == STOPPED) {   }  
    3.     if (mPaused) {  }  
    4.     else if (mResumed) {     }  
    5.     // 主要是下边的代码  
    6.     final long currentTime = Math.max(frameTime, mStartTime);  
    7.     return animationFrame(currentTime);  
    8. }  
    接着是animationFrame()方法:
    boolean animationFrame(long currentTime) {
            boolean done = false;
            switch (mPlayingState) {
                 case RUNNING:
                 case SEEKED:
                       float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
                            if (fraction >= 1f) {
                                  if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
                              // Time to repeat
                              if (mListeners != null) {
                                 int numListeners = mListeners.size();
                                for (int i = 0; i < numListeners; ++i) {
                                        mListeners.get(i).onAnimationRepeat(this);
                                }
                                 }
                                  if (mRepeatMode == REVERSE) {
                                 mPlayingBackwards = !mPlayingBackwards;
                                }
                              mCurrentIteration += (int)fraction;
                                fraction = fraction % 1f;
                               mStartTime += mDuration;
                                 } else {
                                done = true;
                                 fraction = Math.min(fraction, 1.0f);
                               }
                            }
                            if (mPlayingBackwards) {
                             fraction = 1f - fraction;
                    }
                  animateValue(fraction);
                  break;
             }
             return done;
    }
    我们看到。在每一次调用该方法时。都会依据动画对象的一些和时间相关的属性的值来计算fraction的值,来推断要返回true还是false。
    从代码中。能够看出,fraction代表的就是动画运行过程中的每一帧在整个动画运行过程中所处的时间的比率。


    分析到此。整个属性动画的实现原理基本清楚了,还剩最后一点 ——
    每一次调用animationFrame方法时,怎么利用计算出来的fraction来改变动画作用对象的属性值以达到动画的效果?答案是上文中处的animateValue(fraction)方法,须要注意的是,ObjectAnimator类重写了父类的animateValue(fraction)方法,来看详细逻辑:
    void animateValue(float fraction) {
            super.animateValue(fraction); // 首先调用父类的方法
            int numValues = mValues.length;
            for (int i = 0; i < numValues; ++i) {
                mValues[i].setAnimatedValue(mTarget);
    void setAnimatedValue(Object target) {
            if (mProperty != null) {
                mProperty.set(target, getAnimatedValue());
            }
            if (mSetter != null) {
                try {
                    mTmpValueArray[0] = getAnimatedValue();
                    mSetter.invoke(target, mTmpValueArray);
                } catch (InvocationTargetException e) {
                    Log.e("PropertyValuesHolder", e.toString());
                } catch (IllegalAccessException e) {
                    Log.e("PropertyValuesHolder", e.toString());
                }
            }
    }
            }
    }
    父类ValueAnimator的animateValue(fraction)方法:
    void animateValue(float fraction) {
      // 此时。我们在文章的开头提到的插值器登场了
            fraction = mInterpolator.getInterpolation(fraction);
    private TimeInterpolator mInterpolator sDefaultInterpolator;
    private static final TimeInterpolator sDefaultInterpolator = new AccelerateDecelerateInterpolator();
     // 能够看到,假设不进行设置的话,默认的插值器就是 AccelerateDecelerateInterpolator          
            mCurrentFraction = fraction;
            int numValues = mValues.length;
            for (int i = 0; i < numValues; ++i) {
           // 细节省略了
                   mValues[i].calculateValue(fraction);
            }
            if (mUpdateListeners != null) {
                   int numListeners = mUpdateListeners.size();
                   for (int i = 0; i < numListeners; ++i) {
                         mUpdateListeners.get(i).onAnimationUpdate(this);
                   }
            }
    }


    至此,属性动画实现原理基本清楚了。

  • 相关阅读:
    POX flow_stats2.py analysis by uestc_gremount
    C#基础知识点
    C++学了这么多年,你仍不知道的事
    VC7 HTML Dialog开发实例讲解
    VC与JavaScript交互(四) --- WebBrowser或CHtmlView中轻松屏蔽脚本错误(JavaScript)
    VC与JavaScript交互(三) --- CWebPage类调用javascript函数(给js函数传参,并取得返回值)
    VC与JavaScript交互(二) --- 调用JS函数
    VC与JavaScript交互(一) --- 如何实现
    动态链接库(VC_Win32)
    wcscpy wcscpy_s strcpy strcpy_s的区别
  • 原文地址:https://www.cnblogs.com/gccbuaa/p/7295246.html
Copyright © 2011-2022 走看看