美文网首页
认识动画(3)-属性动画(Property Animator)

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

作者: 小庄bb | 来源:发表于2018-03-14 16:20 被阅读52次

    概述

    在认识动画第一节我们就提到了这个属性动画(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

    相关文章

      网友评论

          本文标题:认识动画(3)-属性动画(Property Animator)

          本文链接:https://www.haomeiwen.com/subject/tboxqftx.html