Android动画篇(三)—— 属性动画ValueAnimator的使用

前言:厚积才能薄发,未来只属于为梦想而奋斗的人们,今天的你决定未来的自己。
一、概述 大家可能觉得补间动画和逐帧动画已经很全面了,为什么还要引入Property Animator(属性动画)呢?有关View Animation(视图动画)和Property Animator(属性动画)的分类区别可以参考我的《Android动画篇(一)—— alpha、scale、translate、rotate、set的xml属性及用法》
1.为什么引入Property Animator(属性动画)
(1)Property Animator(属性动画)能实现补间动画无法实现的功能
大家都知道补间动画和逐帧动画统称为View Animation,也就是说两个动画只能对派生子View的控件实例起作用,而Property Animator则不同,从名字可以看出属性动画是作用于控件属性的,正因为属性动画能够针对控件某一属性来做动画,所以他能够单独改变该控件属性的值。比如颜色,实现控件在一定时间内颜色渐变,这就是Property Animator(属性动画)能实现补间动画无法实现的功能的最重要原因。
(2)View Animation(视图动画)仅能对指定的控件做动画,Property Animator(属性动画)能通过改变控件的某个属性值做动画
假设我将一个按钮从左上角通过补间动画移动到右下角,在移动过程中和移动后这个按钮都不会响应点击事件的。为什么呢?
因为补间动画仅仅改变的是控件的显示位置而已,并没有改变控件本身的值,View Animation动画是通过Parent View实现的,在view被draw时Parent View改变它的绘制参数,这样view的大小位置角度等虽然变化了,但是view的实际属性并没有改变。所以有效区域还是动画之前的区域,我们看到的效果仅仅是系统作用在按钮上的显示效果,利用动画将控件从原来的位置移动到右下角,但是控件的内部值是没有任何变化的。所以点击区域还是原来的点击区域(下面会实例验证)。
2、补间动画点击区域问题
下面我们就用TranslateAnimation来做移动动画的例子,看他的点击区域是否变化。
【Android动画篇(三)—— 属性动画ValueAnimator的使用】首先我给TextView添加响应事件,点击TextView时会弹出Toast,然后我点击开始动画时,TextView向右下角移动
从下图可以看出,移动前TextView可以点击弹出Toast,移动后点击TextView则没有弹出Toast,相反点击TextView原来的区域则可以弹出Toast。这就说明:补间动画只能对控件做动画,并不能改变控件内部的属性值。
Android动画篇(三)—— 属性动画ValueAnimator的使用
文章图片

这个效果的代码我就不贴出来了,具体可以参考《 Android动画篇(二)—— 代码生成alpha、scale、translate、rotate、set及插值器动画》
二、ValueAnimator的使用 Property Animator(属性动画)包括ValueAnimator和ObjectAnimator,这里主要讲讲ValueAnimator的主要用法,ValueAnimator是针对值的,这个不会对控件做任何操作,我们可以设置它在某个值运动到另一个值,通过监听它的值变化来操作控件。
1、ValueAnimator的简单使用
(1)创建ValueAnimator实例
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 200); valueAnimator.setDuration(2000); valueAnimator.start();

这里我们利用ValueAnimator.ofInt创建了一个0到200的动画运算,动画时长2s,然后开始动画,从这段代码看出ValueAnimator没有跟任何控件相关,也正好说明ValueAnimator对值做动画运算,而不是针对控件,我们要通过监听ValueAnimator的动画过程来自己多控件做操作。
(2)添加监听
上面三行代码实现了值的动画变化,我们来添加监听:
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int value = https://www.it610.com/article/(int) animation.getAnimatedValue(); Log.e(TAG,"value:" + value); } });

我们通过addUpdateListener添加监听,从监听动画运算的回传结果中,是表示当前ValueAnimator实例,我们通过animation.getAnimatedValue()得到当前值,打印出来看看:
Android动画篇(三)—— 属性动画ValueAnimator的使用
文章图片

这就是ValueAnimator的功能,对指定区间值做动画运算,我们通过运算过程做监听来操作自己的控件。
核心就两点:
A:ValueAnimator只负责对指定区间的数值进行动画运算;
B:我们需要对运算过程做监听,然后自己对控件进行动画操作。
2、ValueAnimator的使用实例
我们使用ValueAnimator实现上面的动画效果,将控件从(0,0)位移到(400,400)的位置,得到的结果却是,点击开启动画按钮后,TextView从(0,0)2秒内移动到(400,400),运动前后TextView都可以点击
效果如下:
Android动画篇(三)—— 属性动画ValueAnimator的使用
文章图片

