Android动画篇(四)—— 属性动画ValueAnimator的高级进阶

前言:成功只属于永不放弃的人。凡诸事有成者都有惊人的毅力做后盾,相信自己,持之以恒。
上一篇文章给大家介绍了ValueAnimator的基本用法,都是比较简单的用法,这里带大家学习一下有关加速器(Interpolator)、计算器(Evaluator)、ValueAnimator的ofObject用法等相关知识。
一、插值器(Interpolator) 插值器(也叫加速器)有关的效果我在《Android动画篇(二)—— 代码生成alpha、scale、translate、rotate、set及插值器动画》做了演示。
什么是插值器?我们知道通过ofInt(0,400)定义了动画区间值为0-400,然后通过添加AnimatorUpdateListener来监听动画实时变化,那么这个变化是怎么样变的呢?是匀速变化还是有快有慢,如果我想先减速后加速,那么该怎么做呢?这就是插值器的作用,插值器就是用来控制动画区间值如果计算出来的。比如LinearInterpolator是匀速计算返回区间点的值,而DecelerateInterpolator则是开始变快,后期变慢,其他都类似。
1、插值器的使用
我们使用弹跳插值器(BounceInterpolator)为例做一个简单的例子,BounceInterpolator就是在动画结束的时候弹跳起来。
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 400); valueAnimator.setDuration(1000); valueAnimator.setInterpolator(new BounceInterpolator()); 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,"value:" + value); } }); valueAnimator.start();

效果图如下:
Android动画篇(四)—— 属性动画ValueAnimator的高级进阶
文章图片

如果大家看懂了上一篇文章,这里的代码就非常容易理解了。在监听中,我只改变了top和bottom的位置,跟着动画变化的值来改变top和bottom的值,setDuration(1000)设置了动画时长,setInterpolator()设置了插值器,也就是过渡值的变化规则,插值器的意义其实就是相当于物理学中的加速度参数,这也是像加速器的原因。
2、自定义插值器
(1)概述
我们先看看最简单的匀速插值器LinearInterpolator是怎么样的
@HasNativeInterpolator public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {public LinearInterpolator() { }public LinearInterpolator(Context context, AttributeSet attrs) { }public float getInterpolation(float input) { return input; }/** @hide */ @Override public long createNativeInterpolator() { return NativeInterpolatorFactoryHelper.createLinearInterpolator(); } }

abstract public class BaseInterpolator implements Interpolator { private @Config int mChangingConfiguration; /** * @hide */ public @Config int getChangingConfiguration() { return mChangingConfiguration; }/** * @hide */ void setChangingConfiguration(@Config int changingConfiguration) { mChangingConfiguration = changingConfiguration; } }

在上面的可以得知LinearInterpolator继承了BaseInterpolator实现了Interpolator接口,而Interpolator 接口直接继承自TimeInterpolator,而且Interpolator 没有添加其他的方法,我们来看看TimeInterpolator这个接口:
/** * A time interpolator defines the rate of change of an animation. This allows animations * to have non-linear motion, such as acceleration and deceleration. */ public interface TimeInterpolator {/** * Maps a value representing the elapsed fraction of an animation to a value that represents * the interpolated fraction. This interpolated value is then multiplied by the change in * value of an animation to derive the animated value at the current elapsed animation time. * * @param input A value between 0 and 1.0 indicating our current point *in the animation where 0 represents the start and 1.0 represents *the end * @return The interpolation value. This value can be more than 1.0 for *interpolators which overshoot their targets, or less than 0 for *interpolators that undershoot their targets. */ float getInterpolation(float input); }

在TimeInterpolator里面只有一个函数, float getInterpolation(float input):
  • float input是float类型,范围是0-1,表示当前动画的进度,0表示动画刚刚开始,1表示动画结束。0.5表示动画中间位的位置,其他的以此类推
  • 返回值表示当前实际想要显示的进度,取值可以大于1也可以小于0,大于1表示超出动画位置,小于0表示小于开始位置
