Android动画探究

    个人觉得,一款移动应用软件,影响用户体验的很大一部分就取决于动画,Android中也是如此,因此在应用层开发中我个人也很注重动画的使用,在能够熟练使用后就想探究其原理了,因此,开这篇博客不仅仅是对动画的一个总结,也是对动画的更深入的一个探究。话不多说,开写了。

    Android动画可以分为三类:Tween Animation(补间动画)、Frame Animatino(帧动画)、Property Animation(属性动画),其中,补间动画比较简单,无非就是平移、缩放、透明度渐变,这些只需要根据相关api设置好想要的结果参数即可,想要达到预期效果可能会经过不断地调试设置~0.0~,当然,如果是老手,已经熟练了,熟练到知道一个dp、sp在手机上是多少个单位以及结果图在脑海里的样子 -0-会很容易了~。本文会重点介绍补间动画和属性动画,尤其是属性动画。

     一、补间动画

    1. 补间动画的使用

    补间动画又分为四大类,分别是:旋转动画、平移动画、缩放动画、透明度渐变动画。下面从xml开始依次介绍动画的用法和相关的属性,最后通过直接用代码实现动画来探究其原理。

    (1)旋转动画RoateAnimation

     在补间动画中,动画都是平面2D效果,因此旋转动画也是指逆时针或顺时针旋转,其用法无非就是设置各个属性,其各属性含义如下:

<?xml version="1.0" encoding="utf-8"?>
<!-- 旋转动画 -->

<!--
     ======  属性说明   =========
     formDegrees:  开始旋转前的角度
     toDegrees:    旋转后的角度
     pivotX:       中心点X的坐标,其单位%p表示相对父容器的坐标,%表示相对自己的坐标,没有单位只有数字则是指在屏幕上坐标的x轴坐标
     pivotY:       旋转中心店Y的坐标
     duration:     动画持续时间
     fillAfter:    如果为true(前提是fillEnable不是false,fillEnable属性默认为true),则动画执行完毕后会保留执行后的状态
     startOffset:  动画开始的延迟时间,单位是毫秒,比如如果该值为3000,那么动画就在3秒后执行
     repeatCount: 动画执行的次数
     repeatMode:  重复的方式,枚举类型,有两种选择"restart"表示执行完一次动画后又从头执行,"reverse"本身就是相反的意思,
                                              它表示动画执行完动画后沿着原来旋转的方向反向旋转回来   

-->
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:detachWallpaper="true"
    android:duration="1400"
    android:fillAfter="false"
    android:fromDegrees="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:repeatCount="2"
    android:repeatMode="reverse"
    android:toDegrees="180"
    android:zAdjustment="bottom" >

</rotate>
     以上的动画效果是:一个View绕其中心点顺时针旋转180°,并重复两次,在结束第一次动画后“原路返回”执行第二次动画(repeateMode属性设置为reverse的效果),动画持续时间是1.4秒,并且动画在结束后回到执行动画之前的位置(如果fillAfter设置为true,则执行动画完毕后视图保持执行后的状态)。

     值得注意的就是pivotX和pivotY属性的单位,如果带有%表示相对自己旋转,如假设pivotX和pivotY都是50%,则表示动画以本视图的中心点(宽/2和高/2)旋转,如果是50%p,则表示动画以该视图的父视图的中心点来旋转。如果是50,则表示相对于屏幕坐标系的绝对坐标来旋转。

    xml设置动画完毕,就差在java代码中使用了,使用方法很简单,如下两行代码搞定:

Animation anima = AnimationUtils.loadAnimation(mContext, R.anim.anim_rote);
mImageView.startAnimation(anima);
    这样就可以让一个ImageView旋转起来,当然这是最简单的动画效果。现在假设我们有这样一个需求,在上面的动画基础上,我们需要在动画执行完成后,让该动画在上次的动画基础上继续执行,也就是前一个动画是从0到180°旋转,下一个动画就开始从180°到360°旋转,虽然fillAfter设置为true可以做到动画完成后保存其执行后的状态,但是当动画再次执行时仍然是从0到180°执行,因此,我们需要在动画结束后保存其执行后的状态,然后让其再次执行时从上一次执行后的状态开始执行,这就需要对动画进行监听,然而,我们要想实现这个需求使用xml并不方便,因为需要动态改变其属性,所以需要在代码中创建动画,这就体现了在代码中创建动画的灵活性但是维护性差评。对上面的代码改造如下:

    

