差不多的开发者都应该知道的Android提供的三种属性动画:

  • View Animation
  • Drawable Animation
  • Property Animation

但是在Android系统不断更新完善的过程中,他们添加了很多低版本所没有的属性动画,为了兼容这些低版本的动画,他们创建了一个兼容库,NineOldAnimations。我们就拿NineOldAnimations兼容库来看看属性动画:

属性动画的总体设计:

Animation 通过PropertyValuesHolder 来更新对象的目标属性,如果用户没有设定目标属性的Property对象,那么会通过反射的形式调用目标属性的setter方法来更新属性值;否则,通过Property的set方法来设置属性值,这个属性值则通过KeyFrameSet的计算得到,而KeyFrameSet又是通过时间插值器和类型估值器来计算的,在动画执行的过程中不断计算当前时刻目标属性的值,然后 更新属性值来达到动画效果。

属性动画的核心类介绍:

了解核心类的名称和作用

  • ValueAnimation:该类是一个Animation的子类,实现了动画的整个处理逻辑,也是属性动画最核心的类
  • ObjectAnimation:对象属性动画的操作类,继承ValueAnimation,通过该类使用动画的形式操作对象的属性
  • TimeInterpolator:时间插值器,根据时间流逝的百分比来计算当前属性值改变的百分比,系统预置的有线性插值器(LinearInterpolator),加速减速插值器(AccelerateDecelerateInterpolator)和减速插值器(DecelerateInterpolator)等。
  • TypeEvaluator:类型估值器。根据当前属性改变的百分比来计算改变后的属性值,系统预装的有针对整数属性(IntEvaluator),针对浮点属于(FloatEvaluator)和针对Color属性(ArgbEvaluator)
  • Property:属性对象,定义了属性的set和get方法
  • PropertyValuesHolder:持有目标属性Property、setter和getter方法还有关键帧集合的类。
  • KeyFrameSet:存储一个动画的关键帧集合
  • AnimationProxy:在Android 3.0以下是使用View的属性动画的辅助类

流程图:

  • ValueAnimation流程
  1. 开始
  2. 设置动画执行时间,目标对象,属性值
  3. 启动动画
  4. 判断是否延迟执行,否的话跳过5、6两步
  5. 将该动画放到等待队列
  6. 通过handler发送一个延迟消息来延后执行
  7. 执行动画
  8. 根据时间插值器和估值器计算当前属性的值
  9. 判断是否设置了Property,否的话跳过第10步,是的话跳过第11步
  10. 通过Property的set方法更新属性值
  11. 通过反射调用属性的setter方法更新属性值
  12. 判断东动画是否结束,是的话结束,否的话执行第8步。
  • ObJectAnimation流程
  1. 开始
  2. 设置动画执行时间,目标对象,属性值
  3. 启动动画
  4. 判断动画是否延迟启动,否的话跳过第5、6两步
  5. 将该动画加入等待队列
  6. 通过Handler方一个延迟消息来延后执行
  7. 执行动画
  8. 根据时间插值器和估值器计算当前时间属性的值
  9. 判断系统的版本是否小于11,是的话跳过10、否的话跳过11
  10. 通过Android 3.0 以后的setter方法实现动画方法
  11. 通过matrix实现动画方法。
  12. 判断动画是否结束,是的话动画结束,否的话指定第8步

核心原理分析:

我们看下面这行代码,实现一个非常简单的功能,一个View从x轴缩放到原来的0.3倍,动画执行时间为1秒钟:

   public void showSimpleAnimation(View view){        ValueAnimator colorAnimation = ObjectAnimator.ofFloat(view,"scaleX",0.3f);        colorAnimation.setDuration(1000) ;        colorAnimation.start();    }

要看他们的原理我们首先要从ObjectAnimator入手,我们看下ObjectAnimator的ofFloat函数:

    public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {        ObjectAnimator anim = new ObjectAnimator(target, propertyName);        anim.setFloatValues(values);        return anim;    }