input参数表示当前动画的进度,匀速增加的。动画进度就是动画时间上的进度,与我们任何设置无关,只与时间有关,随时间的增加,动画进度自然增加,从0到1,我们在setDuration(1000)设置了动画时长的,动画在该时间内完成。
而返回值表示动画进度的具体数值,它的取值范围是对应ofInt()、ofFloat()来指定的,这个返回值就表示当前时间动画的具体进度值。
ValueAnimator valueAnimator = ValueAnimator.ofInt(100, 400); valueAnimator.setDuration(1000); valueAnimator.setInterpolator(new BounceInterpolator()); 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,"value:" + value); } }); valueAnimator.start();

从上面的代码知道,我们设置了AnimatorUpdateListener后,在监听回调函数中设置animation.getAnimatedValue()就可以获得动画具体进度值,那么这个值是怎么来的呢?
计算公式:当前进度值 = 100+(400-100)*显示进度
其中100和400就是我们ofInt(100,400)设置的起点值和终点值,我们要计算位置是,在起始值100的基础上加上实际运动距离(400-100)乘以运动进度。ofInt中的animation.getAnimatedValue()的值就是这么来的。
我们知道了input与getInterpolation()返回值的关系了,来看看LinearInterpolator 是如何重写getInterpolation()方法的?
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {········public float getInterpolation(float input) { return input; }········ }

LinearInterpolator 在getInterpolation()函数中,直接把input返回了,把动画的进度作为动画具体的数值进度,也就是动画进度和时间进度一致,比如动画进度为0,那么动画具体数值进度也为0,动画进度为0.5,那么动画具体数值进度也为0.5。
(2)实例
自定义插值器,继承BaseInterpolator就可以了,实现其中的方法getInterpolation(float input),我们将该方法的返回值作了修改,我们将进度翻转过来。然后使用我们的插值器。
public class MyInterpolator extends BaseInterpolator { @Override public float getInterpolation(float input) { return 1 - input; } }

使用自定义的插值器
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 400); valueAnimator.setDuration(1000); //设置自定义的插值器 valueAnimator.setInterpolator(new MyInterpolator()); 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,"value:" + value); } }); valueAnimator.start();

效果如下:
Android动画篇(四)—— 属性动画ValueAnimator的高级进阶
文章图片

我们在将进度数值倒叙返回后,TextView在结束为止(400)返回到开始位置(0)。
到这里,想必大家已经了解getInterpolation(float input)函数中input参数与返回值的关系,在重写插值器时,需要强有力的数学知识做支持,一般都是通过数学公式来计算插值器的变化趋势。你们可以在分析几个其他插值器的写法,可以总结成公式,放在公式插图软件里面,看看对应的数学图在(0,1)之间的走向,这个走势就是插值器在数值变化时的样子。
二、计算器(Evaluator) 1、概述
上面我们提到通过监听器拿到当前动画所对应的具体数值,而不是百分制的进度,那么就有一个地方,根据当前的进度值转化为对应的数值,这个地方就是计算器(Evaluator),Evaluator就是将加速器返回的进度值转化为相对于的数值。其实Evaluator就是一个转换器,他能把小数进度转换成对应的数字值。
上面提到的公式:
当前值 = 100 + (400-100)*显示进度

这个公式就是在Evaluator内计算的,在拿到当前数字进度所对应的值后将其返回。看看下面这幅图:
Android动画篇(四)—— 属性动画ValueAnimator的高级进阶
文章图片

上面这幅图是从定义动画的数值区间到通过AnimatorUpdateListener获得当前动画所对应具体数值的整个过程,解释一下:
(1)ofInt(0,400)表示指定动画的数值区间,是从0运动到400;
(2)在动画开始之后,通过加速器返回动画进度所对应的数字进度,这个数字进度是百分制的,以小数表示,比如0.2;
(3)Evaluator将加速器返回的进度值转化为相对于的数值;
(4)通过AnimatorUpdateListener监听器使用animation.getAnimatedValue()函数获得Evaluator中返回的数值。
2、各种Evaluator
无论是什么动画它的进度必然是在0-1之间,0表示没开始,1表示结束,任何动画都适用。但是Evaluator不一样,我们知道Evaluator是用来根据加速器Interpolator返回的小数进度转换成当前的数值进度。
那么使用ofInt()来定义动画,动画中的值都是int类型,所对应的Evaluator返回值时,也是返回int类型的数值;如果使用ofFloat()来定义动画,动画中的值都是float类型,所对用的Evaluator返回值时,也是返回float类型的数值。所以每种Evaluator的定义方式必定有对应的Evaluator来使用,他的专用原因在于数值动画类型不一致,在通过Evaluator返回时会报类型强转错误。所以在动画数值类型一致时,Evaluator才能通用,ofInt()对应IntEvaluator,ofFloat()对应FloatEvaluator,在设置Evaluator时通setEvaluator(TypeEvaluator value)来设置:
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 400); valueAnimator.setDuration(1000); valueAnimator.setEvaluator(new IntEvaluator()); valueAnimator.setInterpolator(new BounceInterpolator()); 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,"value:" + value); } }); valueAnimator.start();