/**
	 * 旋转
	 * @param view
	 */
	public void onRote(View view){
//		Animation anima = AnimationUtils.loadAnimation(mContext, R.anim.anim_rote);
		mFlag++;
		Animation anima = null;
		if(mFlag%2==1){
			mToDegrees =180;
			mFromeDegrees = 0;
			anima = new RotateAnimation(mFromeDegrees, mToDegrees,Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF,0.5F);
		}else if(mFlag%2==0){
			mToDegrees =360;
			mFromeDegrees = 180;
			anima = new RotateAnimation(mFromeDegrees, mToDegrees, Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF,0.5F);
		}
		anima.setDuration(1000);
		anima.setFillAfter(true);
		anima.setRepeatMode(Animation.REVERSE);
		anima.setRepeatCount(2);
		anima.setAnimationListener(new AnimationListener());
		mImageView.startAnimation(anima);
	}

/**
     * 动画监听
     * 
     * @author Administrator
     *
     */
    @SuppressLint("NewApi")
    private class AnimationListener implements
            android.view.animation.Animation.AnimationListener {
        @Override
        public void onAnimationEnd(Animation animation) {
            // TODO Auto-generated method stub
            Animation anima = AnimationUtils.loadAnimation(mContext,
                    R.anim.anim_rote_01);
            // mImageView.startAnimation(anima);
            // mImageView.postInvalidate();
            // startActivity(new---activity);
            Toast.makeText(mContext, "动画执行结束", Toast.LENGTH_LONG).show();
        }

        @Override
        public void onAnimationRepeat(Animation animation) {
            // TODO Auto-generated method stub
            Toast.makeText(mContext, "动画重复", Toast.LENGTH_LONG).show();
        }

        @Override
        public void onAnimationStart(Animation animation) {
            // TODO Auto-generated method stub
            Toast.makeText(mContext, "动画开始了", Toast.LENGTH_LONG).show();
        }
    }
       上面的代码创建的动画效果和xml创建的是一样的,需要注意的是,在创建Animation对象的时候,API中提供了三个构造器,三个构造器源码如下:

     

/**
     * Constructor used when a RotateAnimation is loaded from a resource.
     * 
     * @param context Application context to use
     * @param attrs Attribute set from which to read values
     */
    public RotateAnimation(Context context, AttributeSet attrs) {
    //此构造器是在用AnimationUtils.load的时候被调用,我们并不会直接使用到,官方注释已经很清楚了。
    }

/**
     * Constructor to use when building a RotateAnimation from code.
     * Default pivotX/pivotY point is (0,0).
     *   这个构造器比较简单,就是一个起始角度和结束角度,一般比较少用,因为我们需要指定旋转中心点
     * @param fromDegrees Rotation offset to apply at the start of the
     *        animation.
     * 
     * @param toDegrees Rotation offset to apply at the end of the animation.
     */
    public RotateAnimation(float fromDegrees, float toDegrees) {
        mFromDegrees = fromDegrees;
        mToDegrees = toDegrees;
        mPivotX = 0.0f;
        mPivotY = 0.0f;
    }

/**
     * Constructor to use when building a RotateAnimation from code
     *    这个用的也不是很多,就是在上一个构造器的基础上添加了poivotX和pivotY两个参数用于指定旋转点
     * @param fromDegrees Rotation offset to apply at the start of the
     *        animation.
     * 
     * @param toDegrees Rotation offset to apply at the end of the animation.
     * 
     * @param pivotX The X coordinate of the point about which the object is
     *        being rotated, specified as an absolute number where 0 is the left
     *        edge.
     * @param pivotY The Y coordinate of the point about which the object is
     *        being rotated, specified as an absolute number where 0 is the top
     *        edge.
     */
    public RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY) {
        mFromDegrees = fromDegrees;
        mToDegrees = toDegrees;

        mPivotXType = ABSOLUTE;
        mPivotYType = ABSOLUTE;
        mPivotXValue = pivotX;
        mPivotYValue = pivotY;
        initializePivotPoint();
    }

