认识动画(3)-属性动画(Property Animator)

概述

在认识动画第一节我们就提到了这个属性动画(Property Animator),也了解了它与视图动画的差异,但是大家有没有想过为已经有了View Aniamtion还要再引入一个Property呢?接下来我们就先讨论下,它的存在必要性
1.为什么引入Property Animator(属性动画)
既然引入了Property Animator就说明View Animation(这里实际指得是补间动画)有不能达到的效果,例如我们想将一个控件的背景颜色1秒之内从红色渐变为绿色,这个效果很明显利用补间动画的透明化,移动,旋转,放大缩小功能所不能完成的,而这个效果可以通过Property Animator完美实现.

  • 这就是第一个原因,属性动画能做到补间动画做不到的事.
  • 至于第二点原因,补件动画是对指定的控件做动画,而Property则可以改变某一控件的属性值.
  • 而第三点原因与第二条有很大的关系,上面我们也说了,补间动画只能对控件进行动画,而属性动画是针对属性的,所以补间动画并没有改变控件的任何属性,而属性动画则是直接改变了动画的属性来进行动画.
举个栗子:

假如我们使用TrabslateAnimation使一个已经设置了事件监听的按钮从(0,0)移动到(50,50)此时,点击在(50,50)的按钮并不能激活点击事件,然而当我们点击(0,0)原来按钮的位置时仍会激活点击事件.这就说明了对于Button来讲,虽然经历了动画,但实际上它的属性没有任何改变.

ValueAnimator

使用方式

ValueAnimator的使用方式很简单,一共只需要两步:

  1. 创建ValueAnimator实例
ValueAnimator animator = ValueAnimator.ofInt(0,400);  
animator.setDuration(1000);  
animator.start(); 

在这里我们可以看到我们定义了一个0-400的动画.动画时长是1s,然后动画开始.从代码中可以看出整个过程和控件没有任何的关联,那也正好说明了ValueAnimator只是对值做动画运算,而不是针对控件的,我们需要监听ValueAnimator的动画过程自己对控件做操作.

  1. 添加监听
    上面的3行代码,我们已经实现了动画,下面我们就对动画添加监听:
ValueAnimator animator = ValueAnimator.ofInt(0,400);  
animator.setDuration(1000);  
  
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
    @Override  
    public void onAnimationUpdate(ValueAnimator animation) {  
        int curValue = (int)animation.getAnimatedValue();  
       // 利用得到的curValue值就是当前View要操作的属性的当前值
    }  
});  
animator.start(); 

上面代码中我们对动画添加了一个addUpdateListener方法监听,在监听回传的结果中,是表示当前的ValueAnimator实例,我们通过animation.getAnimatedValue()获得当前值来设置动画属性.
这就是ValueAnimator的功能,他只负责数值从起点到终点的变化,而我们需要通过对数值变化的监听改变控件的属性值,完成动画.总结起来就两点:

  • ValueAniamtor只负责对指定的数字区间进行动画运算
  • 我们需要对运算过程进行监听,然后自己对控件做动画操作

常用方法

ValueAniamtor常用的方法如下:

/** 
 * 设置动画时长,单位是毫秒 
 */  
ValueAnimator setDuration(long duration)  
/** 
 * 获取ValueAnimator在运动时,当前运动点的值 
 */  
Object getAnimatedValue();  
/** 
 * 开始动画 
 */  
void start()  
/** 
 * 设置循环次数,设置为INFINITE表示无限循环 
 */  
void setRepeatCount(int value)  
/** 
 * 设置循环模式 
 * value取值有RESTART,REVERSE, 
 */  
void setRepeatMode(int value)  
/** 
 * 取消动画 
 */  
void cancel()  

两个监听器

  1. 前面我们讲过一个添加监听器animator.addUpdateListener,以监听动画过程中值的实时变化,其实在ValueAnimator中共有两个监听器:
/** 
 * 监听器一:监听动画变化时的实时值 
 */  
public static interface AnimatorUpdateListener {  
    void onAnimationUpdate(ValueAnimator animation);  
}  
//添加方法为:public void addUpdateListener(AnimatorUpdateListener listener)  
/** 
 * 监听器二:监听动画变化时四个状态 
 */  
public static interface AnimatorListener {  
    void onAnimationStart(Animator animation);  
    void onAnimationEnd(Animator animation);  
    void onAnimationCancel(Animator animation);  
    void onAnimationRepeat(Animator animation);  
}  
//添加方法为:public void addListener(AnimatorListener listener)   