但是之前为什么没有设置Evaluator的时候也能正常使用呢?因为ofInt()和ofFloat()是系统直接提供的函数,在使用时都有默认的Interpolator和Evaluator,没有设置则使用默认的,我们看看IntEvaluator的内部实现:
/** * This evaluator can be used to perform type interpolation between int values. */ public class IntEvaluator implements TypeEvaluator {/** * This function returns the result of linearly interpolating the start and end values, with * fraction representing the proportion between the start and end values. The * calculation is a simple parametric calculation: result = x0 + t * (v1 - v0), * where x0 is startValue, x1 is endValue, * and t is fraction. * * @param fractionThe fraction from the starting to the ending values * @param startValue The start value; should be of type int or *Integer * @param endValueThe end value; should be of type int or Integer * @return A linear interpolation between the start and end values, given the *fraction parameter. */ public Integer evaluate(float fraction, Integer startValue, Integer endValue) { int startInt = startValue; return (int)(startInt + fraction * (endValue - startInt)); } }

可以看到IntEvaluator里面只有一个函数evaluate(float fraction, Integer startValue, Integer endValue),其中fraction就是加速器中的返回值,表示当前动画的数值进度,百分制小数表示。startValue和endValue分别对应ofInt(int start , int end)的起始值和结束值。比如上面的动画ofInt(100,400)进行到40%时,在Evaluator函数中fraction=0.4,startValue = https://www.it610.com/article/100,endValue = 400;
下面我们看看进度小数值计算出来的具体数值:
return (int)(startInt + fraction * (endValue - startInt));

这不正是对应着我上面提到的公式吗?
当前值 = 100 + (400-100)*显示进度

计算原理我们在上面已经讲过,根据进度来计算具体数值。我们来自定义一个Evaluator
3、自定义Evaluator
(1)简单实现MyEvaluator
我们定义一个Evaluator,首先实现TypeEvaluator<>,根据数值类型传入泛型类型:
public class MyEvaluator implements TypeEvaluator { @Override public Integer evaluate(float fraction, Integer startValue, Integer endValue) { return null; } }

我们来实现简单的evaluate函数:
return (int) (200 + startValue + fraction * (endValue - startValue));

根据IntEvaluator的基础上做了修改,让返回值增加了200,所以开始定义的区间是ofInt(0,400)但是实际返回的区间是ofInt(200,600),看看怎么使用:
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 400); valueAnimator.setDuration(1000); valueAnimator.setEvaluator(new MyEvaluator()); 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,"value:" + value); } }); valueAnimator.start();

效果图如下:
Android动画篇(四)—— 属性动画ValueAnimator的高级进阶
文章图片

从效果图可看出TextView的动画位置都向下移动了200。
在加速器Interpolator中,我们通过自定义加速器返回的数值进度来返回数值的位置,比如上面我们实现的倒叙动画
在计算器Evaluator中,通过改变进度值所对应的具体数值来改变数值位置。
结论:我们可以通过重写加速器Interpolator来改变数值进度来改变数值位置,也可以通多改变计算器Evaluator中进度所对应的数值来改变数值位置。
(2)实现倒叙输出
我们来定义一个ReverseEvaluator来实现倒叙:
public class ReverseEvaluator implements TypeEvaluator { @Override public Integer evaluate(float fraction, Integer startValue, Integer endValue) { return (int) (endValue - fraction * (endValue - startValue)); } }

其中fraction * (endValue - startValue)表示实际运动的距离,endValue - fraction * (endValue - startValue)表示离终点的距离有多少,实现了从终点出发,最终到达起点。我们使用试一试:
valueAnimator.setEvaluator(new ReverseEvaluator());

