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

一、摘要

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

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

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

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

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

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

Android基础夯实–重温动画(五)之属性动画 ObjectAnimator详解

二、ValueAnimator

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

2.1 初识ValueAnimator

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

我们都知道,在使用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() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {// 我们来检查一下这个方法会调用多少次Log.i("TAG", "curValue is " + animation.getAnimatedValue());// 在ValueAnimator变化的过程中更新控件的透明度mBinding.image.setAlpha((float)alphaAnimator.getAnimatedValue());}
});

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

通过例子,我们可以大概总结使用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,我们可以轻松实现颜色渐变效果:

代码:

ValueAnimator animator = ValueAnimator.ofInt(0xffff00ff, 0xffffff00, 0xffff00ff);
animator.setEvaluator(new ArgbEvaluator());
animator.setDuration(3000);
animator.start();
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic 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,实现图片的放大和渐变过程,先看效果图:

首先我们看到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就是我们传入的初始值和结束值@Overridepublic ValueObject evaluate(float fraction, ValueObject startValue, ValueObject endValue) {// 计算某个时刻的alpha值和scale值。类似速度公式Vt = V0 + atfloat 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值为1ValueObject 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() {@Overridepublic 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() {@Overridepublic void onAnimationStart(Animator animation) {Log.i("TAG", "start");}@Overridepublic void onAnimationEnd(Animator animation) {Log.i("TAG", "end");}@Overridepublic void onAnimationCancel(Animator animation) {Log.i("TAG", "cancel");}@Overridepublic void onAnimationRepeat(Animator animation) {Log.i("TAG", "repeat");}
});
// AnimatorUpdateListener是监听ValueAnimaitor的值不断变化的过程
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic 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达到终点,先上效果图让大家比较直观了解:

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

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

那么代码也自然出来了:

class MyInterpolator extends BaseInterpolator {@Overridepublic 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在属性动画中也是起着重要的一环。先看一张图:

我们可以看到,当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就是在FloatEvalutor的基础之上加了200个像素,而我自定义的Evaluator也是修改了以下FloatEvaluator的代码:

class MyEvaluator implements TypeEvaluator<Float> {@Overridepublic 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基础夯实--重温动画(四)之属性动画 ValueAnimator详解相关推荐

  1. Android基础入门教程——2.3.1 TextView(文本框)详解

    Android基础入门教程--2.3.1 TextView(文本框)详解 标签(空格分隔): Android基础入门教程 本节引言: 学习完Android中的六大布局,从本节开始我们来一个个讲解And ...

  2. Android 基础知识4-3.8 SeekBar(拖动条)详解

    一.简介 拖动条类似进度条,不同的是用户可以控制,比如,应用程序中用户可以对音效进行控制,这就可以使用拖动条来实现.由于拖动条可以被用户控制,所以需要对其进行事件监听,这就需要实现SeekBar.on ...

  3. Android 基础知识4-2.11 AbsoluteLayout(绝对布局)详解

    一.引言 Android中的五大布局,在本节中会讲解第六个布局AbsoluteLayout(绝对布局),之所以把这个放到最后,是因为AbsoluteLayout(绝对布局)我们基本上都是不会使用.当然 ...

  4. Android 基础知识4-2.8 TableLayout(表格布局)详解

    一.TableLayout的概述 表格布局是以行数和列数来确定位置进行排列.就像一间教室,确定好行数与列数就能让同学有序入座. 注意:我们需要先添加<TableRow容器,每添加一个就会多一行, ...

  5. Android源码解析(一)动画篇-- Animator属性动画系统

    Android源码解析-动画篇 Android源码解析(一)动画篇-- Animator属性动画系统 Android源码解析(二)动画篇-- ObjectAnimator Android在3.0版本中 ...

  6. Android动画之Property属性动画

    2019独角兽企业重金招聘Python工程师标准>>> 为什么引入属性动画? 大家都知道Android常见的动画有tween动画,frame动画.但是随着人们对动画的要求不断提高, ...

  7. Android动画完全解析--属性动画

    一.概述 上篇博客介绍了View动画的简单使用和基本工作原理原理,这篇来学习下属性动画.和View动画不同的是,属性动画不再简单的使用平移.旋转.缩放.透明度这4种变换,代替它们的是ValueAnim ...

  8. Android动画框架(二)----属性动画

    转载请注明出处:http://blog.csdn.net/fishle123/article/details/50705928 Android提供三种形式动画:视图动画,帧动画,属性动画.其中属性动画 ...

  9. [python opencv 计算机视觉零基础到实战] 四、了解色彩空间及其详解

    一.学习目标 了解什么是色彩空间 了解opencv中色彩空间的转换 目录 [python opencv 计算机视觉零基础到实战] 一.opencv的helloworld [[python opencv ...

  10. moviepy音视频剪辑:视频剪辑基类VideoClip的属性及方法详解

    ☞ ░ 前往老猿Python博文目录 ░ 一.概述 在<moviepy音视频剪辑:moviepy中的剪辑基类Clip详解>和<moviepy音视频剪辑:moviepy中的剪辑基类Cl ...

最新文章

  1. 2021年大数据Flink(三十七):​​​​​​​Table与SQL ​​​​​​案例四
  2. SAP中各种分摊分配方法
  3. android setContentView处理流程
  4. SAP BCP 超时机制的实现
  5. 三十四 Python分布式爬虫打造搜索引擎Scrapy精讲—scrapy信号详解
  6. Luogu P4774 / LOJ2721 【[NOI2018]屠龙勇士】
  7. 毕业论文word排版设置
  8. aspx中如何加入javascript
  9. 专访阿里云技术掌舵人蒋江伟:中台是企业的未来
  10. 4个月掌握核心技术 成为云计算行业专家
  11. 智能水表营收管理系统
  12. 雷云云服务器文件夹,配置文件云存储 雷蛇Synapse2.0_雷蛇鼠标_键鼠评测-中关村在线...
  13. Day16_IO框架1(File类, IO流, 字节流字符流, IO异常, Properties)
  14. 仿 Drools 决策表 Groovy 实现
  15. php电商开源框架,Sylius 开源PHP电商解决方案
  16. 【rose】rose框架学习总结
  17. Android官方开发相关下载资源一揽子大汇总
  18. React之SCU(性能优化篇)
  19. 2015-8-25股市大跌
  20. RSA非对称加密,前台vue加密,后台java解密

热门文章

  1. mysql省市县表设计_表设计(省市县)
  2. 服务器动态文档技术——CGI、Servlet技术、动态页面Web
  3. 编译一个.cpp文件的过程
  4. 在.cpp文件中调用.c文件中的函数
  5. 计算机科学环境保护,计算机在环保领域中的应用
  6. 阿里语音识别(语音转文字)java调用全程手把手详解-适合中小学生快速上手
  7. 如何做好软件项目管理工作?
  8. Ti芯片 bq76940锂电池管理系统BMS资料,源程序+PDF原理图,主控stm32f030。
  9. 昨天熬夜整了一波,值了!
  10. Ubuntu返回上级目录快捷键