/**
     * Constructor to use when building a RotateAnimation from code
     * 重点就是这个构造器,是我们常用的,第三个参数是用于指定旋转的类型,有三种选择:
     * 1、相对自己(通过Animation.RELATIVE_TO_SELF设置) 2、绝对旋转(相对屏幕坐标系,通过Animation.ABSOLUTE)
     * 3、相对父视图。我们通过Animation.RELATIVE_TO_PARENT设置
     *
     * @param fromDegrees Rotation offset to apply at the start of the
     *        animation.
     * 
     * @param toDegrees Rotation offset to apply at the end of the animation.
     * 
     * @param pivotXType Specifies how pivotXValue should be interpreted. One of
     *        Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
     *        Animation.RELATIVE_TO_PARENT.
     * @param pivotXValue The X coordinate of the point about which the object
     *        is being rotated, specified as an absolute number where 0 is the
     *        left edge. This value can either be an absolute number if
     *        pivotXType is ABSOLUTE, or a percentage (where 1.0 is 100%)
     *        otherwise.
     * @param pivotYType Specifies how pivotYValue should be interpreted. One of
     *        Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
     *        Animation.RELATIVE_TO_PARENT.
     * @param pivotYValue The Y coordinate of the point about which the object
     *        is being rotated, specified as an absolute number where 0 is the
     *        top edge. This value can either be an absolute number if
     *        pivotYType is ABSOLUTE, or a percentage (where 1.0 is 100%)
     *        otherwise.
     */
    public RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,
            int pivotYType, float pivotYValue) {
        mFromDegrees = fromDegrees;
        mToDegrees = toDegrees;

        mPivotXValue = pivotXValue;
        mPivotXType = pivotXType;
        mPivotYValue = pivotYValue;
        mPivotYType = pivotYType;
        initializePivotPoint();
    }

  

    (2)平移动画TranslateAnimation(略)

    (3)缩放动画ScaleAnimation(略)

      (4)透明度渐变动画AlphaAnimation(略)

     上面就以旋转动画为例,将动画的通用属性展示出来,至于平移、缩放、透明度都是一样一样的,就几个特性不一样,但是都可以见名思意的,因此在这里不再赘述。下面直接进入主题,补间动画的实现原理分析,源码分析依然是从RoateAnimation开始。

    

     2.补间动画原理源码初窥

    (1)xml文件加载过程分析

      通常我们加载xml文件生成一个Animation对象时,是用过Google封装的AnimationUtils的loadAnimation()方法,那么我们就从这个方法看看一个xml文件是如何让一个View动起来的。

     

/**
     * Loads an {@link Animation} object from a resource
     *
     * @param context Application context used to access resources
     * @param id The resource id of the animation to load
     * @return The animation object reference by the specified id
     * @throws NotFoundException when the animation cannot be loaded
     */
    public static Animation loadAnimation(Context context, int id)
            throws NotFoundException {
       //XmlResourceParser接口是继承XmlPullParser解析器和AttributeSet接口的,AttributeSet接口以前说过,就不用再说了,
       // 

        XmlResourceParser parser = null;
        try {
            //这里根据id获取解析器然后调用createAnimationFormXml方法生成一个Animation对象
            parser = context.getResources().getAnimation(id);
            return createAnimationFromXml(context, parser);
        } catch (XmlPullParserException ex) {
            NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
                    Integer.toHexString(id));
            rnf.initCause(ex);
            throw rnf;
        } catch (IOException ex) {
            NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
                    Integer.toHexString(id));
            rnf.initCause(ex);
            throw rnf;
        } finally {
            if (parser != null) parser.close();
        }
    }
       正如上面的注释那样,loadAnimation()中其实也没啥代码,就是获取一下解析器然后调用createAnimationFromXml()来生成Animation实例:

     