效果如下:
Android动画篇(四)—— 属性动画ValueAnimator的高级进阶
文章图片

4、关于ArgbEvaluator
ArgbEvaluator使用来做颜色值转换的,我们先来使用一下,最后再讲源码:
在源码我们可以看出返回值时Int类型,可以使用ofInt()来初始化动画的取值范围,颜色值包括A、R、G、B四个值,每个值都是8位,用16进制来表示颜色值。
ValueAnimator valueAnimator = ValueAnimator.ofInt(0xff000000, 0xffffffff); valueAnimator.setDuration(2000); valueAnimator.setEvaluator(new ArgbEvaluator()); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int value = https://www.it610.com/article/(int) animation.getAnimatedValue(); mTextView.setBackgroundColor(value); Log.e(TAG,"value:" + value); } }); valueAnimator.start();

我们将动画的取值范围定义为ofInt(0xff000000, 0xffffffff),即从黑色变为白色,在AnimatorUpdateListener中将回传回来的颜色值设置在TextView的背景颜色中。
Android动画篇(四)—— 属性动画ValueAnimator的高级进阶
文章图片

点击进去看一看ArgbEvaluator的源码:
/** * This function returns the calculated in-between value for a color * given integers that represent the start and end values in the four * bytes of the 32-bit int. Each channel is separately linearly interpolated * and the resulting calculated values are recombined into the return value. * * @param fraction The fraction from the starting to the ending values * @param startValue A 32-bit int value representing colors in the * separate bytes of the parameter * @param endValue A 32-bit int value representing colors in the * separate bytes of the parameter * @return A value that is calculated to be the linearly interpolated * result, derived by separating the start and end values into separate * color channels and interpolating each one separately, recombining the * resulting values in the same way. */ public Object evaluate(float fraction, Object startValue, Object endValue) { int startInt = (Integer) startValue; float startA = ((startInt >> 24) & 0xff) / 255.0f; float startR = ((startInt >> 16) & 0xff) / 255.0f; float startG = ((startInt >>8) & 0xff) / 255.0f; float startB = ( startInt& 0xff) / 255.0f; int endInt = (Integer) endValue; float endA = ((endInt >> 24) & 0xff) / 255.0f; float endR = ((endInt >> 16) & 0xff) / 255.0f; float endG = ((endInt >>8) & 0xff) / 255.0f; float endB = ( endInt& 0xff) / 255.0f; // convert from sRGB to linear startR = (float) Math.pow(startR, 2.2); startG = (float) Math.pow(startG, 2.2); startB = (float) Math.pow(startB, 2.2); endR = (float) Math.pow(endR, 2.2); endG = (float) Math.pow(endG, 2.2); endB = (float) Math.pow(endB, 2.2); // compute the interpolated color in linear space float a = startA + fraction * (endA - startA); float r = startR + fraction * (endR - startR); float g = startG + fraction * (endG - startG); float b = startB + fraction * (endB - startB); // convert back to sRGB in the [0..255] range a = a * 255.0f; r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f; g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f; b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f; return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b); }

这段代码主要分为三部分:
(1)根据startValue求出A、R、G、B中各个色彩的初始值;
(2)根据endValue求出A、R、G、B的各个结束值;
(3)根据当前动画百分比进度求出对应的值。
第一部分:根据startValue求出A、R、G、B中各个色彩的初始值
int startInt = (Integer) startValue; float startA = ((startInt >> 24) & 0xff) / 255.0f; float startR = ((startInt >> 16) & 0xff) / 255.0f; float startG = ((startInt >>8) & 0xff) / 255.0f; float startB = ( startInt& 0xff) / 255.0f;

这段代码是根据位移和与运算计算出颜色值中A、R、G、B各部分对应的值,颜色值与A、R、G、B值关系如下:
Android动画篇(四)—— 属性动画ValueAnimator的高级进阶
文章图片

Android中的颜色值遵循RGB/ARGB标准,使用时通常以"#"为为开头的8位16进制表示。其中ARGB依次代表透明度(alpha)、红色(red)、绿色(green)、蓝色(blue);取值范围为0~255,即十六进制的0x00~0xff表示。A从0x00到0xff表示完全透明到完全不透明,RGB从0x00到0xff表示颜色从浅到深。
所以我们的初始值是0xff000000,求出来的startA = 0xff,startR = 0x00,startG = 0x00,startB = 0x00;关于位移和与运算我就不讲了。
第二部分:和第一部分原理一致,根据endValue求出A、R、G、B的各个结束值的颜色值:
int endInt = (Integer) endValue; float endA = ((endInt >> 24) & 0xff) / 255.0f; float endR = ((endInt >> 16) & 0xff) / 255.0f; float endG = ((endInt >>8) & 0xff) / 255.0f; float endB = ( endInt& 0xff) / 255.0f;

我们的结束值是:0xffffffff,求出来的endA = oxff,endR = oxff,endG = oxff,endB = oxff;
最后一部分:根据当前动画百分比进度求出对应的值
float a = startA + fraction * (endA - startA); float r = startR + fraction * (endR - startR); float g = startG + fraction * (endG - startG); float b = startB + fraction * (endB - startB);

对于这个计算都很容易理解,其实和Evalustor的计算公式一样,根据起始值和结束值计算出该进度下的具体值。最后通过位移和与运算将当下的ARGB各个值组合成当前的颜色值。
三、ofObject 1、ofObject的概述
前面讲了ValueAnimator使用ofInt()和ofFloat()来定义动画,但是ofInt()和ofFloat()分别只能传入Int和Float类型的参数,如果我们需要其他操作类型的怎么办?ValueAnimator还有一个ofObject()类型的参数,可以传入任何类型的变量:
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)

