nineoldandroids 详解

NineOldAndroids是一个兼容低版本Android的动画库,使得API 11以下的系统也能使用属性动画。其原理是根据系统版本选择不同实现,API 11及以上通过反射调用属性的set方法,低版本通过Matrix实现。本文从简介、基本原理和源码分析三个方面进行了深入讲解。
摘要由CSDN通过智能技术生成

简介

做Android开发的同学很多都知道或者使用过一个动画库,那就是NineOldAndroids,它的作者及其牛X,好几个著名的开源库都是他的作品,具体大家可以看他的 JakeWharton 。简单来说,NineOldAndroids是一个向下兼容的动画库,主要是使低于API 11的系统也能够使用View的属性动画。以下是个其官网的简述 : 

Android library for using the Honeycomb (Android 3.0) animation API on all versions of the platform back to 1.0!Animation prior to Honeycomb was very limited in what it could accomplish so in Android 3.x a new API was written. With only a change in imports, we are able to use a large subset of the new-style animation with exactly the same API.

View的属性动画在Android API 11及其以后才支持,该库的作用就是让API 11以下的系统也能够正常的使用属性动画。它的类名、用法与官方的都一样,只是包名不一样。使用该库,你就可以在API 版本很低的情况下也能够使用各种属性动画,让你的应用更加有动感、平滑。 官方地址 :  nineoldandroids 。

基本原理简介

一般来说,我们使用NineOldAndroids的属性动画时的代码大致是如下这样的:

ValueAnimator colorAnim = ObjectAnimator.ofFloat(myView, "scaleX", 0.3f);
colorAnim.setDuration(1000);
colorAnim.start();
这个动画会将myView (View的子类型)的宽度在1秒钟之内缩放到原始宽度的30%。下面我们先来简单说明一下NineOldAndroids的基本原理。

不管是官方的支持,还是nideoldandroids的低版本支持,它们使用的技术原理都是一样的。NineOldAndroids的基本原理就是在构建属性动画时根据用户的系统版本来选择不同的实现方式,并且对于低版本的API使用自己的实现来做属性动画。如果用户的系统API大于等于11,即Android 3.0及其以上,那么就会在动画的duration期间内连续地通过反射来调用该属性的set方法来修改它的值。例如上面的 scaleX属性,该动画库会在内部构造 scaleX 的set方法,格式如下为set + 首字母大写属性名 + 参数,例如setS caleX (float scale),这样在一段时间内连续的修改myView的缩放值就达到了动画的效果。我们看setScaleX的文档:

如果用户的系统低于API 11,那么就不会调用View的set属性方法来修改它的属性,而是通过矩阵(Matrix)来修改View的缩放、平移、旋转等动画。关于矩阵的一些支持请参考相关的资料。 Android中图像变换Matrix的原理、代码验证和应用(一) ,Android Matrix理论与应用详解 , Android--Matrix图片变换处理 。

基本原理了解以后我们就来看看它的实现吧。

源码分析

首先我们从它的入口,即ObjectAnimator入手,以上文中的scaleX为例来分析吧。

/**
  * 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).
  *
  * @param target The object whose property is to be animated. This object should
  * have a public method on it called <code>setName()</code>, where <code>name</code> is
  * the value of the <code>propertyName</code> parameter.
  * @param propertyName The name of the property being animated.
  * @param values A set of values that the animation will animate between over time.
  * @return An ObjectAnimator object that is set up to animate between the given values.
  */
    public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
    	// 1、构建属性动画对象, target为你要执行的动画的view,必须为View的子类, propertyName你要修改的属性名称.
    	// values是一个可变参数,如果是一个参数,那么该参数为目标值;如果是两个参数那么一个是起始值,一个是目标值;如果多余两个
    	// 参数,那么是起始值、动画要经过的几个值、目标值。
     ObjectAnimator anim = new ObjectAnimator(target, propertyName);
     // 2、设置属性值
     anim.setFloatValues(values);
     Log.d(propertyName, "### 属性名称 : " + propertyName) ;
     return anim;
    }


  // 3、设置属性的目标值值    
     @Override
    public void setFloatValues(float... values) {
    	// mValues是动画的各个数值的集合,The property/value sets being animated,声明如下 : PropertyValuesHolder[] mValues;
     if (mValues == null || mValues.length == 0) {
      // No values yet - this animator is being constructed piecemeal. Init the values with
      // whatever the current propertyName is
      if (mProperty != null) {
       setValues(PropertyValuesHolder.ofFloat(mProperty, values));
      } else {
       setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
      }
     } else {
      super.setFloatValues(values);
     }
    }
首先通过ofFloat这个工厂方法来创建属性动画,设置它的属性名,然后调用setFloatValues来设置它的目标值。我们看到setFloatValues出现了一个类PropertyValuesHolder,这个类是该动画库的一个核心类之一。它的作用就是保存属性的名称和它的setter, getter方法,以及它的目标值。我们看看它的一些相关函数吧。
/**
 * This class holds information about a property and the values that that property
 * should take on during an animation. PropertyValuesHolder objects can be used to create
 * animations with ValueAnimator or ObjectAnimator that operate on several different properties
 * in parallel.
 */    
public class PropertyValuesHolder implements Cloneable {
    