代码如下:
xml:

java:
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private static final String TAG = MainActivity.class.getSimpleName(); private TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = findViewById(R.id.tv); mTextView.setOnClickListener(this); findViewById(R.id.tv_start_anim).setOnClickListener(this); }@Override public void onClick(View v) { switch (v.getId()) { case R.id.tv: Toast.makeText(this, "click", Toast.LENGTH_SHORT).show(); break; case R.id.tv_start_anim: ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 400); valueAnimator.setDuration(2000); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int value = https://www.it610.com/article/(int) animation.getAnimatedValue(); mTextView.layout(value, value, value + mTextView.getWidth(), value + mTextView.getHeight()); Log.e(TAG,"value:" + value); } }); valueAnimator.start(); break; default: break; } } }

在上面的代码中,监听的时候通过layout函数改变TextView的位置,注意,我们是通过layout函数改变控件的位置的,layout函数改变控件位置这个是永久性的,即通过更改控件的left,top,right,bottom四个点的坐标来更改控件的位置,更改位置后,控件是可以响应事件的。
三、ValueAnimator的常用方法 1.ofInt和ofFloat
与onInt同样功能的还有一个函数,叫ofFloat,具体声明如下:
public static ValueAnimator ofInt(int... values) public static ValueAnimator ofFloat(float... values)

可以看出,它们的参数都是可变长参数,我们可以传入任何数量的值,传进去的值列表表示动画的变化范围,比如ofInt(0,100,50)表示从数值0变化到100,再从100变化到50。ofInt与ofFloat的唯一区别就是参数值类型不同,ofInt需要传入int类型的参数,ofFloat则需要传入float类型的参数
ofFloat的使用:
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 400f, 200f, 600f); valueAnimator.setDuration(2000); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { Float v = (Float) animation.getAnimatedValue(); int value = https://www.it610.com/article/v.intValue(); mTextView.layout(value, value, value + mTextView.getWidth(), value + mTextView.getHeight()); Log.e(TAG,"value:" + value); } }); valueAnimator.start();

效果如下图:
Android动画篇(三)—— 属性动画ValueAnimator的使用
文章图片

点击开启动画后,TextView先向右下方运动,然后回来,然后再从向右下方运动。
这里我们使用了ofFloat(0f, 400f, 200f, 600f)构造了一个比较复杂的动画渐变,值从0到400,再回到200,然后再到600的变化。
在监听时,首先要得到动画的值:
Float v = (Float) animation.getAnimatedValue();

在ofFloatz中通过函数getAnimatedValue()获得的值为什么要强转为Float类型,先看下该函数的声明:
public Object getAnimatedValue()

它的返回值是一个Object的原始类型,那我们知道它要转换成什么类型吗?注意,我们在设定动画初始值时使用的是ofFloat()函数,所以每个值的类型必定是float,那么从返回值取出的值的类型也是float,ofInt()函数同理。
2、其他常用方法
  • setStartDelay(long startDelay)延时开始动画,单位毫秒
  • setDuration(long duration)设置动画时长,单位毫秒
  • start()启动动画
  • cancel()退出动画
  • setRepeatCount(int value)循环次数,INFINITE表示无限循坏
  • setRepeatMode(int)重复类型,有REVERSE和RESTART两种类型,与setRepeatCount配合使用,reverse表示倒叙回放,restart表示重新开始
举个例子:
private static final String TAG = MainActivity.class.getSimpleName(); private TextView mTextView; private ValueAnimator mValueAnimator; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = findViewById(R.id.tv); mTextView.setOnClickListener(this); findViewById(R.id.tv_start_anim).setOnClickListener(this); findViewById(R.id.tv_cancel).setOnClickListener(this); }@Override public void onClick(View v) { switch (v.getId()) { case R.id.tv: Toast.makeText(this, "click", Toast.LENGTH_SHORT).show(); break; case R.id.tv_start_anim: mValueAnimator = setAnimator3(); break; case R.id.tv_cancel: if (mValueAnimator != null) { //退出动画 mValueAnimator.cancel(); } break; default: break; } }

private ValueAnimator setAnimator3() { ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 600f); //设置动画时长 2秒 valueAnimator.setDuration(2000); //设置重复次数,无限次 valueAnimator.setRepeatCount(ValueAnimator.INFINITE); //设置重复类型,倒叙从新开始 valueAnimator.setRepeatMode(ValueAnimator.REVERSE); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { Float v = (Float) animation.getAnimatedValue(); int value = https://www.it610.com/article/v.intValue(); mTextView.layout(mTextView.getLeft(), value, value + mTextView.getRight(), value + mTextView.getHeight()); Log.e(TAG,"value:" + value); } }); //设置2秒启动动画 valueAnimator.setStartDelay(2000); //开启动画 valueAnimator.start(); return valueAnimator; }

效果如下:
Android动画篇(三)—— 属性动画ValueAnimator的使用
文章图片

这里有两个按钮,点击start anim时,TextView垂直向下运动,我定义的运动值是ofFloat(0f,600f),从效果可以看出,我设置了两秒的动画时长,ValueAnimator.INFINITE无限循环,每次循环都是ValueAnimator.REVERSE使其倒叙重新开始循环,两秒后启动动画,点击cancel anim按钮后取消动画。由于最后取消动画还需要用到ValueAnimator实例,所以我们在方法中返回animtor。
3、三个监听器
(1)添加监听器
/** * 监听器一:监听动画变化时的实时值 */ 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) /** *监听器三:监听动画暂停和暂停后再恢复的状态 */ public static interface AnimatorPauseListener { void onAnimationPause(Animator animation); void onAnimationResume(Animator animation); } //添加方法:public void addPauseListener(AnimatorPauseListener listener)

监听器一:AnimatorUpdateListener就是监听动画实时变化的状态,在onAnimationUpdate(ValueAnimator animation)函数中返回的就是动画的实例animator,根据实例可以获取到不同的数据,比如:animation.getAnimatedValue()获取动画实时变化的值,这里就不细讲了。添加方法是addUpdateListener(AnimatorUpdateListener listener)。
监听器二:AnimatorListener主要是监听动画的四个状态:start,end,cancel,repeat;当动画开始时,会调用onAnimationStart(Animator animation)函数;当动画结束时,调用onAnimationEnd(Animator animation)函数;当动画退出时,调用onAnimationCancel(Animator animation)函数;当动画重复时,调用onAnimationRepeat(Animator animation)函数。添加方法是addPauseListener(AnimatorPauseListener listener)。
监听器三:android在api19之后增加了AnimatorPauseListener(),主要是监听动画暂停和暂停后再次动画的状态:pause和resume,在animator设置暂停后pause()会调用onAnimationPause(Animator animation)函数并且返回animator实例,当animator暂停后再次进入动画时调用resume()会调用onAnimationResume(Animator animation)函数。
我们添加上面的监听,代码如下:
private static final String TAG = MainActivity.class.getSimpleName(); private TextView mTextView; private ValueAnimator mValueAnimator; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = findViewById(R.id.tv); mTextView.setOnClickListener(this); findViewById(R.id.tv_start_anim).setOnClickListener(this); findViewById(R.id.tv_cancel).setOnClickListener(this); findViewById(R.id.tv_pause).setOnClickListener(this); findViewById(R.id.tv_resume).setOnClickListener(this); }@Override public void onClick(View v) { switch (v.getId()) { case R.id.tv: Toast.makeText(this, "click", Toast.LENGTH_SHORT).show(); break; case R.id.tv_start_anim: mValueAnimator = setAnimatorListener(); break; case R.id.tv_cancel: if (mValueAnimator != null) { //退出动画 mValueAnimator.cancel(); } break; case R.id.tv_pause: if (mValueAnimator != null) { //暂停动画 mValueAnimator.pause(); } break; case R.id.tv_resume: if (mValueAnimator != null) { //继续动画 mValueAnimator.resume(); } break; default: break; } }

/** * 监听器的使用 * * @return */ private ValueAnimator setAnimatorListener() { ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 200); //设置动画时长 2秒 valueAnimator.setDuration(1000); //设置重复次数,无限次 valueAnimator.setRepeatCount(ValueAnimator.INFINITE); //设置重复类型,倒叙从新开始 valueAnimator.setRepeatMode(ValueAnimator.REVERSE); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int value = https://www.it610.com/article/(int) animation.getAnimatedValue(); mTextView.layout(mTextView.getLeft(), value, mTextView.getRight(), value + mTextView.getHeight()); Log.e(TAG,"AnimatorUpdateListener: value:" + value); } }); valueAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { Log.e(TAG, "AnimatorListener:Start"); }@Override public void onAnimationEnd(Animator animation) { Log.e(TAG, "AnimatorListener:End"); }@Override public void onAnimationCancel(Animator animation) { Log.e(TAG, "AnimatorListener:Cancel"); }@Override public void onAnimationRepeat(Animator animation) { Log.e(TAG, "AnimatorListener:Repeat"); } }); valueAnimator.addPauseListener(new Animator.AnimatorPauseListener() { @Override public void onAnimationPause(Animator animation) { Log.e(TAG, "AnimatorPauseListener:Pause"); }@Override public void onAnimationResume(Animator animation) { Log.e(TAG, "AnimatorPauseListener:Resume"); } }); //开启动画 valueAnimator.start(); return valueAnimator; }

动画效果如下:
图一图二
Android动画篇(三)—— 属性动画ValueAnimator的使用
文章图片
Android动画篇(三)—— 属性动画ValueAnimator的使用
文章图片

动画设置了无限循环,开启动画后再关闭动画,看日志:
Android动画篇(三)—— 属性动画ValueAnimator的使用
文章图片

Android动画篇(三)—— 属性动画ValueAnimator的使用
文章图片

Android动画篇(三)—— 属性动画ValueAnimator的使用
文章图片

从图一中,可以看出,动画开始时回调了onAnimationStart()函数;动画值我设置的是(0,200),当数值到达20后,回调onAnimationRepeat()函数,并且每次重复都会执行该方法;点击退出动画时回调onAnimationCancel()函数;然后动画结束回调onAnimationEnd()函数。
图二,我们在看看开始动画后,点击pause anim按钮后,再点击resume anim按钮,看打印日志:
Android动画篇(三)—— 属性动画ValueAnimator的使用
文章图片

从日志可以看出,pause()后动画暂停了onAnimationPause()函数,Resume()后动画继续执行回调onAnimationResume()函数,我们可以在暂停动画期间做相关的操作。
(2)移除监听器
上面我们做了如何添加监听器,现在我们来看看如何移除监听器
/** * 移除AnimatorUpdateListener */ void removeUpdateListener(AnimatorUpdateListener listener); void removeAllUpdateListeners(); /** * 移除AnimatorListener */ void removeListener(AnimatorListener listener); void removeAllListeners(); /** * 移除AnimatorPauseListener */ void removePauseListener(AnimatorPauseListener listener)

每个监听器都有至指定的移除方法,而且AnimatorUpdateListener和AnimatorListener还有移除所有监听的方法,animator移除监听器后,就不会再接收到动画监听,我们给animator移除所有监听实验一下:
//移除动画监听 mValueAnimator.removeAllListeners();

在前面代码的基础上,我们在动画重复第三次的时候,点击移除按钮,移除所有的动画监听。
效果图:
Android动画篇(三)—— 属性动画ValueAnimator的使用
文章图片

Android动画篇(三)—— 属性动画ValueAnimator的使用
文章图片

可见只打印了循环三次以前的log,在移除我们添加的AnimatorListener之后,我们打印log的代码就不会再执行了,所以也就不会再有log了。
到这里有关ValueAnimator的常用函数基本就讲完,更深入的ValueAnimator的高级用法将另外开篇讲。
源码下载地址:https://github.com/FollowExcellence/AndroidAnimation
请大家尊重原创者版权,转载请标明出处: https://blog.csdn.net/m0_37796683/article/details/90440702 谢谢!
动画系列文章:
1、 Android动画篇(一)—— alpha、scale、translate、rotate、set的xml属性及用法
  • 补间动画的XML用法以及属性详解
2、Android动画篇(二)—— 代码实现alpha、scale、translate、rotate、set及插值器动画
  • 代码动态实现补间动画以及属性详解
3、 Android动画篇(三)—— 属性动画ValueAnimator的使用
  • ValueAnimator的基本使用
4、 Android动画篇(四)—— 属性动画ValueAnimator的高级进阶
  • 插值器(Interpolator)、计算器(Evaluator)、ValueAnimator的ofObject用法等相关知识
5、 Android动画篇(五)—— 属性动画ObjectAnimator基本使用
  • ObjectAnomator的基本使用以及属性详解
6、 Android动画篇(六)—— 组合动画AnimatorSet和PropertyValuesHolder的使用
  • AnimatorSet动画集合和PropertyValuesHolder的使用
以上几篇动画文章是一定要掌握的,写的不好请多多指出!

    推荐阅读