private static Animation createAnimationFromXml(Context c, XmlPullParser parser,
            AnimationSet parent, AttributeSet attrs) throws XmlPullParserException, IOException {
        //声明一个Animationd对象
        Animation anim = null;

        // Make sure we are on a start tag.
        int type;
        int depth = parser.getDepth();
        //再说一次,Android中对资源xml文件解析都是使用Pull解析的,这里先判断是否有下一个节点,是否结束
        while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
               && type != XmlPullParser.END_DOCUMENT) {

            if (type != XmlPullParser.START_TAG) {
                continue;
            }

            String  name = parser.getName();
            //如果是set节点(就是我们多中组合动画的SetAnimation了),就递归调用自己,直到把所有的动画解析完毕
           //然后下面是一系列的判断根据alpha、scale、rotate、translate来生成不同的动画
            if (name.equals("set")) {
                anim = new AnimationSet(c, attrs);
                createAnimationFromXml(c, parser, (AnimationSet)anim, attrs);
            } else if (name.equals("alpha")) {
                anim = new AlphaAnimation(c, attrs);
            } else if (name.equals("scale")) {
                anim = new ScaleAnimation(c, attrs);
            }  else if (name.equals("rotate")) {
                anim = new RotateAnimation(c, attrs);
            }  else if (name.equals("translate")) {
                anim = new TranslateAnimation(c, attrs);
            } else {
                throw new RuntimeException("Unknown animation name: " + parser.getName());
            }

            if (parent != null) {
                parent.addAnimation(anim);
            }
        }

        return anim;

    }
       由上面的代码可以看出来,在使用xml文件加载动画时,其本质上还是通过解析xml文件根据节点来生成相应的XXXAnimation实例,因此,如果Android项目中不用xml文件,所有布局、动画都直接用代码实现那么速度会快一些,但是也得不偿失,因为代码布局很生硬啊!一个项目得多长时间呢。好了,接下来就看看RoateAnimation中是怎么实现动画的吧!

      首先看下这个类中定义的属性字段,不多,所以全贴出来:

    private float mFromDegrees;
      
    private float mToDegrees;
    //注意这里,看到,默认的mPivotXType是绝对的,就是相对于坐标系的,这个ABSLUTE是在RoateAnimation的父类Animation中
   //定义的 
    private int mPivotXType = ABSOLUTE;
    private int mPivotYType = ABSOLUTE;
    private float mPivotXValue = 0.0f;
    private float mPivotYValue = 0.0f;

    private float mPivotX;
    private float mPivotY;
       属性字段就那么几个,各个字段的含义上面的xml文件都有说道,下面看下加载xml文件解析后使用到的RoateAnimation(Context context, AttributeSet attrs)构造器:
public RotateAnimation(Context context, AttributeSet attrs) {
        super(context, attrs);
        //获取类型数组,就是xml中属性值得类型,int、float等
        TypedArray a = context.obtainStyledAttributes(attrs,
                com.android.internal.R.styleable.RotateAnimation);
        //获取到旋转后的位置角度以及开始的角度
        mFromDegrees = a.getFloat(
                com.android.internal.R.styleable.RotateAnimation_fromDegrees, 0.0f);
        mToDegrees = a.getFloat(com.android.internal.R.styleable.RotateAnimation_toDegrees, 0.0f);
        //这个不用看,就封装了旋转类型和旋转角度的一系列转换
        Description d = Description.parseValue(a.peekValue(
            com.android.internal.R.styleable.RotateAnimation_pivotX));
        mPivotXType = d.type;
        mPivotXValue = d.value;

        d = Description.parseValue(a.peekValue(
            com.android.internal.R.styleable.RotateAnimation_pivotY));
        mPivotYType = d.type;
        mPivotYValue = d.value;

        a.recycle();
        //这个方法只有当mPivotXType为ABSOLUTE时才会起作用
        initializePivotPoint();
    }
       上面的代码几乎就是RoateAnimation所有的代码了,还有几个构造器上面已经介绍过。哦额,忘记了,这个类里面还有两个很重要的方法,是重写了其父类Animation的,那就是applyTransformation()方法和·initialize()方法。先说initialize()方法,顾名思义就是初始化的操作,源码如下:
 @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        mPivotX = resolveSize(mPivotXType, mPivotXValue, width, parentWidth);
        mPivotY = resolveSize(mPivotYType, mPivotYValue, height, parentHeight);
    }
     可以看到,在RoateAnimation中该方法首先调用了父类的initialize()方法,其父类中的initialize()中会调用Animation的reset()方法,该方法中只有简单几行动画状态参数的初始化,再无其他。那么现在问题来了,动画是怎么被触发的呢?不激动,这得从我们如何开始一个动画说起,我们启动一个动画不就是一个mView.startAnimation()吗!那我们就去这个方法里找找去,startAnimation()源码如下:

    

/**
     * Start the specified animation now.
     *
     * @param animation the animation to start now
     */
    public void startAnimation(Animation animation) {
        animation.setStartTime(Animation.START_ON_FIRST_FRAME);
        //给该View设置一个动画
        setAnimation(animation);
        //使父容器缓存失效
        invalidateParentCaches();
        //意思就是使这个View失效,那么它就需要进行重绘,上面设置了一个setAnimation(),那么重绘时就会执行相应的动画了
        //关于动画的一切都从这里开始,下一篇博客会从这个方法开始讲起
         invalidate(true);
    }

      在RoateAnimation中还有一个方法applyTransformation(),这个方法是很关键的,名字直译过来就是应用变换,也就是开始动画的变换,动画的真正实现也是从这里开始,下面我们看下其源码:

  

 @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        float degrees = mFromDegrees + ((mToDegrees - mFromDegrees) * interpolatedTime);
        float scale = getScaleFactor();
        
        if (mPivotX == 0.0f && mPivotY == 0.0f) {
            t.getMatrix().setRotate(degrees);
        } else {
           //如果有旋转点就设置旋转的中心点,先获取一个Matrix(矩阵)
           t.getMatrix().setRotate(degrees, mPivotX * scale, mPivotY * scale);
        }
    }
       首先说下Transformation是个什么类,看其解释:
/**
 * Defines the transformation to be applied at
 * one point in time of an Animation.
 *意思就是:定义一个动画执行时的某个时间点的变换,这个变换是一个名词哈!稍后深入原理我们会讲到这个类。
 */
     再看下Matrix的setRotate里面是个什么东东:


 /**
     * Set the matrix to rotate about (0,0) by the specified number of degrees.
     *  这里调用了native_setRotate()方法,这个方法并不是java实现的,而是C,这个方法声明在Matrix如:
     * private static native void native_setRotate(int native_object,float degrees, float px, float py);
     */
    public void setRotate(float degrees) {
        native_setRotate(native_instance, degrees);
    }
       可以看到,动画的实现实际上是调用底层方法实现的,java本身并不实现。这里不对Matrix做过多介绍,我们只是初窥,下一篇博客会有详细的介绍。补间动画就到此为止了,本文以RoateAnimation为例,将了补间动画在xml中和java代码中的使用。下面接着将属性动画。


   二、属性动画(Property Animation)

    属性动画是3.0后才有的特性,它比比补间动画要灵活且实现的功能更多一些,首先看·下关于属性动画的几个类:

   (1)ValueAnimator: 动画执行类

   (2)ObjectAnimator:动画执行类,是ValueAnimator的子类

   (3)AnimatorSet:复杂组合动画,可以同时完成多个属性动画

   (4) AnimatorInflater:用于加载xml的属性文件动画,和AnimationUtils.loadAnimation()作用一样

   (5) TypeEvaluator : 类型估值,主要用于设置动画操作属性的值

   (6)TimeInterpolator:时间插值,也就是插值器

    1、ObjectAnimator的用法

     (1)从xml加载属性动画

       xml的旋转属性动画文件如下:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    android:propertyName="rotationX"
    android:repeatCount="120"
    android:valueFrom="0.0"
    android:valueTo="360.0"
    android:valueType="floatType" />
     上面的动画是让一个View绕X轴完成360°旋转,每次动画执行3秒,重复120次,值得注意的是propertyName属性,该属性是一个View中所必须有的属性,并且为该属性提供了getter和setter方法,在xml文件中,该属性是一个enum类型,用alt+/可以查看 - -、如果在代码中不确定VIew有哪些属性,也可以用这种方式查看啊。。。在xml中布局完毕,就需要在代码中加载了,实现如下:
ObjectAnimator objAnima = (ObjectAnimator) AnimatorInflater.loadAnimator(this, R.animator.property_anima);
    	objAnima.setTarget(mImageView);
    	objAnima.start();
     很简单吧,一定要注意,在xml中不能为动画设置View目标,必须在代码中设置。下面看下在布局文件中使用set实现多动画组合,文件如下:

   

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    >
    <!-- 绕X轴旋转 -->
<objectAnimator 
    android:duration="30000"
    android:propertyName="rotationX"
    android:repeatCount="120"
    android:valueFrom="0.0"
    android:valueTo="360.0"
    android:valueType="floatType" />
<!-- 绕Y轴旋转 -->
<objectAnimator 
    android:duration="30000"
    android:propertyName="rotationY"
    android:repeatCount="120"
    android:valueFrom="0.0"
    android:valueTo="360.0"
    android:valueType="floatType" />

</set>
    这个动画完成的就是同时完成X轴和Y轴旋转,3D效果,在set节点中有一个ordering属性,该属性有两个可选值:sequentially表示动画一个个执行,together表示同时执行。在代码中加载也是一样的,只是把上面的ObjectAnimator改成AnimatorSet即可。

   (2)在代码中直接创建ObjectAnimator动画

    代码如下,实现和上面的一样绕着X轴旋转的效果

ObjectAnimator objAnima = ObjectAnimator.ofFloat(mImageView, "rotationX", 0.0F,360F);
    	objAnima.setDuration(3000);
    	objAnima.start();
     是一个代码使用需要注意的是,第二个参数,这个参数必须是View的一个具有getter和setter方法的参数,后面两个参数表示起始和结束角度(如果是平移则是距离了),可以是只有一个参数,默认一个起始位置参数。现在需求又变了,让VIew绕X轴、Y轴同时旋转并且期间有一个透明度变换,代码实现如下:

   

ObjectAnimator objAnima = ObjectAnimator.ofFloat(mImageView, "rotationX", 0.0F,360F);
    	objAnima.setDuration(3000);
    	ObjectAnimator objAnima1 = ObjectAnimator.ofFloat(mImageView, "rotationY", 0.0F,360F);
    	objAnima1.setDuration(3000);
    	ObjectAnimator objAnima2 = ObjectAnimator.ofFloat(mImageView, "alpha",1.0F,0.2F);
    	objAnima2.setDuration(3000);
    	AnimatorSet animaSet = new AnimatorSet();
    	//设置objAnima和objAnima1、objAnima2同时执行
    	animaSet.play(objAnima).with(objAnima1);
    	animaSet.play(objAnima1).with(objAnima2);
    	animaSet.start();

   2、ValueAnimator用法

    ValueaNnimator并不需要指定属性名称,而是通过监听来手动设置属性的,使用如下:

ValueAnimator valueAnima = ValueAnimator.ofFloat(0.0F,360F);
    	valueAnima.addUpdateListener(new AnimatorUpdateListener() {
			@Override
			public void onAnimationUpdate(ValueAnimator va) {
				float value = (Float) va.getAnimatedValue();
				mImageView.setRotationX(value);
				mImageView.setRotationY(value);
			}
		});
    	valueAnima.setDuration(3000);
    	valueAnima.start();

    现在我们知道了属性动画的基本用法,用法很是灵活,可以根据需求来实现很多丰富的效果,在Animator中还有两个重要的方法,那就是setEvaluator()和setInterpolator(null);关于这两个方法我们在动画下一篇博客里会说到

     

   三、动画的高级用法

        1、补间动画的差之器Interpolator

        插值器是一个时间插值类,实际上是一个速率的控制类,就是控制了动画在执行的过程中速率的控制,Interpolator是一个空的接口,源码如下:

 

/**
 * An interpolator defines the rate of change of an animation. This allows
 * the basic animation effects (alpha, scale, translate, rotate) to be 
 * accelerated, decelerated, repeated, etc.
 */
public interface Interpolator extends TimeInterpolator {
    // A new interface, TimeInterpolator, was introduced for the new android.animation
    // package. This older Interpolator interface extends TimeInterpolator so that users of
    // the new Animator-based animations can use either the old Interpolator implementations or
    // new classes that implement TimeInterpolator directly.
}
    可以看到,里面没有任何方法,但是实现该接口的有很多类,这里我直接引用别人的博客的差之器:
  1. AccelerateDecelerateInterpolator============动画开始与结束的地方速率改变比较慢,在中间的时候加速。
  2. AccelerateInterpolator===================动画开始的地方速率改变比较慢,然后开始加速。 
  3. AnticipateInterpolator ==================开始的时候向后然后向前甩。
  4. AnticipateOvershootInterpolator=============开始的时候向后然后向前甩一定值后返回最后的值。
  5. BounceInterpolator=====================动画结束的时候弹起。
  6. CycleInterpolator======================动画循环播放特定的次数,速率改变沿着正弦曲线。
  7. DecelerateInterpolator===================在动画开始的地方快然后慢。
  8. LinearInterpolator======================以常量速率改变。
  9. OvershootInterpolator====================向前甩一定值后再回到原来位置。
  10. PathInterpolator========================新增的,就是可以定义路径坐标,然后可以按照路径坐标来跑动;注意其坐标并不是 XY,而是单方向,也就是我可以从0~1,然后弹回0.5 然后又弹到0.7 有到0.3,直到最后时间
    看一下最简单的插值器LinearInterpolapor,源码如下:

   

/**
 * An interpolator where the rate of change is constant
 *
 */
public class LinearInterpolator implements Interpolator {

    public LinearInterpolator() {
    }
    
    public LinearInterpolator(Context context, AttributeSet attrs) {
    }
    //这个参数就是输入的
    public float getInterpolation(float input) {
        return input;
    }
}
     以上就是他的全部源码了,啥也没有,是不是很简单,再来一个稍微复杂一点的CycleInterpolator,其源码如下:

/**
 * Repeats the animation for a specified number of cycles. The
 * rate of change follows a sinusoidal pattern.
 *
 */
public class CycleInterpolator implements Interpolator {
    public CycleInterpolator(float cycles) {
        mCycles = cycles;
    }
    
    public CycleInterpolator(Context context, AttributeSet attrs) {
        TypedArray a =
            context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.CycleInterpolator);
        
        mCycles = a.getFloat(com.android.internal.R.styleable.CycleInterpolator_cycles, 1.0f);
        
        a.recycle();
    }
    //这个值input就是一个小数,在0~1之间,0表示动画的开始,1表示动画结束,
    public float getInterpolation(float input) {
        return (float)(Math.sin(2 * mCycles * Math.PI * input));
    }
    
    private float mCycles;
}
     代码如此简单,我们自定义一个插值器当然也很简单了,如下是我定义的一个插值器
/**
	 * 自定义差之器
	 * @author Administrator
	 *
	 */
	private class MyInterpolator implements Interpolator{
        
		@Override
		public float getInterpolation(float input) {
			
                        //这是我随便写的一个“算法”....
                        float range = (float) (Math.cos(input)*Math.PI);
			return range;
		}
		
	}

      本文完结。 

       

           

    

   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值