它有两个参数,一个是自定义的Evaluator;一个是可变长参数,object类型
大家可能有疑问,为什么要自定义Evaluator,因为Evaluator是根据动画当前的进度来计算当前对应的值的,如果可变参数类型是object,那么具体的类型就是未知的,所以Evaluator无法根据类型来得出结果,进度值的转换过程也必须由我们来做,我们来看看ofObject()的使用效果:
Android动画篇(四)—— 属性动画ValueAnimator的高级进阶
文章图片

从效果图可以看出,字母A变化到Z,并且变化速度越来越快。
ValueAnimator valueAnimator = ValueAnimator.ofObject(new CharEvaluator(), new Character('A'), new Character('Z')); valueAnimator.setDuration(6000); valueAnimator.setInterpolator(new AccelerateInterpolator()); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { char value = https://www.it610.com/article/(char) animation.getAnimatedValue(); mTextView.setText(String.valueOf(value)); Log.e(TAG,"value:" + value); } }); valueAnimator.start();

这里注意三点:
(1)构造器:
ValueAnimator.ofObject(new CharEvaluator(), new Character('A'), new Character('Z'));

我们定义了一个CharEvaluator(后面会讲),初始化动画时,传入Character对象,起始是A,结束是Z,利用动画自动变化字母A到字母Z,具体的实现就是CharEvaluator的事情了;
(2)AnimatorUpdateListener监听
char value = https://www.it610.com/article/(char) animation.getAnimatedValue(); mTextView.setText(String.valueOf(value));

通过animation.getAnimatedValue()得到当前动画字符,在动画过程中通过Evaluator返回值的类型必然和构造时的类型时一样的,由于传入的是Character,所以得到的也是Character,转为字符串后设置到TextView;
(3)插值器
valueAnimator.setInterpolator(new AccelerateInterpolator());

这里我们使用的加速插值器,随着动画的进行,速度会越来越快。
好,最关键的地方到了,CharEvaluator是怎么实现的呢?ASCII码表中的数值与字符转换的方法,每一个字符都有一个跟它相对应的数值,字母A到字母Z所对应的数值区间为65-90,在程序中我们可以通过强转来改变类型。
比如:数字转字符
char temp = (char)65; //得到的temp的值是大写字母A

字符转数值
char temp = 'A'; int num = (int)temp; //这里的到的数值就是ASCII码表中A对应的值65

CharEvaluator的实现:
public class CharEvaluator implements TypeEvaluator { @Override public Character evaluate(float fraction, Character startValue, Character endValue) { int startInt = (int) startValue; int endInt = (int) endValue; int value = https://www.it610.com/article/(int) (startInt + fraction * (endInt - startInt)); return (char) value; } }

在这里,我们就是利用A-Z字母在ASCII字码表中数值递增的原理,先求出对应字符的数值,再将数值转化为具体字符。
2、ofObject的自定义对象
上面我们初度使用了ofObject(),ofObject()能初始化任何对像,我们来自定义一个对象,然后利用ofObject()来构造这个对象的动画。
Android动画篇(四)—— 属性动画ValueAnimator的高级进阶
文章图片

在这里,我们自定义一个view,在view上画一个圆,有动画效果,插值器使用的是回弹插值器(BounceInterpolator)
(1)定义类Point
public class Point { private int radius; public Point(int radius) { this.radius = radius; }public int getRadius() { return radius; }public void setRadius(int radius) { this.radius = radius; } }

point类很简单,只有一个成员变量radius
(2)自定义MyPointView
public class MyPointView extends View { private Point mPoint; public MyPointView(Context context, AttributeSet attrs) { super(context, attrs); }@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mPoint != null) { Paint paint = new Paint(); paint.setColor(Color.RED); paint.setStyle(Paint.Style.FILL); paint.setAntiAlias(true); canvas.drawCircle(500f, 600f, mPoint.getRadius(), paint); } }public void doAnimator() { ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(), new Point(20), new Point(300)); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mPoint = (Point) animation.getAnimatedValue(); invalidate(); } }); valueAnimator.setDuration(1000); valueAnimator.setInterpolator(new BounceInterpolator()); valueAnimator.start(); } }