关于监听器一:AnimatorUpdateListener就是为监听动画的实时变化状态,在onAnimationUpdate中得到了代表当前动画状态的实例.我们就不详讲了,主要讲一下下面这个AnimatorListener:
相信大家也能看出来,AnimatorListener主要监听动画播放的四个状态,start,end, cancel,repeat;当动画开始,结束,取消,重复时分别会调用上述方法.

  1. 取消监听
    上面我们讲了如何添加监听,接下来我们来看如何移除监听器:
void removeUpdateListener(AnimatorUpdateListener listener);  
void removeAllUpdateListeners();  
 /** 
  * 移除AnimatorListener 
  */  
void removeListener(AnimatorListener listener);  
void removeAllListeners();

针对AnimatorUpdateListener和AnimatorListener,每个监听都有两个方法来移除;我们就移除AnimatorListener,removeListener(AnimatorListener listener)用于在animator中移除指定的监听器,而removeAllListeners()用于移除animator中所有的AnimatorListener监听器;

  1. 其他函数
    上面我们讲了ValueAnimator中常用的一些函数,但是还有一些函数虽然不常用,但我们还是简单讲一下,他们分别是:
/** 
 * 延时多久时间开始,单位是毫秒 
 */  
public void setStartDelay(long startDelay)  
/** 
 * 完全克隆一个ValueAnimator实例,包括它所有的设置以及所有对监听器代码的处理 
 */  
public ValueAnimator clone()  

setStartDelay方法非常容易理解,让我们的动画延迟几秒再开始播放,我们重点看一下clone()方法.首先什么叫克隆,克隆就是完全一样,什么叫完全一样,就是本体有的克隆出来的实例也有,包括事件监听.不过克隆完毕后,对本体操作,却不会影响克隆体,虽然一样,但是相互独立.

ofObject

上面我们讲了ofInt()和ofFloat来定义动画,但ofInt()只能传入Integer类型的值,而ofFloat()则只能传入Float类型的值.那如果我们需要操作其他类型的变量要怎么办?其实ValueAnimator还有一个函数ofObject(),可以传进去任何类型的变量,定义如下:

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

它有两个参数,第一个是自定义的Evaluator,第二个可变长参数,Object类型的;
大家可能会疑问,为什么要强制传进去自定义的Evaluator?首先,大家知道Evaluator的作用是根据进度到值的转换过程也必须由我们来做,不然系统哪知道你要干嘛.下来我们看看这个ofObject怎么用?

ValueAnimator animator = ValueAnimator.ofObject(new CharEvaluator(),new Character('A'),new Character('Z'));  
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
    @Override  
    public void onAnimationUpdate(ValueAnimator animation) {  
        char text = (char)animation.getAnimatedValue();  
        tv.setText(String.valueOf(text));  
    }  
});  
animator.setDuration(10000);  
animator.setInterpolator(new AccelerateInterpolator());  
animator.start(); 

这里注意三点:

  1. 构造时:传入的第一个参数是我们自定义的一个CharEvaluator,关于这个类的实现后面会详细说明,这里只需要知道他是一个自定义Evaluator就行,后面两个参数是Character对象,表示动画的起始点.
    这样这个动画要做的事情就一目了然了,他无非就是要使这个动画参数从a->z.具体怎么实现,那就是我们自定义的Evaluator的事了.
  2. 看监听:通过animation.getAnimatedValue()得到的是char类型的值,然后吧字符显示到TextView,又上一条我们看到构造时传入的是Character对象,所以在动画过程通过Evaluator返回的值必然跟构造时类型一致.
  3. 插值器:我们使用的是加速插值器,随着动画的进行,速度会越来越快,这点跟我们上面的效果图是一致的,最关键的就要看我们自定义的CharEvaluator.
    我们知道在ASCII码中,每个字符都有数字跟他一一对应,字母A到字母Z之间的所有字母对应的数字区间为65到90:
    而且在程序中,我们能通过数字强转成对应的字符.
    数字转字符
char temp = (char)65;

字符转数字

char temp = 'A';  
int num = (int)temp; 

在这里就用到了数字与字符转换,主要代码如下:

public class CharEvaluator implements TypeEvaluator<Character> {  
    @Override  
    public Character evaluate(float fraction, Character startValue, Character endValue) {  
        int startInt  = (int)startValue;  
        int endInt = (int)endValue;  
        int curInt = (int)(startInt + fraction *(endInt - startInt));  
        char result = (char)curInt;  
        return result;  
    }  
}  

将字符转换成数字,然后对渐变的字母进行输出.
除了可以使用字符作为参数,同样也可以使用自定义类型.

ObjectAnimator

基本使用

上面给大家介绍了ValueAnimator,但ValueAnimator有个缺点,就是只能对数值对动画计算.我们想要哪个控件操作,需要监听动画过程,在监听中对控件操作.这样使用对补间动画而言就相对比较麻烦.
为了能让动画直接与对应的控件相关联,以是我们监听动画过程中解放出来,我们的ObjectAniamtor中所能使用的方法,在ObjectAnimator中都可以正常使用.但ObjectAnimator也重写了几个方法,比如ofInt(),ofFloat()等.我们先看看利用ObjectAnimator重写的ofFloat()等,我们先看看使用ObjectAnimator重写的ofFloat方法如何实现一个动画:(改变透明度)

ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"alpha",1,0,1);  
animator.setDuration(2000);  
animator.start(); 

这里我还是先了解构造动画方法参数

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)   
  • 第一个参数用于指定这个动画操作的是哪个控件
  • 第二个参数用于指定这个动画要操作这个控件的哪个属性
  • 第三个参数是可变长参数,这个就是跟ValueAniamtor中可变长参数的意义一样,就是指这个属性从哪变到哪.

setter函数

我们再回过头来看看改变alpha值的ObjectAnimator方法

ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"alpha",1,0,1); 

TextView控件有alpha这个属性吗?没有,不光TextView没有,就连他的父类View也没有.那它怎么来改变这个值的呢?其实,ObjectAniamtor做动画,并不是根据控件中xml的属性来改变的,而是通过指定属性所对应的set方法来改变的.比如上面指定的改变alpha的属性值,ObjectAnimator在做动画时就会到指定控件(TextView)中去找对应的setAlpha()方法来改变控件中对应的值.同样的道理,在我们
指定改变"xxxx"值的时候,ObjectAnimator也会到TextView中找对应的setXxxx()方法.而这些方法都是从View哪里继承过来的.在View中有关动画,总共有下面几组set方法.

//1、透明度:alpha  
public void setAlpha(float alpha)  
  
//2、旋转度数:rotation、rotationX、rotationY  
public void setRotation(float rotation)  表示围绕Z旋转,rotation表示旋转度数 
public void setRotationX(float rotationX)  表示围绕X轴旋转,rotationX表示旋转度数 
public void setRotationY(float rotationY)  表示围绕Y轴旋转,rotationY表示旋转度数
  
//3、平移:translationX、translationY  
public void setTranslationX(float translationX)   表示在X轴上的平移距离,以当前控件为原点,向右为正方向,参数translationX表示移动的距离。
public void setTranslationY(float translationY)  表示在Y轴上的平移距离,以当前控件为原点,向下为正方向,参数translationY表示移动的距离。 

  
//缩放:scaleX、scaleY  
public void setScaleX(float scaleX)  在X轴上缩放,scaleX表示缩放倍数
public void setScaleY(float scaleY)  在Y轴上缩放,scaleY表示缩放倍数 

现在我们可以通过使用ObjectAniamtor方法直接对View属性做动画设置.在开始使用这些函数之前,我们先来做一个总结:

  1. 要使用ObjectAnimator来构造动画,要操作的控件中,必须存在对应的属性的set方法
  2. setter方法的命名必须以骆驼拼写法命名,即set后每个首字母大写,其余字母小写,即类似于setPropertyName所对应的属性为propertyName

ObjectAnimator动画原理

我们先来看一张图:

image

