美文网首页Android 动画Android知识
Android基础夯实--重温动画(四)之属性动画 ValueA

Android基础夯实--重温动画(四)之属性动画 ValueA

作者: 08dc70a40fb1 | 来源:发表于2017-03-27 18:34 被阅读1376次

    宝剑锋从磨砺出,梅花香自苦寒来;千淘万漉虽辛苦,吹尽狂沙始到金; 长风破浪会有时,直挂云帆济沧海

    欢迎大家想看更多关于Android基础夯实系列博文,请移步到我的博客:
    Ryane's Blog

    一、摘要

    Animator类作为属性动画的基类,它是一个抽象类,它提供了实现动画的基本架构,但是我们不能直接使用它,因为它只是提供了最基本的的实现动画的方法,只有让它的子类继承它并进行相应扩展之后,我们才会使用它实现动画。在属性动画中,Animator包括了ValueAnimator、ObjectAnimator和AnimatorSet三个子类,下面给大家详解ValueAnimator。

    如果你想了解更权威的解释,可以查看官方文档:Property Animation

    本文主要对ValueAnimator做介绍,如果大家有兴趣,可以继续阅读本动画系列其他相关文章,作者也在不断更新完善相关内容,希望大家可以指出有误之处。

    Android基础夯实--重温动画(一)之Tween Animation

    Android基础夯实--重温动画(二)之Frame Animation

    Android基础夯实--重温动画(三)之初识Property Animation

    二、ValueAnimator

    ValueAnimator,就是针对值的,也就是说ValueAnimator不会对控件进行任何操作,而是控制值的变化,然后我们监听这个值的变化过程,自己来控制控件的变化。什么意思呢?就像我们上面1.2中的例子,使用属性动画来控制TextView的位移,我们在初始化ValueAnimator时,会设置一个初始值和结束的值,例子我用这两个值来控制TextView在y轴上的位置,然后设置监听器,监听初始值变化到结束值的过程,在不断变化过程中,通过调用TextView的layout方法来不断更新TextView的位置,从而实现位移动画。

    2.1 初识ValueAnimator

    先上一个例子,实现图片的渐变过程:

    Alpha

    我们都知道,在使用Tween Animation时是非常容易实现的,使用AlphaAnimation就可以实现,假如我们用属性动画的话,怎么实现呢?也是非常简单,布局代码就不贴了,看看使用ValueAnimator如何简单快捷地实现渐变动画。

    // 第一步,创建一个ValueAnimator。直接调用ValueAnimator.ofFloat来初始化,设置开始值和结束值
    final ValueAnimator alphaAnimator = ValueAnimator.ofFloat(1, 0);
    // 设置变化时长
    alphaAnimator.setDuration(1000);
    alphaAnimator.start();
    
    // 第二步,ValueAnimator设置监听器
    alphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            // 我们来检查一下这个方法会调用多少次
            Log.i("TAG", "curValue is " + animation.getAnimatedValue());
            // 在ValueAnimator变化的过程中更新控件的透明度
            mBinding.image.setAlpha((float)alphaAnimator.getAnimatedValue());
        }
    });
    

    而我们在监听器内设置的Log的结果如下,我们可以看到onAnimationUpdate方法被不断地执行,输出值不断由我们设置的初始值变化到我们设置的结束值,所以这个值的变化过程正是我们需要让控件变化的过程。

    Log结果

    通过例子,我们可以大概总结使用ValueAnimator的两个主要过程:

    (1). 初始化ValueAnimator,并设置初始值和结束值,还有动画的时间,然后start。

    (2). 给ValueAnimator设置监听器,通过getAnimatedValue()拿到变化值,然后我们手动更新控件的变化。

    2.2 深入了解ValueAnimator

    由于ValueAnimator里面的方法确实不少,所以我们从上面的例子入手,从常用到不常用地讲解ValueAnimator的API,毕竟只要我们掌握了最常用的知识点之后,在我们需要时再去深入了解不常用的知识点,我觉得是个最有效率的学习方式。

    由上面的Demo代码的第一步我们可以看到,首先我们需要获取到一个ValueAnimator实例,按照我们的常规思维,我们都会通过new一个对象出来,以代码为例,我们是通过ofFloat方法来获取一个实例对象,那么我们的第一个疑问就是关于构造函数的,到底ValueAnimator有没有构造函数呢?如果有,为什么不通过构造函数来初始化呢?

    答案是有的,至于为什么,我们一探究竟。

    2.2.1构造函数

    • ValueAnimator():创建一个ValueAnimator对象。

    ValueAnimator确实有它的构造函数,但是官方文档不建议我们直接使用它,因为在内部实现的时候才会用到它。之所以不需要用到它,是因为API给我们封装了一系列的的方法来获取实例对象。

    2.2.2实例化对象的方法

    • ValueAnimator ofInt (int... values):返回一个int型变化的ValueAnimator。
    • ValueAnimator ofFloat (float... values):返回一个float型变化的ValueAnimator。
    • ValueAnimator ofObject (TypeEvaluator evaluator, Object... values):返回一个object型变化的ValueAnimator。
    • ValueAnimator ofArgb (int... values):返回一个颜色值变化的ValueAnimator,API LEVEL 21引入。
    • ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values):返回一个PropertyValuesHolder型变化的ValueAnimator,在ObjectAnimator再详说。

    为什么我们需要通过这些方法来实例化对象呢?这是因为这些方法内部都对实例化对象进行了封装,我们以ofInt为例看一下它的内部实现,它内部其实还是通过new的方式来实例化,然后通过设置一些属性,然后返回这个ValueAnimator对象。

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

    ofArgb的使用

    在ValueAnimator中的ofArgb()可以帮助我们实现颜色的渐变效果,Google在API LEVEL 21之后增加了这个方法ofArgb()。通过这个方法我们更容易地实现颜色演变,通过ofArgb和ArgbEvaluator,我们可以轻松实现颜色渐变效果:

    ofInt

    代码:

    ValueAnimator animator = ValueAnimator.ofInt(0xffff00ff, 0xffffff00, 0xffff00ff);
    animator.setEvaluator(new ArgbEvaluator());
    animator.setDuration(3000);
    animator.start();
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mBinding.image.setBackgroundColor((Integer) animation.getAnimatedValue());
        }
    });
    

    ofObject的使用

    ofObject方法是什么意思呢?我们都知道ofInt和ofFloat都是针对Int值和Float值的变化,但是,我们只能控制一个值的变化,但是当我们需要实现多值变化时,它们就不再满足我们的需求。例如我们需要同时实现位移、透明度变化等动画,这里需要设置两个属性值的变化,所以如果我们只有一个初始值是不行的,因为两个属性的初始值和结束值不一样,那么我们就可以将两个属性值封装到一个对象里面,那么初始值的object和结束值的object就可以包含两个属性不同的初始值和结束值了。

    下面是一个对ValueAnimator ofObject (TypeEvaluator evaluator, Object... values)方法具体使用的小Demo,实现图片的放大和渐变过程,先看效果图:

    image

    首先我们看到ofObject的参数里面有一个TypeEvaluator和一个Object型可变参数,一般传入一个初始值和结束值,首先TypeEvaluator就是一个计算值的工具,API提供有现成的(下面详说),也可以自己实现(这里为了给大家知道是个什么东西,就自己实现);然后Obejct型,我们自己写一个类代替Obejct型。

    因为我们有两个动画,包括放大和透明度变化,我们定义一个叫ValueObject的类,里面就包含两个属性,代码也非常简单:

    class ValueObject {
        float alphaValue;   //透明度的值
        float scaleValue;   //伸缩变化的值
    
        public ValueObject(float alphaValue, float scaleValue) {
            this.alphaValue = alphaValue;
            this.scaleValue = scaleValue;
        }
    }
    

    然后,我们就需要自定义TypeEvaluator了,因为TypeEvaluator是一个接口,我们就写一个名叫MyEvaluator的类,它实现了TypeEvaluator的接口,传入我们的值类型为ValueObject,然后重写evaluate方法(TypeEvaluator接口只有这个方法需要实现),代码也很简单:

    class MyEvaluator implements TypeEvaluator<ValueObject> {
    
        // 属性动画封装了一个因子fraction,我们设置动画时需要setDuration(xxxx),例如时间为1000ms,那么当到达100ms时,fraction就为0.1
        // fraction也就是当前时间占总时间的百分比,startValue和endValue就是我们传入的初始值和结束值
        @Override
        public ValueObject evaluate(float fraction, ValueObject startValue, ValueObject endValue) {
            // 计算某个时刻的alpha值和scale值。类似速度公式Vt = V0 + at
            float nowAlphaValue = startValue.alphaValue + (endValue.alphaValue - startValue.alphaValue) * fraction;
            float nowScaleValue = startValue.scaleValue + (endValue.scaleValue - startValue.scaleValue) * fraction;
            return new ValueObject(nowAlphaValue, nowScaleValue);
        }
    }
    

    这两个类我们都实现了,那么动画就很简单了:

    public void objectAnimation() {
        // 初始alpha值为1,scale值为1
        ValueObject startObjectVal = new ValueObject(1f, 1f);
        // 结束alpha值为0,scale值为2,相当于透明度变为0,尺寸放大到2倍
        ValueObject endObjectVal = new ValueObject(0f, 2f);
        MyEvaluator myEvaluator = new MyEvaluator();
    
        final ValueAnimator animator = ValueAnimator.ofObject(myEvaluator, startObjectVal, endObjectVal);
        animator.setDuration(3000);
        animator.start();
    
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mBinding.image.setAlpha(((ValueObject) animation.getAnimatedValue()).alphaValue);
                mBinding.image.setScaleType(ImageView.ScaleType.CENTER);
                mBinding.image.setScaleX(((ValueObject) animation.getAnimatedValue()).scaleValue);
                mBinding.image.setScaleY(((ValueObject) animation.getAnimatedValue()).scaleValue);
            }
        });
    }
    

    2.2.3常用方法

    • void addUpdateListener(ValueAnimator.AnimatorUpdateListener listener):添加值变化监听器。主要监听值变化,实现动画。
    • void addUpdateListener(AnimatorUpdateListener listener):添加动画状态监听器。重写动画开始、结束、取消、重复四个方法,监听不同状态。
    • void cancel (): 取消动画。
    • void end ():让动画到达最后一帧。
    • void start():开始动画。
    • void pause():暂停动画。
    • void resume():继续动画。
    • void reverse ():反向播放动画。
    • boolean isRunning():是否在运行中。
    • boolean isStarted():是否已经开始。

    2.2.4属性相关的方法

    • void setCurrentFraction(float fraction):设置当前时间因子。即时间到达的百分比。

    • float getAnimatedFraction():获取当前时间因子。即时间到达的百分比。

    • void setCurrentPlayTime (long playTime):设置当前的时间,取值为0-duration,单位毫秒。

    • long getCurrentPlayTime ():获取当前的时间,单位毫秒。

    • ValueAnimator setDuration (long duration):设置动画总时长,单位毫秒。

    • long getDuration ():获取动画总时长,单位毫秒。

    • void setFrameDelay (long frameDelay):设置每一帧之间间隔多少毫秒。

    • long getFrameDelay ():获取每一帧之间间隔多少毫秒。

    • void setInterpolator (TimeInterpolator value):设置动画的Interpolator,和View Animation的Interpolator通用。

    • TimeInterpolator getInterpolator ():获取当前使用的插值器。

    • void setRepeatCount(int value):设置重复次数。

    • int getRepeatCount():获取重复次数。

    • void setRepeatMode(int value):设置重复模式。有RESTART和REVERSE两种。

    • int getRepeatMode():获取重复模式。

    • void setStartDelay(long startDelay):设置开始前延迟毫秒数。

    • long getStartDelay():获取开始前延迟毫秒数。

    • void getAnimatedValue():获取计算出来的当前属性值。

    • getAnimatedValue(String propertyName):获取计算出来的当前某个属性的值。

    • void setEvaluator(TypeEvaluator value):设置求值器。

    • void setFloatValues(float... values):设置Float型变化值,一般设置初始值和结束值,当然你也可以设置中间值,因为这是一个可变参数,长度可变。

    • void setIntValues(int... values):设置Int型变化值,一般设置初始值和结束值,当然你也可以设置中间值,因为这是一个可变参数,长度可变。

    • setObjectValues(Object... values):设置Object型变化值,一般设置初始值和结束值,当然你也可以设置中间值,因为这是一个可变参数,长度可变。

    2.2.5监听器

    ValueAnimator有两个监听器,一个是AnimatorListener,一个AnimatorUpdateListener,通过代码我们查看它们的区别。 AnimatorListener主要是用来监听动画不同状态的监听器,从代码中我们可以看到它有四种不同的状态,当我们需要在不同状态中进行不同操作时,我们可以实现这个监听器。AnimatorUpdateListener是监听ValueAnimaitor的值不断变化的过程,通常使用这个监听器更新控件状态,实现动画过程。

    // AnimatorListener主要是用来监听动画不同状态的监听器
    animator.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animation) {
            Log.i("TAG", "start");
        }
    
        @Override
        public void onAnimationEnd(Animator animation) {
            Log.i("TAG", "end");
        }
    
        @Override
        public void onAnimationCancel(Animator animation) {
            Log.i("TAG", "cancel");
        }
    
        @Override
        public void onAnimationRepeat(Animator animation) {
            Log.i("TAG", "repeat");
        }
    });
    
    // AnimatorUpdateListener是监听ValueAnimaitor的值不断变化的过程
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            Log.i("TAG", "curVal:" + animation.getAnimatedValue());
        }
    });
    
    

    三、Interpolator

    Interpolator,译名插值器,在我的意思里就是加速器,即这是一个改变我们动画速率的一个工具,可以实现加速、减速、匀速等这些特效。我们在Android基础夯实--重温动画(一)之Tween Animation第五部分讲了Android提供给我们使用的插值器,其实属性动画和视图动画是共用一套Interpolator的。在上面我们讲到,在属性动画中,我们可以通过setInterpolator (TimeInterpolator value)来给我们的动画增加一个插值器,传入参数是TimeInterpolator,通过查阅API,我们可以知道,TimeInterpolator是一个接口。我们再来看看它和我们常用的插值器的关系。

    我们常用的插值器,如AccelerateDecelerateInterpolator,AccelerateInterpolator, AnticipateInterpolator,AnticipateOvershootInterpolator等,它们的父类是BaseInterpolator。

    而BaseInterpolator是实现了Interpolator,而Interpolator则是继承TimeInterpolator接口。所以究其根源,我们常用的插值器和属性动画使用的TimeInterpolator其实是同一个东西。

    既然了解了它们是同一个东西,那么我们就需要了解怎么来实现一个自己的Interpolator了,一般我们只要继承BaseInterpolator,并实现它的getInterpolation(float input)方法就行了。

    举个例子,Android提供给我们的LinearInterpolator(这是一个匀速插值器)中,它的getInterpolation是这样的:

    public float getInterpolation(float input) {
        return input;
    }
    

    首先我们看一下参数input是什么,input表示当前动画的进度,它的取值范围是0-1,0代表起点,1代表终点,随着动画的播放,input从0到1逐渐变大;而返回值就是指当前的实际进度,听起来有点拗口,我们可以这么想,例如本来当input为0.1的时候,我们返回值如果大于0.1,那么就说明我们从0到0.1这个阶段是一个加速阶段,如果小于0.1,就说明这是一个减速过程。可以看到LinearInterpolator是直接把input返回,可以知道这是一个匀速的过程。

    再来看看AccelerateDecelerateInterpolator,这是开始和结束速度慢,中间部分加速。我们来看一下它的getInterpolation函数:

    public float getInterpolation(float input) {
        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
    }
    

    可以看到这就是一个余弦公式,因为0-1这个时间内,刚开始和结束前这两个部分斜率是比较低的,所以速度会比较慢,但是中间部分斜率明显变大,所以中间部分呈现加速状态。

    余弦公式

    经过这两个例子,我们大概知道,当我们需要实现一个Interpolator时,只需要继承BaseInterpolator,并实现它的getInterpolation(float input)方法就行了,举个例子:实现一个0-0.25秒内到达3/4,0.25-0.75秒内从3/4退回1/4,最后0.25秒内从1/4达到终点,先上效果图让大家比较直观了解:

    Demo

    所以我们可以很清楚的列出关系式:

    关系式

    那么在getInterpolation中,对应根据input列出算法:

    算法

    那么代码也自然出来了:

    class MyInterpolator extends BaseInterpolator {
        @Override
        public float getInterpolation(float input) {
            if (input <= 0.25) {
                return 3 * input;
            } else if (input <= 0.75) {
                return (1 - input);
            } else {
                return 3 * input - 2;
            }
        }
    }
    

    四、Evaluator

    Evaluator在属性动画中也是起着重要的一环。先看一张图:

    Evaluator

    我们可以看到,当Interpolator返回了当前进度滞后,Evaluator就会根据进度来计算出确定的值,以供监听器返回,所以我们就可以知道了,Evaluator其实就是一个根据我们需求而制作的一个计算器。

    其实在上面的例子我已经简单地教大家自定义了一个Evaluator,在属性动画中,Android 也为我们提供了很多的Evaluator,例如IntEvaluator,FloatEvaluator等,我们可以先看一下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方法,在返回值中是一条公式,就是根据开始值和结束值,当前进度,计算结果,并返回,这条公式也是非常简单,这里就不详说了。但是实际开发中,有时候原生的Evaluator不适合我们使用的时候,我们就需要自定义一个Evaluator,正如我上面的例子中用到的,当我们使用了自定义的Object作为初始值和结束值时,我们就需要定义一个自己的Evaluator。下面举一个为了自定义而自定义的Evaluator:

    Evaluator

    由图可知,自定义的Evaluator就是在FloatEvalutor的基础之上加了200个像素,而我自定义的Evaluator也是修改了以下FloatEvaluator的代码:

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

    这是FloatEvaluator的代码:

    public class FloatEvaluator implements TypeEvaluator<Number> {
        public Float evaluate(float fraction, Number startValue, Number endValue) {
            float startFloat = startValue.floatValue();
            return startFloat + fraction * (endValue.floatValue() - startFloat);
        }
    }
    

    所以到这里大家也可以大概了解了怎么自定义Evaluator,非常的简单,实现TypeEvaluator接口,并传入一个类型,也就是初始值和结束值的类型,然后重写evaluate方法,根据当前进度fraction来计算当前的返回值即可。

    五、 总结

    总体来说,ValueAnimator并不会很难,只要我们掌握了Animator的初始化、初始值、结束值、fraction、Evaluator、监听器的概念,那么我们基本掌握了ValueAnimator的使用,当然,伴随着我们的重复使用、加深理解,当然我们离熟悉掌握ValueAnimator也不远了。当然Animator中除了ValueAnimator以外,还有ObjectAnimator,这也是一个非常重要的概念,下一篇,我给大家带来ObjectAnimator的详解。

    相关文章

      网友评论

        本文标题:Android基础夯实--重温动画(四)之属性动画 ValueA

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