    /**
     * The name of the property associated with the values. This need not be a real property,
     * unless this object is being used with ObjectAnimator. But this is the name by which
     * aniamted values are looked up with getAnimatedValue(String) in ValueAnimator.
     * 属性名称
     */
    String mPropertyName;

    /**
     * 属性
     */
    protected Property mProperty;

    /**
     * The setter function, if needed. ObjectAnimator hands off this functionality to
     * PropertyValuesHolder, since it holds all of the per-property information. This
     * property is automatically
     * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
     * 属性的setter方法
     */
    Method mSetter = null;

    /**
     * The getter function, if needed. ObjectAnimator hands off this functionality to
     * PropertyValuesHolder, since it holds all of the per-property information. This
     * property is automatically
     * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
     * The getter is only derived and used if one of the values is null. 属性的getter方法
     */
    private Method mGetter = null;

    /**
     * The type of values supplied. This information is used both in deriving the setter/getter
     * functions and in deriving the type of TypeEvaluator.   属性的类ing,比如float, int等。
     */
    Class mValueType;

    /**
     * The set of keyframes (time/value pairs) that define this animation.
     * 这里是动画关键帧的即可,即在duration时间内的动画帧集合,它保存的是在每个时刻该该属性对应的值。
     */
    KeyframeSet mKeyframeSet = null;


    /**
     * Constructs and returns a PropertyValuesHolder with a given property and
     * set of float values.
     * @param property The property being animated. Should not be null.
     * @param values The values that the property will animate between.
     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
     */
    public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) {
        // 1、构建的是FloatPropertyValuesHolder
        return new FloatPropertyValuesHolder(property, values);
    }



    // 内部类, Float类型的PropertyValuesHolder
    static class FloatPropertyValuesHolder extends PropertyValuesHolder {
    

        // Cache JNI functions to avoid looking them up twice
        //private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
        //        new HashMap<Class, HashMap<String, Integer>>();
        //int mJniSetter;
        private FloatProperty mFloatProperty;// float型属性

        FloatKeyframeSet mFloatKeyframeSet;// 动画的关键帧,这个是重点
        float mFloatAnimatedValue;

        public FloatPropertyValuesHolder(String propertyName, FloatKeyframeSet keyframeSet) {
            super(propertyName);
            mValueType = float.class;
            mKeyframeSet = keyframeSet;
            mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
        }

        public FloatPropertyValuesHolder(Property property, FloatKeyframeSet keyframeSet) {
            super(property);
            mValueType = float.class;
            mKeyframeSet = keyframeSet;
            mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
            if (property instanceof FloatProperty) {
                mFloatProperty = (FloatProperty) mProperty;
            }
        }

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

        // 2、构造函数
        public FloatPropertyValuesHolder(Property property, float... values) {
            super(property);
            // 设置目标属性值
            setFloatValues(values);
            if (property instanceof  FloatProperty) {
                mFloatProperty = (FloatProperty) mProperty;
            }
        }

        // 3、设置动画的目标值
        @Override
        public void setFloatValues(float... values) {
            // 4、调用了父类的方法
            super.setFloatValues(values);
            mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;// 5、获取动画关键帧
        }

        // 计算当前的动画值
        @Override
        void calculateValue(float fraction) {
            mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction);
        }

        // 代码省略
        }

}

该类是属性和属性值的辅助类,它保存了属性的名称、setter、getter、以及该属性在duration时间段内各个时刻对应属性数值( mKeyframeSet )。这样,当执行动画时,动画库只需要根据动画的执行时间,然后到mKeyframeSet中查询这个时刻对应的属性值,然后修改View的这个属性,连续的这样操作即可达到动画的效果。在这个例子中,我们的属性是scaleX,目标属性值是0.3f。因此,对应的属性类为FloatPropertyValuesHolder,还有一种是IntPropertyValuesHolder,这都很好理解,就不多说。

这个例子中,我们会调用注释2中的构造函数,然后该函数会调用setFloatValues来设置动画的目标值,然后会到达注释3处的函数。在这个函数中调用了父类中对应的方法,然后就获取到了动画的关键帧。那么很可能计算各个时刻的属性值的操作放在了父类 (即 PropertyValuesHolder ) 的setFloatValues函数中。我们一起来看看吧。

/**
  * Set the animated values for this object to this set of floats.
  * If there is only one value, it is assumed to be the end value of an animation,
  * and an initial value will be derived, if possible, by calling a getter function
  * on the object. Also, if any value is null, the value will be filled in when the animation
  * starts in the same way. This mechanism of automatically getting null values only works
  * if the PropertyValuesHolder object is used in conjunction
  * {
    @link ObjectAnimator}, and with a getter function
  * derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
  * no way of determining what the value should be.
  *
  * @param values One or more values that the animation will animate between.
  */
    public void setFloatValues(float... values) {
     mValueType = float.class;
     mKeyframeSet = KeyframeSet.ofFloat(values);
    }
可以看到该函数又调用了KeyFrameSet的ofFloat方法,继续看 : 
class KeyframeSet {

  int mNumKeyframes;

  Keyframe mFirstKeyframe;
  Keyframe mLastKeyframe;
  /*Time*/Interpolator mInterpolator; // only used in the 2-keyframe case
  ArrayList<Keyframe> mKeyframes; // only used when there are not 2 keyframes
  TypeEvaluator mEvaluator;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值