我们先来理一理这里的代码:先看看外部调用的动画函数
public void doAnimator() { ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(), new Point(20), new Point(300)); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mPoint = (Point) animation.getAnimatedValue(); invalidate(); } }); valueAnimator.setDuration(1000); valueAnimator.setInterpolator(new BounceInterpolator()); valueAnimator.start(); }

ofObject()的构造方法:
ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(), new Point(20), new Point(300));

这里可以看到,在构造动画时,动画所对应的值是Point类型,那么在Evaluator中的返回值类型也是Point,有关PointEvaluator下面再讲,下面看看监听中的代码:
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mPoint = (Point) animation.getAnimatedValue(); invalidate(); } });

在监听过程中,通过animation.getAnimatedValue()得到的mPoint实例,保存在成员变量中,然后 invalidate()强制刷新View。在强制刷新后,会走onDraw()方法:
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mPoint != null) { Paint paint = new Paint(); paint.setColor(Color.RED); paint.setStyle(Paint.Style.FILL); paint.setAntiAlias(true); canvas.drawCircle(500f, 600f, mPoint.getRadius(), paint); } }

这里设置画笔的属性,在(500,600)的位置画出圆形。在构造ofObject()函数中,初始值和中间值都是Point类型,PointEvaluator的返回值类型也是Point:
public class PointEvaluator implements TypeEvaluator { @Override public Point evaluate(float fraction, Point startPoint, Point endPoint) { int startInt = startPoint.getRadius(); int endInt = endPoint.getRadius(); int value = https://www.it610.com/article/(int) (startInt + fraction * (endInt - startInt)); return new Point(value); } }

这里根据初始半径和最终半径求出当前动画所对应的半径值,然后构建一个Point对象。
(3)使用MyPointView
在布局中添加控件:

调用控件的属性:
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private static final String TAG = MainActivity.class.getSimpleName(); private TextView mTextView; private MyPointView myPointView; private ValueAnimator mValueAnimator; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myPointView = findViewById(R.id.myPointView); mTextView = findViewById(R.id.tv); 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_start_anim: setMyPointView(); break; case R.id.tv_cancel: if (mValueAnimator != null) { //退出动画 mValueAnimator.cancel(); } break; default: break; } }/** * ofObject的使用2 */ private voidsetMyPointView() { myPointView.doAnimator(); } }

点击start anim按钮时调用myPointView.doAnimator()方法开始动画。
至此,本文结束!篇幅比较长。
源码下载地址:https://github.com/FollowExcellence/AndroidAnimation
请大家尊重原创者版权,转载请标明出处: https://blog.csdn.net/m0_37796683/article/details/90483462 谢谢!
动画系列文章:
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的使用
【Android动画篇(四)—— 属性动画ValueAnimator的高级进阶】以上几篇动画文章是一定要掌握的,写的不好请多多指出!

    推荐阅读