精华内容
参与话题
问答
  • Android属性动画之ValueAnimator

    千次阅读 2015-10-14 18:42:12
    属性动画通过改变一个对象的属性值来进行动画,属性动画包含了以下几个特性: 1、持续时间(Duration) 主要用来定义动画的持续时间,默认值为300ms。2、时间插值器(Time interpolation) 指定时间变化的百分比,...

    属性动画通过改变一个对象的属性值来进行动画,属性动画包含了以下几个特性:
    1、持续时间(Duration)
    主要用来定义动画的持续时间,默认值为300ms。

    2、时间插值器(Time interpolation)
    指定时间变化的百分比,就是当前流逝时间除以指定的持续时间,这个可以自定义,继承Interpolator,重写getInterpolation方法。

    3、重复次数和行为(Repeat count and behavior)
    指定动画的执行次数和动画的重复模式

    4、动画集(Animator sets)
    可以把多个动画放到一个集合中,是他们同时执行,或者指定它们直接的顺序和延迟。

    5、Frame refresh delay(帧刷新延迟)
    可以指定如何去刷新动画的帧,默认是每10ms刷新一次,这个刷新也取决于系统的繁忙程度。

    上面我们知道属性动画就是改变对象的属性值来实现动画,ValueAnimator的特点就是你不需要明确的指定你要改变的对象和属性,你只需要得到一个动态的值来自己去设置相应对象的属性,也就是它就是提供属性的变化值,你拿到这个值可以动态的更改对象属性值。总结一句就是监听动画过程,自己实现属性的改变。

    举个例子:

    // 这里指定了值的变化范围
    ValueAnimator animator = ValueAnimator.ofFloat(0, 500);
    // 这里指定变化持续时间
    animator.setDuration(1000);
    //开始动画
    animator.start()
    //开始动画后,我们可以动态的获取变化值
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
    {
        @Override
        public void onAnimationUpdate(ValueAnimator animation)
        {
            //根据变化值来设置imageView对象的Y轴坐标,这样就实现了imageView的垂直移动
            imageView.setTranslationY((Float) animation.getAnimatedValue());
        }
    });

    上面使用imageView.setTranslationY((Float) animation.getAnimatedValue())来动态的改变图片的translationY属性,需要说明的是,如果在低版本中,我们使用的是NineOldAnimations这个库,用法跟系统基本一致,在NineOldAnimations里面我们动态改变对象的属性的时候,它提供了一个ViewHelper类,它是设置各种动画值的帮助类,可以简单的设置并应用动画值。所以在3.0以下版本中,使用ViewHelper来进行属性值的改变,上面的设置等同如下:

    ViewHelper.setTranslationX(imageView, (Float) animation.getAnimatedValue());

    上面的过程如下图所示:
    这里写图片描述
    上面的过程应该比较清晰,在上面我们就设置了一个持续时间,下面我们可以来看看我们可以为ValueAnimator设置其他哪些东西。

    public ObjectAnimator setDuration(long duration)
    设置持续时间
    
    public void setEvaluator(TypeEvaluator value)
    设置估值器
    
    public void setInterpolator(/*Time*/Interpolator value)
    设置插值器
    
    public void setTarget(Object target)
    设置目标对象
    
    public void setRepeatCount(int value)
    设置动画重复次数
    
    public void setRepeatMode(int value)
    设置重复模式
    
    public void setValues(PropertyValuesHolder... values)
    设置值
    
    public void setStartDelay(long startDelay)
    设置启动延时
    
    public static void setFrameDelay(long frameDelay)
    设置帧延迟
    
    public void setIntValues(int... values)
    设置Int值,对应ValueAnimator.ofInt函数
    
    public void setFloatValues(float... values)
    设置Float值。对应ValueAnimator.ofFloat函数
    
    public void setCurrentPlayTime(long playTime)
    设置当前执行时间
    
    public void setObjectValues(Object... values)
    设置Object值,对应ValueAnimator.ofObject函数

    上面我们知道ValueAnimator是主要提供一个动态的变化值,这个值是怎么来变化的,它的变化函数就是由估值器和插值器来决定的,我们可以自定义估值器和插值器来自定义值的变化,另外这个变化值的类型,ValueAnimator提供了四种类型,Int,Float,Objcet,PropertyValuesHolder,它囊括了所有的类型。

    1、变化值的类型的确定
    我们知道,在我们定义一个属性动画对象的时候,可以不需要通过自己来创建的,主要有四种方式:

    public static ValueAnimator ofInt(int... values) 
    
    public static ValueAnimator ofFloat(int... values)
    
    public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) 
    
    public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values)

    从上面我们可以看到,不同的方式其实对应的就是不同类型的变化值。第一种方式的变化值类型为Int,第二种方式的变化值类型为Float,第三种方式的变化值类型为Object,第四种方式的变化值类型为PropertyValuesHolder,它其实是一个集合。

    2、估值器和插值器
    对于给定一个范围的值,例如上面例子中ValueAnimator.ofFloat(0, 500),它给定的变化范围为[0, 500],那么在这个范围内到底是如何变化的呢?可以是线性变化,可以是加速变化,可以是减速变化,在内部已经为我们定义好了几种变化方式,我们可以根据情况来进行使用。

    首先我们来说说时间插值器和类型估值器

    TimeInterpolator中文翻译为时间插值器,它的作用是根据时间流逝的百分比来计算出当前属性值改变的百分比,系统预置的有LinearInterpolator(线性插值器:匀速动画)、AccelerateDecelerateInterpolator(加速减速插值器:动画两头慢中间快)和DecelerateInterpolator(减速插值器:动画越来越慢)等;

    下面看看线性插值器的源码:

    public class LinearInterpolator implements Interpolator {  
    
        public LinearInterpolator() {  
        }  
    
        public LinearInterpolator(Context context, AttributeSet attrs) {  
        }  
    
        public float getInterpolation(float input) {  
            return input;  
        }  
    }

    我们自定义插值器的时候,只需要重写getInterpolation方法,其中传入的input参数就是时间流逝的百分比,这个百分比就是当前时间的流逝除以设置的持续时间Duration来得到的。我们实现这个函数的时候,可以通过改变这个值来实现我们想要的效果。

    TypeEvaluator的中文翻译为类型估值算法,它的作用是根据当前属性改变的百分比来计算改变后的属性值,系统预置的有IntEvaluator(针对整型属性)、FloatEvaluator(针对浮点型属性)和ArgbEvaluator(针对Color属性)。

    我们知道插值器的作用就是返回当前属性改变的百分比,这个百分比我们可以通过重写getInterpolation来自定义。其实真正的变化后的值是从估值器来得到的。

    我们来看看IntEvaluator的源码:

    public class IntEvaluator implements TypeEvaluator<Integer> {  
    
        public Integer evaluate(float fraction, Integer startValue, Integer endValue) {  
            int startInt = startValue;  
            return (int)(startInt + fraction * (endValue - startInt));  
        }  
    } 

    上述算法很简单,evaluate的三个参数分别表示:估值小数、开始值和结束值,其中的估值小数就是上面getInterpolation的返回值,开始值就是变化值的开始,结束值就是变化值的结束,对应上面例子ValueAnimator.ofFloat(0, 500),开始值为0,结束值为500,通过这三个参数,最终计算出变化后的值,然后将这个值返回去,我们最终得到的就是这个值,然后对指定对象的属性进行设置,这样来实现指定属性值的变化,从而实现了动画效果。

    所以我们如果希望自定义变化值的变化快慢,我们需要自定义一个插值器和一个估值器,插值器是为估值器服务的,估值器是为我们服务的,因为它最终返回了变化后的值。

    最后,我们如何得到这个变化后的值呢?从上面的例子中我们可以看到,我们只需要使用ValueAnimator的addUpdateListener函数来增加一个更新监听,当这个值变化之后,就会回调onAnimationUpdate函数,在传入的参数ValueAnimator对象中使用getAnimatedValue函数我们就可以获取到变化后的那个值,拿到这个变化后的值之后我们就可以动态的更新对象的属性值了。

    还有需要注意的是,我们如果没有显式指定插值器和估值器,它内部有默认值。

    下面我们来举个例子;

    /** 
     * 抛物线 
     * @param view 
     */  
    public void paowuxian(View view)  
    {  
    
        ValueAnimator valueAnimator = new ValueAnimator();  
        valueAnimator.setDuration(3000);  
        //这个地方设置了变化值的类型
        valueAnimator.setObjectValues(new PointF(0, 0));  
        //设置插值器
        valueAnimator.setInterpolator(new LinearInterpolator());  
        //设置估值器
        valueAnimator.setEvaluator(new TypeEvaluator<PointF>()  
        {  
            // fraction = t / duration  
            @Override  
            public PointF evaluate(float fraction, PointF startValue,  
                    PointF endValue)  
            {  
                Log.e(TAG, fraction * 3 + "");  
                // x方向200px/s ,则y方向0.5 * 10 * t  
                PointF point = new PointF();  
                point.x = 200 * fraction * 3;  
                point.y = 0.5f * 200 * (fraction * 3) * (fraction * 3);  
                //返回变化值
                //这个返回值会在addUpdateListener的回调中得到
                return point;  
            }  
        });  
    
        valueAnimator.start();  
        valueAnimator.addUpdateListener(new AnimatorUpdateListener()  
        {  
            @Override  
            public void onAnimationUpdate(ValueAnimator animation)  
            {  
                // 得到估值器里面的那个返回值
                PointF point = (PointF) animation.getAnimatedValue();
                //设置属性值  
                mBlueBall.setX(point.x);  
                mBlueBall.setY(point.y);  
    
            }  
        });  
    }  

    上面基本说清楚了ValueAnimator的特定和用法,下面来说说如何为这个动画添加事件监听。

    ValueAnimator animator = ValueAnimator.ofFloat();
    animator.setFloatValues(0, 500);
    animator.setTarget(imageView);
    animator.setDuration(1000);
    animator.start();
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
    {
        @Override
        public void onAnimationUpdate(ValueAnimator animation)
        {
            imageView.setTranslationY((Float) animation.getAnimatedValue());
        }
    });
    
    animator.addListener(new Animator.AnimatorListener(){
    
        @Override
        public void onAnimationStart(Animator animation) {
            Log.d(TAG, "onAnimationStart");
        }
    
        @Override
        public void onAnimationEnd(Animator animation) {
            Log.d(TAG, "onAnimationEnd");
        }
    
        @Override
        public void onAnimationCancel(Animator animation) {
            Log.d(TAG, "onAnimationCancel");
        }
    
        @Override
        public void onAnimationRepeat(Animator animation) {
            Log.d(TAG, "onAnimationRepeat");
        }
    });

    从上面可以看到直接添加一个监听就可以了,这样就可以监听动画的开始、结束、被取消、重复等事件,上面你需要重写上面四个函数一个都不能少,如果你只需要重写自己需要的函数,那你可以使用AnimatorListenerAdapter,例如只需要重新onAnimationEnd函数,因为AnimatorListenerAdapter继承自AnimatorListener。

    参考文章:
    http://blog.csdn.net/singwhatiwanna/article/details/17639987

    http://blog.csdn.net/lmj623565791/article/details/38067475

    http://blog.csdn.net/feiduclear_up/article/details/45893619

    http://developer.android.com/guide/topics/graphics/prop-animation.html

    展开全文
  • ValueAnimator的简单介绍和使用

    千次阅读 2017-03-25 18:26:55
    上一篇博客中的动画也可以通过使用ValueAnimator动画来实现,在这一篇博客中,我就来介绍如何使用ValueAnimator来实现这些动画!!!ValueAnimator和ObjectAnimator一样,既可以通过在xml中声明也可以在java代码实现...

    上一篇博客主要介绍了如何使用ObjectAnimator来实现我们想要的动画效果。ObjectAnimator是ValueAnimator的子类。上一篇博客中的动画也可以通过使用ValueAnimator动画来实现,在这一篇博客中,我就来介绍如何使用ValueAnimator来实现这些动画!!!ValueAnimator和ObjectAnimator一样,既可以通过在xml中声明也可以在java代码中声明,长久以来我都不用java代码中声明animator,这次我就使用java代码来生成animator实例(其实是因为使用xml声明过valueAnimator动画之后,还是需要通过代码来设置动画更新的监听事件,不如都是用java代码来设置来的痛快)。好了废话不多说,先看一下效果图,由于所有的动画动一样,为了与上一篇ObjectAnimator动画区别,我使用了弹性插值器,具体的在讲述到代码的时候在详说!!!
    效果图:
    这里写图片描述这里写图片描述这里写图片描述这里写图片描述这里写图片描述这里写图片描述这里写图片描述这里写图片描述这里写图片描述

    主布局文件:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:weightSum="2">
    
        <ImageView
            android:id="@+id/iv"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_gravity="center"
            android:layout_weight="1"
            android:src="@mipmap/ic_launcher" />
    
        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1">
            <GridLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:columnCount="2">
    
                <Button
                    style="@style/custom_button"
                    android:onClick="startScaleXAnimator"
                    android:text="@string/start_scale_x" />
    
                <Button
                    style="@style/custom_button"
                    android:onClick="startScaleYAnimator"
                    android:text="@string/start_scale_y" />
    
                <Button
                    style="@style/custom_button"
                    android:onClick="startAlphaAnimator"
                    android:text="@string/start_alpha" />
    
                <Button
                    style="@style/custom_button"
                    android:onClick="startRotateAnimator"
                    android:text="@string/start_rotate" />
    
                <Button
                    style="@style/custom_button"
                    android:onClick="startRotateXAnimator"
                    android:text="@string/start_rotate_x" />
    
                <Button
                    style="@style/custom_button"
                    android:onClick="startRotateYAnimator"
                    android:text="@string/start_rotate_y" />
    
                <Button
                    style="@style/custom_button"
                    android:onClick="startTranslationXAnimator"
                    android:text="@string/start_translation_x" />
    
                <Button
                    style="@style/custom_button"
                    android:onClick="startTranslationYAnimator"
                    android:text="@string/start_translation_y" />
            </GridLayout>
            <Button
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                style="@style/custom_button"
                android:text="@string/app_name"
                android:onClick="startSetAnimator"/>
        </LinearLayout>
    
    </LinearLayout>
    

    java文件:

    package com.example.valueanimatordemo;
    
    import android.animation.ValueAnimator;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.view.animation.BounceInterpolator;
    import android.animation.ValueAnimator.AnimatorUpdateListener;
    import android.widget.ImageView;
    
    public class MainActivity extends AppCompatActivity {
    
        private ValueAnimator mValueAnimator;
        private ImageView iv;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mValueAnimator = new ValueAnimator();
            mValueAnimator.setFloatValues(0.0f,1.0f,2.0f,3.0f);
            mValueAnimator.setDuration(5000);
            mValueAnimator.setInterpolator(new BounceInterpolator());
            iv = (ImageView) findViewById(R.id.iv);
            mValueAnimator.setEvaluator(null);
        }
        public void startScaleXAnimator(View view) {
            mValueAnimator.removeAllUpdateListeners();
            final float ivScaleX = iv.getScaleX();
            AnimatorUpdateListener scaleX = new AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float rate = (float) animation.getAnimatedValue();
                    iv.setScaleX(ivScaleX*(1+rate));
                }
            };
            mValueAnimator.setTarget(null);
            mValueAnimator.addUpdateListener(scaleX);
            mValueAnimator.start();
        }
    
        public void startScaleYAnimator(View view) {
            mValueAnimator.removeAllUpdateListeners();
            final float ivScaleY = iv.getScaleY();
            AnimatorUpdateListener scaleY = new AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float rate = (float) animation.getAnimatedValue();
                    iv.setScaleY(ivScaleY*(1+rate));
                }
            };
            mValueAnimator.setTarget(null);
            mValueAnimator.addUpdateListener(scaleY);
            mValueAnimator.start();
        }
    
        public void startAlphaAnimator(View view) {
            mValueAnimator.removeAllUpdateListeners();
            mValueAnimator.setFloatValues(1.0f,0.3f);
            final float alpha = iv.getAlpha();
            AnimatorUpdateListener mAlpha = new AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float rate = (float) animation.getAnimatedValue();
                    iv.setAlpha(rate*alpha);
                }
            };
            mValueAnimator.setTarget(null);
            mValueAnimator.addUpdateListener(mAlpha);
            mValueAnimator.start();
        }
    
        public void startRotateAnimator(View view) {
            mValueAnimator.removeAllUpdateListeners();
            mValueAnimator.setFloatValues(0.0f,360f);
            AnimatorUpdateListener mRotate = new AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = (float) animation.getAnimatedValue();
                    iv.setRotation(value);
                }
            };
            mValueAnimator.setTarget(null);
            mValueAnimator.addUpdateListener(mRotate);
            mValueAnimator.start();
        }
    
        public void startRotateXAnimator(View view) {
            mValueAnimator.removeAllUpdateListeners();
            mValueAnimator.setFloatValues(0.0f,360f);
            AnimatorUpdateListener mRotateX = new AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = (float) animation.getAnimatedValue();
                    iv.setRotationX(value);
                }
            };
            mValueAnimator.setTarget(null);
            mValueAnimator.addUpdateListener(mRotateX);
            mValueAnimator.start();
        }
    
        public void startRotateYAnimator(View view) {
            mValueAnimator.removeAllUpdateListeners();
            mValueAnimator.setFloatValues(0.0f,360f);
            AnimatorUpdateListener mRotateY = new AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = (float) animation.getAnimatedValue();
                    iv.setRotationY(value);
                }
            };
            mValueAnimator.setTarget(null);
            mValueAnimator.addUpdateListener(mRotateY);
            mValueAnimator.start();
        }
    
        public void startTranslationXAnimator(View view) {
            mValueAnimator.removeAllUpdateListeners();
            mValueAnimator.setFloatValues(0,0f,400f);
            AnimatorUpdateListener mLocationX = new AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = (float) animation.getAnimatedValue();
    //                iv.setX(value);//滑动的起始点为原点
                    iv.setTranslationX(value);//滑动的起始点为view所在的X点
                }
            };
            mValueAnimator.setTarget(null);
            mValueAnimator.addUpdateListener(mLocationX);
            mValueAnimator.start();
        }
    
        public void startTranslationYAnimator(View view) {
            mValueAnimator.removeAllUpdateListeners();
            mValueAnimator.setFloatValues(0,0f,400f);
            AnimatorUpdateListener mLocationY = new AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = (float) animation.getAnimatedValue();
                    float rate = animation.getAnimatedFraction();
    //                iv.setY(value);//滑动的起始点为原点
                    iv.setTranslationY(value);//滑动的起始点为view所在的X点
                }
            };
            mValueAnimator.setTarget(null);
            mValueAnimator.addUpdateListener(mLocationY);
            mValueAnimator.start();
        }
    
        public void startSetAnimator(View view){
            mValueAnimator.removeAllUpdateListeners();
            mValueAnimator.setFloatValues(1.0f,0.3f);
            final float alpha = iv.getAlpha();
            final float scaleX = iv.getScaleX();
            final float scaleY = iv.getScaleY();
            AnimatorUpdateListener set = new AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float rate = (float) animation.getAnimatedFraction();
                    iv.setScaleX(scaleX+(rate*scaleX)*3);
                    iv.setScaleY(scaleY+(rate*scaleY)*3);
                    iv.setRotation(rate*360f);
    //                iv.setRotationX(rate*360f);
    //                iv.setRotationY(rate*360f);
                    iv.setAlpha(rate*alpha);
                    if(iv.getAlpha()<0.3){
                        iv.setAlpha(0.3f);
                    }
                }
            };
            mValueAnimator.setTarget(null);
            mValueAnimator.addUpdateListener(set);
            mValueAnimator.start();
        }
    }
    

    在使用ValueAnimator的时候,除了一个插值器之外,还多了一个差值器!在这里我们简单说一下,在下一篇博客中,我们在详细说一下估值器的作用。
    估值器就是动画状态更新的一个回调,方法传入的一个ValueAnimator实例,我们可以通过这个实例获取动画的值,如果是控制ScaleX,那么我们获取的值就是一个float值,根据这个值来重新更新View的ScaleX。不过ValueAnimator的动画值也是计算出来的,那么计算的依据是什么呢,相信你已经猜到了吧!!没错就是插值器传出的值,要知道插值器传出的值范围在0~1之间,这代表的是动画的进度,而估值器就是根据插值器返回的值计算当前时刻动画的值。而且无论是插值器还是估值器我们都可以自定义的,好了,不说了,下一篇博客在详说吧!!!

    其实单一控制一个属性的动画很容易实现的,但是每次看到其他优秀APP上的动画就感觉没头绪有没有,这其实是因为我们接触的还不够多,相信在不断的学习的情况,在不远的将来,那些我们现在看起来很复杂的动画,都可以轻易实现的!!

    好了,关于valueAnimator动画方面就说到这,写一篇博客还会介绍动画相关的内容的,希望大家多多关注!!

    这是我的微信公众号,如果可以的话,希望您可以帮忙关注一下,这将是对我最大的鼓励了,谢谢!!
    公众号

    代码地址:
    https://github.com/zhuyuqiang2017/Animation

    展开全文
  • Android开发的过程中经常要用到属性动画,这里对ValueAnimator做一个非常简单的源码过程分析,方便以后在网上扒例子的时候抄的快一点。

    Android开发的过程中经常要用到属性动画,经常都是网上扒下来看下怎么用,但是经常不知道为什么要这么用,手一哆嗦一不小心就点到源码里面去了。我们就来看看Android属性动画ValueAnimator类源码的简单实现,从而对ValueAnimator类有个大概的了解。

    在Android开发过程中做动画效果的时候用到ValueAnimator的时候最简单的方法我们是这么干的

            // ValueAnimator
            ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 200, 100);
            valueAnimator.setDuration(3000);
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    int currentValue = (Integer) animation.getAnimatedValue();
                    setX(currentValue);
                }
            });
            valueAnimator.start();

    看到了ValueAnimator的简单使用那就得看下里面是怎么实现的了。ValueAnimator的源码准备从两个地方入手。
    1. ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 200, 100);这里干了些啥。
    2. valueAnimator.start();里面都干了些啥子东西。在start的分析过程中同时可以看到onAnimationUpdate回调函数的调用时机。

    第一部分ValueAnimator.ofInt()函数做了一些什么事情

    为了分析ValueAnimator.ofInt()函数里面的实现过程,得先简单的知道下面的类图关系。

    这里写图片描述

    准备分两个部分来看这个类图。
    1. PropertyValuesHolder部分:PropertyValuesHolder有好几个子类,这里只是列出了四个子类IntPropertyValuesHolder,FloatPropertyValuesHolder,MultiFloatValuesHolder,MultiIntValuesHolder。从类图中还可以看到PropertyValuesHolder 到 Keyframes的关联关系。从源码上面也可以看到PropertyValuesHolder类里面有Keyframes类型的成员变量mKeyframes(IntPropertyValuesHolderd里面对应的是Keyframes.IntKeyframes类型的mIntKeyframes变量),这里PropertyValuesHolder里面对于mKeyframes的成员变量只是关心setEvaluator(TypeEvaluator evaluator); getType(); getValue(float fraction); invalidateCache(); getKeyframes();这几个方法。
    2. Keyframes部分:先看类图的右下部分Keyframe类是一个抽象类有三个子类ObjectKeyframe,IntKeyframe,FloatKeyframe。Keyframe类表示动画的关键时刻包括关键时间和该时间点对应的值以及插值器等。再来看Keyframes部分类图中KeyframeSet实现了Keyframes,所以重心点在KeyframeSet,从上面的类图间的关系看到KeyframeSet关联了Keyframe,源码上面也可以看到KeyframeSet里面有一个Keyframe的成员变量mKeyframes 的List。

    从整体上来看这个类图PropertyValuesHolder是最外层的类他关心的方法只是setEvaluator(TypeEvaluator evaluator); getType(); getValue(float fraction); invalidateCache(); getKeyframes(); 那就得来看每个方法的具体实现了,从类图可以看出这些方法的具体实现都是在KeyframeSet类以及KeyframeSet类的子类中实现的。这里我们就挑一个函数来分析看KeyframeSet类里面的getValue(float fraction);的具体实现。

    在看这个函数的具体实现之前我们先得知道两个东西
    1. Interpolator 时间插值器,根据时间流逝的百分比计算出当前属性值改变的百分比。定义动画变换的速度。能够实现alpha/scale/translate/rotate动画的加速、减速和重复等。Interpolator类其实是一个空接口,继承自TimeInterpolator,TimeInterpolator时间插值器允许动画进行非线性运动变换,如加速和限速等,该接口中只有接口中有一个方法 float getInterpolation(float input)这个方法。传入的值是一个0.0~1.0的值,返回值可以小于0.0也可以大于1.0。
    2. TypeEvaluator 估值器,根据当前属性改变的百分比来计算改变后的属性值。系统给我们提供三种插值器IntEvaluator:针对整型属性,FloatEvaluator:针对浮点型属性,ArgbEvaluator:针对Color属性。如果还需要其他类型的插值器就得自己去自定义了。看看IntEvaluator的源码是怎么实现的。

    /**
     * This evaluator can be used to perform type interpolation between <code>int</code> values.
     */
    public class IntEvaluator implements TypeEvaluator<Integer> {
    
        /**
         * This function returns the result of linearly interpolating the start and end values, with
         * <code>fraction</code> representing the proportion between the start and end values. The
         * calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
         * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
         * and <code>t</code> is <code>fraction</code>.
         *
         * @param fraction   The fraction from the starting to the ending values
         * @param startValue The start value; should be of type <code>int</code> or
         *                   <code>Integer</code>
         * @param endValue   The end value; should be of type <code>int</code> or <code>Integer</code>
         * @return A linear interpolation between the start and end values, given the
         *         <code>fraction</code> parameter.
         */
        public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
            int startInt = startValue;
            return (int)(startInt + fraction * (endValue - startInt));
        }
    }

    好像也没什么东西样的就一个函数evaluate,我们关注返回值得类型是int型的数据。同时我们还得关注函数的三个参数startValue,endValue一个开始的值一个结束的值这个好说,第一个参数fraction是开始值和结束值变化的百分比。

    这下可以安心的来看KeyframeSet 类的 getValue(float fraction)了,源码如下

       /**
         * Gets the animated value, given the elapsed fraction of the animation (interpolated by the
         * animation's interpolator) and the evaluator used to calculate in-between values. This
         * function maps the input fraction to the appropriate keyframe interval and a fraction
         * between them and returns the interpolated value. Note that the input fraction may fall
         * outside the [0-1] bounds, if the animation's interpolator made that happen (e.g., a
         * spring interpolation that might send the fraction past 1.0). We handle this situation by
         * just using the two keyframes at the appropriate end when the value is outside those bounds.
         *
         * @param fraction The elapsed fraction of the animation
         * @return The animated value.
         */
        public Object getValue(float fraction) {
            // Special-case optimization for the common case of only two keyframes
            if (mNumKeyframes == 2) {
                if (mInterpolator != null) {
                    fraction = mInterpolator.getInterpolation(fraction);
                }
                return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(),
                        mLastKeyframe.getValue());
            }
            if (fraction <= 0f) {
                final Keyframe nextKeyframe = mKeyframes.get(1);
                final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
                if (interpolator != null) {
                    fraction = interpolator.getInterpolation(fraction);
                }
                final float prevFraction = mFirstKeyframe.getFraction();
                float intervalFraction = (fraction - prevFraction) /
                    (nextKeyframe.getFraction() - prevFraction);
                return mEvaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(),
                        nextKeyframe.getValue());
            } else if (fraction >= 1f) {
                final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2);
                final TimeInterpolator interpolator = mLastKeyframe.getInterpolator();
                if (interpolator != null) {
                    fraction = interpolator.getInterpolation(fraction);
                }
                final float prevFraction = prevKeyframe.getFraction();
                float intervalFraction = (fraction - prevFraction) /
                    (mLastKeyframe.getFraction() - prevFraction);
                return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
                        mLastKeyframe.getValue());
            }
            Keyframe prevKeyframe = mFirstKeyframe;
            for (int i = 1; i < mNumKeyframes; ++i) {
                Keyframe nextKeyframe = mKeyframes.get(i);
                if (fraction < nextKeyframe.getFraction()) {
                    final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
                    final float prevFraction = prevKeyframe.getFraction();
                    float intervalFraction = (fraction - prevFraction) /
                        (nextKeyframe.getFraction() - prevFraction);
                    // Apply interpolator on the proportional duration.
                    if (interpolator != null) {
                        intervalFraction = interpolator.getInterpolation(intervalFraction);
                    }
                    return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
                            nextKeyframe.getValue());
                }
                prevKeyframe = nextKeyframe;
            }
            // shouldn't reach here
            return mLastKeyframe.getValue();
        }

    从函数的介绍我们也可以看到这个函数的参数float fraction已经是通过插值器转换之后的属性百分比的值了可能是小于0的也可能是大于1的。这个函数的作用简单来说就是通过传入经过插值器转换之后的值(您也可以把他理解成属性变化的百分比)得到具体属性的值,最后给到onAnimationUpdate回调函数。
    15-21行,如果动画过程中我们只关注两个关键时间点(动画的开始点,动画的结束点)直接通过估值器去计算得到属性值。
    22-33行,如果fraction<=0,只有在第一个时间点和第二个时间点的时候才会有fraction<=0的情况这个好理解哦。第29行intervalFraction表示两个点之间的百分比这个好说吧,我们知道了两个点的值,以及这两个点过程中的百分比 把这三个参数给估值器得到属性的具体值。
    33-44行,如果fraction>=1,只有在倒数第二个时间点和倒数第一个时间才有这样的情况,同样得到intervalFraction然后通过估值器去计算。
    46-61行,有多个时间点,先判断fraction落在哪个时间段。然后得到intervalFraction,给估值器去计算。

    总的来说这个函数就是你给我一个插值器变化之后的值,我就给你这个时间点的属性值。

    IntKeyframes里面对应的是public int getIntValue(float fraction)具体的实现也是一样的。

    终于到了ValueAnimator.ofInt()的过程分析了,这里就 以int类型来分析,float,rgb的类型都是一样的道理得到的。

        public static ValueAnimator ofInt(int... values) {
            ValueAnimator anim = new ValueAnimator();
            anim.setIntValues(values);
            return anim;
        }

    是个static函数 参数是可变的可以传多个int型的数据。会通过出入的int数据去确定动画变化过程中的关键时间点和该时间点对应的数据。这个函数做的事情就是new了一个ValueAnimator对象anim 继续调用了anim.setIntValues。然后把这个对象返回了。继续看setIntValues()函数。

        public void setIntValues(int... values) {
            if (values == null || values.length == 0) {
                return;
            }
            if (mValues == null || mValues.length == 0) {
                setValues(PropertyValuesHolder.ofInt("", values));
            } else {
                PropertyValuesHolder valuesHolder = mValues[0];
                valuesHolder.setIntValues(values);
            }
            // New property/values/target should cause re-initialization prior to starting
            mInitialized = false;
        }

    5-7行, 如果mValues(PropertyValuesHolder[] mValues)之前没有被设置过则调用setValues函数这个函数的参数我们等下再分析。如果之前已经设置了则改变第一个PropertyValuesHolder的值。这里我们先看setValues函数的内容然后在看setValues函数的参数。

        public void setValues(PropertyValuesHolder... values) {
            int numValues = values.length;
            mValues = values;
            mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
            for (int i = 0; i < numValues; ++i) {
                PropertyValuesHolder valuesHolder = values[i];
                mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
            }
            // New property/values/target should cause re-initialization prior to starting
            mInitialized = false;
        }

    就赋了三个变量的值了,这个好理解mValuesMap key对应的是属性的字符串value对应的是PropertyValuesHolder对象。

    接下来该看下setValues函数的参数是怎么得到的了哦。跟踪PropertyValuesHolder.ofInt(“”, values)函数。

        public static PropertyValuesHolder ofInt(String propertyName, int... values) {
            return new IntPropertyValuesHolder(propertyName, values);
        }

    看的出来构造的是IntPropertyValuesHolder对于(PropertyValuesHolder的子类,因为这里我们分析的是int型的数据这条主线)
    IntPropertyValuesHolder类的构造函数

            public IntPropertyValuesHolder(String propertyName, int... values) {
                super(propertyName);
                setIntValues(values);
            }

    IntPropertyValuesHolder类的setIntValues函数。

            @Override
            public void setIntValues(int... values) {
                super.setIntValues(values);
                mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes;
            }

    直接看super.setIntValues(values) PropertyValuesHolder类中setIntValues函数。

        public void setIntValues(int... values) {
            mValueType = int.class;
            mKeyframes = KeyframeSet.ofInt(values);
        }

    设置了mValueType的类型是int.class。给mKeyframes赋值了。分析类图的时候我们也知道mKeyframes变量是PropertyValuesHolder重要的部分。我们是要通过mKeyframes去获取动画变化过程中各个时间点的属性值的。
    继续跟踪KeyframeSet类中ofInt函数。

        public static KeyframeSet ofInt(int... values) {
            int numKeyframes = values.length;
            IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];
            if (numKeyframes == 1) {
                keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
                keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
            } else {
                keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);
                for (int i = 1; i < numKeyframes; ++i) {
                    keyframes[i] =
                            (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
                }
            }
            return new IntKeyframeSet(keyframes);
        }

    第3行,new了一个IntKeyframe数组(IntKeyframe是Keyframe的子类,在分析类图的时候说过Keyframe里面保存的是关键时间点的时间和该时间对应的数据)
    第4-7行,如果参数只有一个那么有两个关键时间点一个是(0, 0)一个是(1, value)。这个也好理解就是一个开始点一个结束点。
    第7-12行,如果参数的个数不止一个的时候,根据参数的个数去平分时间点,比如ofInt的初始值是 ofInt(100, 200, 300)那么我们得到的时间点就是(0, 100), (1/2, 200),(1, 300)。三个关键的时间点。这个也好理解哦。
    14行,返回了IntKeyframeSet的对象并且把keyframes的数据传进去了,里面做的事情还是挺简单的我们就不进去了看了。这样整个ValueAnimator.ofInt()部分就走到头了。

    总结ValueAnimator.ofInt()做的事情。就是得到了PropertyValuesHolder的对象(不管具体的是IntPropertyValuesHolder还是FloatPropertyValuesHolder得对象都是他的子类)。并且把这个对象赋值给了ValueAnimator里面的mValues,和mValuesMap(key是属性字符串,value才是PropertyValuesHolder的对象)。PropertyValuesHolder里面有一个Keyframes mKeyframes的对象,同样Keyframes里面有 mKeyframes的List(时间点和该时间点对应的属性值的List)。PropertyValuesHolder里面mKeyframes对象的每个操作都是在mKeyframes的List基础之上进行的。为了更好的理解这一部分我们先提前看下是怎么通过PropertyValuesHolder去获取动画过程中各个时间点对应的属性值的,直接跳到看ValueAnimator animateValue()函数,这个函数在下文讲ValueAnimator start()函数的会调用到,会把整个串起来的,这里先提前看下。

        @CallSuper
        void animateValue(float fraction) {
            fraction = mInterpolator.getInterpolation(fraction);
            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);
                }
            }
        }

    第一行先通过插值器吧时间进度按照某种规律(匀速,加速等等)转换了一下。然后去遍历mValues,通过前面的分析我们知道mValues是PropertyValuesHolder的数组我们分析过的哦,遍历去调用每个PropertyValuesHolder的calculateValue函数,进去看下了。
    PropertyValuesHolder类里面的calculateValue函数。

        void calculateValue(float fraction) {
            Object value = mKeyframes.getValue(fraction);
            mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
        }

    用到了PropertyValuesHolder里面的Keyframes mKeyframes了吧,又调用了Keyframes的getValue函数。在跟进去比如我们调用的是ValueAnimator.ofInt()构造的ValueAnimator,那么这里会走到IntKeyframeSet类的getValue函数

        @Override
        public Object getValue(float fraction) {
            return getIntValue(fraction);
        }

    直接调用的是getIntValue,就是我们在分析类图的时候分析过的函数把(KeyframeSet getValue()一样的意思),这样就得到了动画过程中时间点对应的值了。这个值就直接给了onAnimationUpdate回调函数了。

    第二部分ValueAnimator.start()函数做了一些什么事情

    其实吧,前面干的事情都还没进入到动画的过程,还在是在赋值做一些动画的准备,下面开始动画过程的分析。入口函数ValueAnimator 的start函数。
    ValueAnimator类里面的start函数。

        @Override
        public void start() {
            start(false);
        }
    private void start(boolean playBackwards) {
            if (Looper.myLooper() == null) {
                throw new AndroidRuntimeException("Animators may only be run on Looper threads");
            }
            mReversing = playBackwards;
            mPlayingBackwards = playBackwards;
            if (playBackwards && mSeekFraction != -1) {
                if (mSeekFraction == 0 && mCurrentIteration == 0) {
                    // special case: reversing from seek-to-0 should act as if not seeked at all
                    mSeekFraction = 0;
                } else if (mRepeatCount == INFINITE) {
                    mSeekFraction = 1 - (mSeekFraction % 1);
                } else {
                    mSeekFraction = 1 + mRepeatCount - (mCurrentIteration + mSeekFraction);
                }
                mCurrentIteration = (int) mSeekFraction;
                mSeekFraction = mSeekFraction % 1;
            }
            if (mCurrentIteration > 0 && mRepeatMode == REVERSE &&
                    (mCurrentIteration < (mRepeatCount + 1) || mRepeatCount == INFINITE)) {
                // if we were seeked to some other iteration in a reversing animator,
                // figure out the correct direction to start playing based on the iteration
                if (playBackwards) {
                    mPlayingBackwards = (mCurrentIteration % 2) == 0;
                } else {
                    mPlayingBackwards = (mCurrentIteration % 2) != 0;
                }
            }
            int prevPlayingState = mPlayingState;
            mPlayingState = STOPPED;
            mStarted = true;
            mStartedDelay = false;
            mPaused = false;
            updateScaledDuration(); // in case the scale factor has changed since creation time
            AnimationHandler animationHandler = getOrCreateAnimationHandler();
            animationHandler.mPendingAnimations.add(this);
            if (mStartDelay == 0) {
                // This sets the initial value of the animation, prior to actually starting it running
                if (prevPlayingState != SEEKED) {
                    setCurrentPlayTime(0);
                }
                mPlayingState = STOPPED;
                mRunning = true;
                notifyStartListeners();
            }
            animationHandler.start();
        }

    参数表示动画是否反向播放。
    mReversing:表示当前是否是从后面开始播放的。
    mPlayingBackwards:表示当前动画的这次播放是否是在反向播放。有这样的情况比如动画的count是2,第一次正向播放第二次方向播放。
    mSeekFraction:表示动画要跳到的播放时间点(0~1)setCurrentFraction()函数里面设置。
    mCurrentIteration:表示当前动画是第几次播放。
    7-18行,如果动画是要反向播放的并且我们调用了setCurrentFraction(0函数设置了mSeekFraction,这个if里面做的事情就是去设置动画是从哪个点去播放的计算出mSeekFraction和mCurrentIteration的值,举个例子,我们设置了动画的播放次数是5次(setRepeatCount(5)),并且调用了setCurrentFraction(1.3),。那么经过setCurrentFraction(1.3)函数之后mSeekFraction = 0.3,mCurrentIteration = 1。这段代码会做的事情就是因为我们的动画是要反向播放的计算之后mSeekFraction = 0.7,mCurrentIteration = 4。因为要反向播放。
    19-28行,如果当前的播放模式是REVERSE的模式,进一步去确定动画这次播放是顺序播放还是反向播放。
    35,36行,获取了AnimationHandler的实例,并且把当前动画加入到了pending animator里面去了。
    37-45行,动画的播放是否要延时的处理。
    46行,调用了animationHandler的start函数。

    那就直接去看AnimationHandler类中start函数

            /**
             * Start animating on the next frame.
             */
            public void start() {
                scheduleAnimation();
            }
            private void scheduleAnimation() {
                if (!mAnimationScheduled) {
                    mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
                    mAnimationScheduled = true;
                }
            }

    mAnimate的Runable

            // Called by the Choreographer.
            final Runnable mAnimate = new Runnable() {
                @Override
                public void run() {
                    mAnimationScheduled = false;
                    doAnimationFrame(mChoreographer.getFrameTime());
                }
            };

    AnimationHandler类中的doAnimationFrame函数

    void doAnimationFrame(long frameTime) {
                mLastFrameTime = 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);
                        } 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();
                }
    
                // Schedule final commit for the frame.
                mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, mCommit, null);
    
                // 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();
                }
            }

    9-23行,循环去遍历mPendingAnimations的list,拿到mPendingAnimations里面的每个动画实例ValueAnimator。如果这个动画不用延时播放直接调用ValueAnimator里面的anim.startAnimation(this);并且把当前的对象传递进去了,如果要延时播放就把这个动画加入到延时的List mDelayedAnims当中去。 回过头看下anim.startAnimation(this);做的事情
    ValueAnimator类中的startAnimation函数。

        private void startAnimation(AnimationHandler handler) {
            if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
                Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
                        System.identityHashCode(this));
            }
            initAnimation();
            handler.mAnimations.add(this);
            if (mStartDelay > 0 && mListeners != null) {
                // Listeners were already notified in start() if startDelay is 0; this is
                // just for delayed animations
                notifyStartListeners();
            }
        }

    先调用了initAnimation函数,给ValueAnimator里面的每个PropertyValuesHolder的Keyframes设置了估值器。PropertyValuesHolder,Keyframes前面都有提到过。然后又把这个动画的对象加入到了AnimationHandler里面的mAnimations list里面去了。
    在回头继续AnimationHandler类中的doAnimationFrame函数
    27-33行,遍历有延时的动画,如果延时时间到了就把动画加入到mReadyAnims里面去。
    34-43行,去遍历mReadyAnims里面的动画,调用anim.startAnimation(this);加入到mAnimations里面去,并且从mDelayedAnims里面移除掉。
    47-56行,遍历mAnimations里面的动画,调用anim.doAnimationFrame(frameTime)函数开始具体的动画逻辑了,如果动画结束了就把该动画加入到mEndingAnims当中去,进去看下doAnimationFrame函数的具体动作了
    ValueAnimator类中的doAnimationFrame函数。

        final boolean doAnimationFrame(long frameTime) {
            if (mPlayingState == STOPPED) {
                mPlayingState = RUNNING;
                if (mSeekFraction < 0) {
                    mStartTime = frameTime;
                } else {
                    long seekTime = (long) (mDuration * mSeekFraction);
                    mStartTime = frameTime - seekTime;
                    mSeekFraction = -1;
                }
                mStartTimeCommitted = false; // allow start time to be compensated for jank
            }
            if (mPaused) {
                if (mPauseTime < 0) {
                    mPauseTime = frameTime;
                }
                return false;
            } else if (mResumed) {
                mResumed = false;
                if (mPauseTime > 0) {
                    // Offset by the duration that the animation was paused
                    mStartTime += (frameTime - mPauseTime);
                    mStartTimeCommitted = false; // allow start time to be compensated for jank
                }
            }
            // The frame time might be before the start time during the first frame of
            // an animation.  The "current time" must always be on or after the start
            // time to avoid animating frames at negative time intervals.  In practice, this
            // is very rare and only happens when seeking backwards.
            final long currentTime = Math.max(frameTime, mStartTime);
            return animationFrame(currentTime);
        }

    设置了状态,记录了mStartTime的时间。接着调用了animationFrame函数。

        boolean animationFrame(long currentTime) {
            boolean done = false;
            switch (mPlayingState) {
            case RUNNING:
            case SEEKED:
                float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
                if (mDuration == 0 && mRepeatCount != INFINITE) {
                    // Skip to the end
                    mCurrentIteration = mRepeatCount;
                    if (!mReversing) {
                        mPlayingBackwards = false;
                    }
                }
                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;
                        // Note: We do not need to update the value of mStartTimeCommitted here
                        // since we just added a duration offset.
                    } else {
                        done = true;
                        fraction = Math.min(fraction, 1.0f);
                    }
                }
                if (mPlayingBackwards) {
                    fraction = 1f - fraction;
                }
                animateValue(fraction);
                break;
            }
    
            return done;
        }

    如果设置了duration,计算得到时间的进度,比如fraction = 1.3;说明动画是在第二次播放了。如果设置了播放模式是REVERSE。fraction = 1 - 0.3 = 0.7。这个好理解吧。 接着调用animateValue函数。done返回值表示动画是否完成了。
    接着看下animateValue函数。

        @CallSuper
        void animateValue(float fraction) {
            fraction = mInterpolator.getInterpolation(fraction);
            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);
                }
            }
        }

    先调用插值器设置加速,匀速之类的。然后通过PropertyValuesHolder计算得到当前的属性值,这个上文当中有提到过,第一部分ValueAnimator.ofInt()最后讲到的正好和这里接上了
    继续回到AnimationHandler类中的doAnimationFrame函数
    58-63行,遍历mEndingAnims,那个动画结束了就设置一些值,并且回调动画结束的函数。
    70-72行,如果还有动画有调用了scheduleAnimation函数,又重复了上面的逻辑了。知道所有的动画都播放完。

    总结第二部分,动画的具体实现是靠AnimationHandler来驱动,AnimationHandler里面也是各种list的记录各个状态的动画。

    流水账终于记完了,下一篇准备看看ObjectAnimator源码的简单实现。Android属性动画ObjectAnimator源码简单分析

    展开全文
  • ValueAnimator详解

    2017-06-15 08:29:27
    属性动画是通过改变控件的内部属性值来实现动画效果。这篇主要讲ValueAnimator的知识点。

    Android动画共分为两种:View Animation(视图动画)和Property Animator(属性动画)

    • View Animation 包括 Tween Animation(补间动画)和 Frame Animation(逐帧动画)
    • Property Animator 包括 ValueAnimator 和 ObjectAnimation

    View Animation 和 Property Animator的区别:

    区别 View Animation Property Animator
    引入时间 API Level 1 API Level 11
    所在包名 android.view.animation android.animation
    命名 XXXXAnimation XXXXAnimator
    作用对象 控件 控件属性



    这里以TranslateAnimation位移动画为例,补间动画改变的只是控件的显示位置,没有改变控件的实际位置。这个过程是由Parent View来实现的,在View被绘制时,Parent View改变View的 绘制参数,这个View就会发生对应的位移动画,但是View的实际参数并没有改变。对应的View在移动过程中和移动后是没有点击效果的,然而在View原来的点击区域,可能此时已经不可见,但是仍然可以有点击效果。属性动画就恰恰相反,属性动画是通过改变控件的内部属性值来实现动画效果。

    简单使用:
    这里写图片描述

     valueAnimator = ValueAnimator.ofInt(10,800);
              valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                  @Override
                  public void onAnimationUpdate(ValueAnimator animation) {
                      int value = (int) animation.getAnimatedValue();
                      tv.setText("值:"+value);
                      img.layout((int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,16,getResources().getDisplayMetrics())+value), (int) img.getY(),
                              (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,16,getResources().getDisplayMetrics())+value+img.getWidth()),(int) img.getY()+img.getHeight());//坐标以父控件为基准
                  }
              });
              valueAnimator.setDuration(1000);
              valueAnimator.start();
    

    基本的的api使用,请查看api文档,这里我们将一些别的。

    取消监听的方法

     /*
      * 移除 AnimatorUpdateListener
      */
      void removeUpdateListener(AnimatorUpdateListener listener);
      void removeAllUpdateListeners();
       /*
        * 移除 AnimatorListener
        */
      void removeListener(AnimatorListener listener);
      void removeAllListeners();
      /*
       * 延时多久时间开始,单位是毫秒
       */
      public void setStartDelay(long startDelay)
      /*
       * 完全克隆一个 ValueAnimator 实例,包括它所有的设置以及所有对监听器代码的处理
       * 就像克隆羊一样,克隆羊拥有母体所有的特征,但是完成克隆之后,就是新的生命体,跟母体也就没有任何关系了
       */
      public ValueAnimator clone()

    Evaluator计算器

    这里写图片描述
    Evaluator其实就是一个转换器,将小数进度转换成对应的数值。Evaluator都是专用的,比如ofInt(int…),那么对应的Evaluator必然要返回int类型的值,否则就会报强转的错误。valueAnimator.setEvaluator()来设置转换器.

    IntEvaluator源码:

    public class IntEvaluator implements TypeEvaluator<Integer> {
        public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
            int startInt = startValue;
            return (int)(startInt + fraction * (endValue - startInt));
        }
    }

    其中 fraction 就是插值器中的返回值,表示当前动画的数值进度,百分制的小数表示。 startValue 和 endValue 分别对应 ofInt(int start,int end)中的 start 和 end 的数值;

    自定义Evaluator

    public class MyEvaluator implements TypeEvaluator<Integer> {
        @Override
        public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
            int startInt = startValue;
            return (int)(200+startInt + fraction * (endValue - startInt));
        }
    }

    所以我们可以通过自定义插值器改变数值进度来改变数值位置,也可以通过自定义Evaluator改变进度所对应数值来改变数值位置。

    ArgbEvaluator色值过渡计算器

    例子:

     valueAnimator = ValueAnimator.ofInt(0xfff10f0f,0xfff10fbf,0xff740ff1,0xff2f0ff1,0xff0f94f1,0xff0ff1af,0xff14f10f,0xffeaf804,0xfff92a0f);
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    int value = (int) animation.getAnimatedValue();
                    img.setBackgroundColor(value);
                }
            });
            valueAnimator.setDuration(6000);
            valueAnimator.setEvaluator(new ArgbEvaluator());
            valueAnimator.start();

    效果:
    这里写图片描述

    源码:

    public class ArgbEvaluator implements TypeEvaluator {
        private static final ArgbEvaluator sInstance = new ArgbEvaluator();
        /**
         * @hide
         */
        public static ArgbEvaluator getInstance() {
            return sInstance;
        }
        public Object evaluate(float fraction, Object startValue, Object endValue) {
            int startInt = (Integer) startValue;
            int startA = (startInt >> 24) & 0xff;
            int startR = (startInt >> 16) & 0xff;
            int startG = (startInt >> 8) & 0xff;
            int startB = startInt & 0xff;
    
            int endInt = (Integer) endValue;
            int endA = (endInt >> 24) & 0xff;
            int endR = (endInt >> 16) & 0xff;
            int endG = (endInt >> 8) & 0xff;
            int endB = endInt & 0xff;
    
            return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
                    (int)((startR + (int)(fraction * (endR - startR))) << 16) |
                    (int)((startG + (int)(fraction * (endG - startG))) << 8) |
                    (int)((startB + (int)(fraction * (endB - startB))));
        }

    色值:A R G B ,分别表示透明度,红色,绿色,蓝色,每一个色值都用十六进制来表示,0xffff0000,
    色值是通过位移和运算求出的。色值和ARGB对应关系如下:
    这里写图片描述

    ofObject讲解

    ofObject()可以实现自定义类型的动画效果

    public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values);

    第一个是自定义的 Evaluator,第二个是可变长参数,Object 类型的

    一个小例子:
    这里写图片描述
    自定义Evaluator

    public class MyEvaluator implements TypeEvaluator<String> {
        private static final String TAG = "MyEvaluator";
        @Override
        public String evaluate(float fraction, String startValue, String endValue) {
            int length = endValue.length();
            String result = String.valueOf(endValue.charAt((int) (fraction * (length -1))));
            return result;
        }
    }
    ValueAnimator animator = ValueAnimator.ofObject(new MyEvaluator(),"且品鸡汤莫问厨娘");
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    String str = (String) animation.getAnimatedValue();
                    tv.setText(str);
                }
            });
            animator.setDuration(5000);
            animator.setInterpolator(new LinearInterpolator());
            animator.start();

    自定义对象实例

    效果图:
    这里写图片描述

     private void animatorOfObjectCustom(){
            ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluator(),new Ponit(20),new Ponit(200));
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    Ponit ponit = (Ponit) animation.getAnimatedValue();
                    mCircleView.setPoint(ponit);
                }
            });
            animator.setDuration(1000);
            animator.setInterpolator(new BounceInterpolator());
            animator.start();
        }

    自定义类

    public class Ponit {
        private int mRadius;
        public Ponit() {
        }
        public Ponit(int mRadius) {
            this.mRadius = mRadius;
        }
        public int getRadius() {
            return mRadius;
        }
        public void setRadius(int mRadius) {
            this.mRadius = mRadius;
        }
    }
    

    自定义view

    public class CircleView extends View {
        private Ponit mCurrentPoint;
        private Paint mPiant ;
        private int mScreenWidth;//屏幕宽度
        public CircleView(Context context) {
            this(context,null);
        }
    
        public CircleView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs,0);
        }
    
        public CircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            mPiant = new Paint(Paint.ANTI_ALIAS_FLAG);
            mPiant.setColor(Color.RED);
            mPiant.setStyle(Paint.Style.FILL);
            mScreenWidth = ((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getWidth();
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            if (mCurrentPoint != null){
                canvas.drawCircle(mScreenWidth/2,getY()+getPaddingTop(),mCurrentPoint.getRadius(),mPiant);
            }
        }
    
        public void setPoint(Ponit point){
            this.mCurrentPoint = point;
            invalidate();
        }
    }
    
    展开全文
  • 转载请注明出处:  上一篇博文我们初步地了解了Property Animation的工作方式及流程,并通过实验加深了对对象属性计算过程的理解,同时,我在文章的最后罗列一些实现Property Animation比较重要的类、接口和...
  • Android属性动画完全解析 ValueAnimator

    万次阅读 2018-04-10 11:13:58
    android 属性动画完全解析,初识属性动画的基本用法:在手机上去实现一些动画效果算是件比较炫酷的事情,因此Android系统在一开始的时候就给我们提供了两种实现动画效果的方式,逐帧动画(frame-by-frame animation)...
  • 1 ValueAnimator和估值器简介 属性动画从API11 开始提供,动画实现主要依靠ValueAnimator和ObjectAnimator两个类,类,属性动画所在包为android.animation.Animator,和补间动画有明显区别,补间动画在android.view....
  • 前言:不要让别人的无知断送了你的梦想,永远坚信你所坚信的。 相关文章: ... 一、概述 ...long long ago,我写过几篇有关Animation的文章,讲解了传统的alpha、scale、translate、rotate的用法...
  • 今日科技快讯近日,有知情人士爆料:美国司法部正就华为是否违反美国对伊朗的制裁规定进行调查。目前,外界尚不清楚美司法部的调查进展到何种程度,以及联邦探员对华为的具体指控。不...
  • ValueAnimator

    2019-04-10 16:04:14
    ValueAnimator是之前提到过的ObjectAnimator的父类; 它使用起来不像ObjectAnimator这么简单,但可控性更高,更能发挥想象力; 一般来说,使用ValueAnimator需要通过以下几步; setObjectValues setEvaluator ...
  • 大家好,在上一篇文章当中,我们学习了Android属性动画的基本用法,当然也是最常用的一些用法,这些用法足以覆盖我们平时大多情况下的动画需求了。但是,正如上篇文章当中所说到的,属性动画对补间动画进行了很大...
  • Android属性动画之ValueAnimator使用

    千次阅读 2016-11-16 16:51:46
    Android属性动画之ValueAnimator
  • 今天,我将讲解属性动画使用中最核心的一个方法类:ValueAnimator,希望你们会喜欢
  • 前言:厚积才能薄发,未来只属于为梦想而奋斗的人们,今天的你决定未来的自己。 一、概述 大家可能觉得补间动画和逐帧动画已经很全面了,为什么还要引入Property Animator(属性动画)呢?有关View Animation...
  • 转载地址:http://mafei.site/2016/07/17/android-valueanimator/?sukey=3997c0719f1515205be8ad9aea79b9ad51de542a2b92fc95a19db31c5683e178477941e608c949305e1948a0d90a8f7b 最近在学习有关自定义 View ...
  • android中动画分为两大类,视图动画(ViewAnimatior)和属性动画(PropertyAnimator),视图动画主要包括逐帧动画和补间动画,属性动画主要分为ValueAnimator和ObjectAnimator。视图动画和属性动画本质上的操作对象都是要...
  • 前言:不要让别人的无知断送了你的梦想,永远坚信你所坚信的。相关文章:《Android自定义控件三部曲文章索引》:http://blog.csdn.net/harvic880925/article/details/50995268一、概述long long ago,我写过...其实这三
  • ValueAnimator属性动画

    2017-07-13 20:01:22
    1 官方文档This class provides a simple timing engine(计时器) for running animations which calculate animated values and set them on target objects.There is a single timing pulse that all animations ...
  • 上一篇文章给大家介绍了ValueAnimator的基本用法,都是比较简单的用法,这里带大家学习一下有关加速器(Interpolator)、计算器(Evaluator)、ValueAnimator的ofObject用法等相关知识。 一、插值器(Interpolator) ....
  • 概述 android动画经常会碰到卡顿,或者阻塞主进程之类的问题。 为了排查此类问题,不得不对动画原理了解一二,于是作此文。 此文围绕两个主线问题展开: ...Android动画总结 (valueAnimator、objectAnimator、Animat

空空如也

1 2 3 4 5 ... 20
收藏数 13,264
精华内容 5,305
关键字:

valueanimator