看了代码知道在ObjectAnimator中函数会首先构造一个ObjectAnimator对象,然后根据设置的属性值来初始化各个时间段对应的属性值,这个属性值就是函数里面的values参数,可以看出来values是一个可变参数,如果是一个参数,那么该参数为目标值;如果是两个参数,那么一个是其实值,一个是目标值。我们看下函数setFloatValues的实现:

       @Override    public void setFloatValues(float... values) {        //   PropertyValuesHolder[] mValues;        //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);        }    }

我们首先看到mValues的对象是ObjectAnimator父类ValueAnimator的一个PropertyValuesHolder对象,这个类是该动画库的一个核心类之一。他的作用就是保存属性的名称和setter'和getter方法,以及他的目标值。我们看下这个类的定义:

public class PropertyValuesHolder implements Cloneable {   //属性名称    String mPropertyName;   //属性对象    protected Property mProperty;    //属性setter方法    Method mSetter = null;   //属性getter方法    private Method mGetter = null;    //属性值的类型:float,int等    Class mValueType;   //动画关键帧的即可,即在duration时间内的动画帧集合,它保存的是在每个时刻该属性的对应值    KeyframeSet mKeyframeSet = null;    //代码省略    public static PropertyValuesHolder ofFloat(String propertyName, float... values) {        //构造的是FloatPropertyValuesHolder对象        return new FloatPropertyValuesHolder(propertyName, values);    }   //代码省略    //内部类,Float类型的PropertyValuesHolder对象    static class FloatPropertyValuesHolder extends PropertyValuesHolder {       //        private FloatProperty mFloatProperty; //float类型属性        FloatKeyframeSet mFloatKeyframeSet; //动画的关键帧        float mFloatAnimatedValue;      //代码省略        public FloatPropertyValuesHolder(Property property, float... values) {            super(property);            //设置目标属性值            setFloatValues(values);            if (property instanceof  FloatProperty) {                mFloatProperty = (FloatProperty) mProperty;            }        }        //设置动画的目标值        @Override        public void setFloatValues(float... values) {            //调用父类方法            super.setFloatValues(values);            //获取动画的关键帧            mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;        }        //计算当前的动画值        @Override        void calculateValue(float fraction) {            mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction);        }       //代码省略}

可以看出这个类是属性和属性值的辅助类,它保存了属性的名称,setter,getter,以及属性在duration时间段内各个时刻对应的属性数值(mKeyframeSet)。动画执行时,动画库只需要根据动画的执行时间,就可以在mKeyframeSet查找到对应的属性值,然后修改执行动画对象的目标属性值,然后连续执行这个过程就可以达到这个动画的效果。上面我们给的例子是属性是scaleX,目标属性值是0.3f。因此,这个例子对应的属性类是FloatPropertyValuesHolder,当然还有IntPropertyValuesHolder等之类的,其实都是一样理解。

在PropertyValuesHolder的内部类FloatPropertyValuesHolder的构造函数调用了setFloatValues来设置动画的目标值。然后才能通过getFloatValue函数获取到动画的关键帧。

这个方法的实现我们看下:

    public void setFloatValues(float... values) {        mValueType = float.class;        mKeyframeSet = KeyframeSet.ofFloat(values);    }

可以看到这个方法的调用了KeyframeSet.ofFloat(values);函数,追踪程序:

    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);    }

我们看到关键帧的计算就在ofFloat函数中。如果用户设置了一个目标值,那么这个值就是最终值,他的其实值被默认为0;如果用户设置了大于1的目标值,这些关键帧都会被存储到KetFrameSet对象中。设置完关键帧之后,我们就可以调用start()方法启动动画了,我们去看下OnjectAnimator对象的start()方法:

    private void start(boolean playBackwards) {        //判断looper是否为空,        if (Looper.myLooper() == null) {            throw new AndroidRuntimeException("Animators may only be run on Looper threads");        }        //设置基本状态        mPlayingBackwards = playBackwards;        mCurrentIteration = 0;        mPlayingState = STOPPED;        //启动动画        mStarted = true;        mStartedDelay = false;        mPaused = false;        //将该动画加入到等待执行的动画队列中        AnimationHandler animationHandler = getOrCreateAnimationHandler();        animationHandler.mPendingAnimations.add(this);        //判断是否延迟        if (mStartDelay == 0) {                        setCurrentPlayTime(0);            //设置动画的开始执行时间,因为动画会有一个duration            //这个开始执行时间+duration就是结束时间            mPlayingState = STOPPED;            mRunning = true;            //触发动画监听器            notifyStartListeners();        }                animationHandler.start();    }    @Override    public void start() {        start(false);    }

我们看到了,这个函数总共做了几件事:

  • 启动动画操作设置了一些基本参数
  • 把自己添加到了待执行的动画列表中
  • 通过AnimationHandler启动了动画






更多相关文章

  1. Android中persistent属性用法详解
  2. Android中Shape Drawable在xml中的使用
  3. Android(安卓)PercentRelativeLayout
  4. Android实现动画组合的四种方式
  5. Activity跳转动画
  6. 【转】android 属性动画之 ObjectAnimator
  7. Android(安卓)Animation解析
  8. Android利用ViewFlipper实现屏幕切换动画效果(上)
  9. android 给动画添加结束监听事件

随机推荐

  1. java与ASP.NET网络应用程序在生命期开始
  2. 怎样用java生成GUID与UUID
  3. 怎么用java 实现两个web service之间调用
  4. Java IO流系列(四)—— 从字节流及其缓冲区
  5. java8中lambda表达式
  6. FilenameFilter文件名过滤器使用实例
  7. Java核心技术卷I:基础知识(原书第8版):14.
  8. String字符串的应用
  9. java求1-1/3+1/5-1/7+1/9
  10. java GBK字符转换成为UTF-8编码字符