在这张图中我们将valueAnimator与ObjectAnimator的流程相对比,我们可以看出前三步两者的步骤一致,区别就在于最后一步.那么我们在对比一下两者的使用过程即可知道在ObjectAnimator的最后一步,为我们做了什么.好了不兜圈子了,接下来让我们一起看看他究竟是怎样替我们完成View属性实时操控.首先我们在构造Animator时获得了一个String的属性名,他可以利用这个属性名假设是xxxx,通过反射机制在控件中找到对应的setXxxx方法然后将Evaluator获得的数值传入该方法以改变控件的属性,达到动画的效果.
根据上面的叙述,这里有几个注意事项:

  1. 拼接set函数的方法:上面我们也说了是首先是强制将属性的第一个字母大写,然后与set拼写,就是对应的set函数的名字,注意,只是强制将属性的第一个字母大写,后面的部分时保持不变的.反过来,如果我们函数命名为setScalePointX(float)那我们在写属性时可以写成'scalePoint'或者ScalePoint都可以,即第一个字母大小写可以随意,但是后面的必须与原方法名保持一致
  2. 如何确定函数的参数类型:上面我们知道如何找到对应的函数名,那对应的参数方法应该如何确定呢?我们在讲ValueAnimator时候说过,在动画过程中产生的数值与构造时传入的值的类型一致,而当前动画产生的值都是在Evaluator这一步产生的,所以ObjectAnimator的动画中产生的数值类型也是与构造时的类型一样.
  3. 调用set函数以后怎么办:从ObjectAnimator的流程可以看到,到这一步已经结束了,关于set函数内部如何操作还需要我们自己操作.我们来看看View中的setScaleY是怎么实现的:
public void setScaleY(float scaleY) {  
    ensureTransformationInfo();  
    final TransformationInfo info = mTransformationInfo;  
    if (info.mScaleY != scaleY) {  
        invalidateParentCaches();  
        // Double-invalidation is necessary to capture view's old and new areas  
        invalidate(false);  
        info.mScaleY = scaleY;  
        info.mMatrixDirty = true;  
        mPrivateFlags |= DRAWN; // force another invalidation with the new orientation  
        invalidate(false);  
    }  
}  

上面代码就做了两件事:第一步,将新的参数赋值给控件属性,第二部调用Invalidate()强制重绘.
注意:
仅且仅当我们只给动画设置一个值时,我们需要实现属性的get方法,因为此时程序才会调用属性对应的get函数来得到动画初始值。如果动画没有初始值,那么就会使用系统默认值。

常用函数

/** 
 * 设置动画时长,单位是毫秒 
 */  
ValueAnimator setDuration(long duration)  
/** 
 * 获取ValueAnimator在运动时,当前运动点的值 
 */  
Object getAnimatedValue();  
/** 
 * 开始动画 
 */  
void start()  
/** 
 * 设置循环次数,设置为INFINITE表示无限循环 
 */  
void setRepeatCount(int value)  
/** 
 * 设置循环模式 
 * value取值有RESTART,REVERSE, 
 */  
void setRepeatMode(int value)  
/** 
 * 取消动画 
 */  
void cancel()  

监听器

/** 
 * 监听器一:监听动画变化时的实时值 
 */  
public static interface AnimatorUpdateListener {  
    void onAnimationUpdate(ValueAnimator animation);  
}  
//添加方法为:public void addUpdateListener(AnimatorUpdateListener listener)  
/** 
 * 监听器二:监听动画变化时四个状态 
 */  
public static interface AnimatorListener {  
    void onAnimationStart(Animator animation);  
    void onAnimationEnd(Animator animation);  
    void onAnimationCancel(Animator animation);  
    void onAnimationRepeat(Animator animation);  
}  
//添加方法为:public void addListener(AnimatorListener listener) 

该内容与上述ValueAnimator一致,没有必要重复介绍.

属性动画的基础使用我们就认识到这里,希望能帮到大家

文章来自:
http://blog.csdn.net/harvic880925/article/details/50598322

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 【Android 动画】 动画分类补间动画(Tween动画)帧动画(Frame 动画)属性动画(Property ...
    Rtia阅读 6,266评论 1 38
  • Animation Animation类是所有动画(scale、alpha、translate、rotate)的基...
    四月一号阅读 1,938评论 0 10
  • 1 背景 不能只分析源码呀,分析的同时也要整理归纳基础知识,刚好有人微博私信让全面说说Android的动画,所以今...
    未聞椛洺阅读 2,770评论 0 10
  • 大致介绍 属性动画系统是一个允许你对几乎任何事情进行的动画操作的健壮的框架。你可以定义一个属性动画来改变任何对象的...
    已迁至知乎_此不再维护阅读 1,162评论 0 4
  • 小城已经很老旧了,但它依旧延续着老旧的模样。在这里你似乎找不到任何一处地方,或是任何一件东西与所谓的现代有丝毫关系...
    d97ff08d6b12阅读 475评论 5 7