前言:成功只属于永不放弃的人。凡诸事有成者都有惊人的毅力做后盾,相信自己,持之以恒。
上一篇文章给大家介绍了ValueAnimator的基本用法,都是比较简单的用法,这里带大家学习一下有关加速器(Interpolator)、计算器(Evaluator)、ValueAnimator的ofObject用法等相关知识。
插值器(也叫加速器)有关的效果我在《Android动画篇(二)—— 代码生成alpha、scale、translate、rotate、set及插值器动画》做了演示。
什么是插值器?我们知道通过ofInt(0,400)定义了动画区间值为0-400,然后通过添加AnimatorUpdateListener来监听动画实时变化,那么这个变化是怎么样变的呢?是匀速变化还是有快有慢,如果我想先减速后加速,那么该怎么做呢?这就是插值器的作用,插值器就是用来控制动画区间值如果计算出来的。比如LinearInterpolator是匀速计算返回区间点的值,而DecelerateInterpolator则是开始变快,后期变慢,其他都类似。
我们使用弹跳插值器(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 = (int) animation.getAnimatedValue();
mTextView.layout(mTextView.getLeft(), value, mTextView.getRight(), value +
mTextView.getHeight());
Log.e(TAG, "value:" + value);
}
});
valueAnimator.start();
效果图如下:
如果大家看懂了上一篇文章,这里的代码就非常容易理解了。在监听中,我只改变了top和bottom的位置,跟着动画变化的值来改变top和bottom的值,setDuration(1000)设置了动画时长,setInterpolator()设置了插值器,也就是过渡值的变化规则,插值器的意义其实就是相当于物理学中的加速度参数,这也是像加速器的原因。
(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):
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 = (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 = (int) animation.getAnimatedValue();
mTextView.layout(mTextView.getLeft(), value, mTextView.getRight(), value +
mTextView.getHeight());
Log.e(TAG, "value:" + value);
}
});
valueAnimator.start();
效果如下:
我们在将进度数值倒叙返回后,TextView在结束为止(400)返回到开始位置(0)。
到这里,想必大家已经了解getInterpolation(float input)函数中input参数与返回值的关系,在重写插值器时,需要强有力的数学知识做支持,一般都是通过数学公式来计算插值器的变化趋势。你们可以在分析几个其他插值器的写法,可以总结成公式,放在公式插图软件里面,看看对应的数学图在(0,1)之间的走向,这个走势就是插值器在数值变化时的样子。
上面我们提到通过监听器拿到当前动画所对应的具体数值,而不是百分制的进度,那么就有一个地方,根据当前的进度值转化为对应的数值,这个地方就是计算器(Evaluator),Evaluator就是将加速器返回的进度值转化为相对于的数值。其实Evaluator就是一个转换器,他能把小数进度转换成对应的数字值。
上面提到的公式:
当前值 = 100 + (400-100)*显示进度
这个公式就是在Evaluator内计算的,在拿到当前数字进度所对应的值后将其返回。看看下面这幅图:
上面这幅图是从定义动画的数值区间到通过AnimatorUpdateListener获得当前动画所对应具体数值的整个过程,解释一下:
(1)ofInt(0,400)表示指定动画的数值区间,是从0运动到400;
(2)在动画开始之后,通过加速器返回动画进度所对应的数字进度,这个数字进度是百分制的,以小数表示,比如0.2;
(3)Evaluator将加速器返回的进度值转化为相对于的数值;
(4)通过AnimatorUpdateListener监听器使用animation.getAnimatedValue()函数获得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 = (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 fraction The fraction from the starting to the ending values
* @param startValue The start value; should be of type int
or
* Integer
* @param endValue The 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 = 100,endValue = 400;
下面我们看看进度小数值计算出来的具体数值:
return (int)(startInt + fraction * (endValue - startInt));
这不正是对应着我上面提到的公式吗?
当前值 = 100 + (400-100)*显示进度
计算原理我们在上面已经讲过,根据进度来计算具体数值。我们来自定义一个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 = (int) animation.getAnimatedValue();
mTextView.layout(mTextView.getLeft(), value, mTextView.getRight(), value +
mTextView.getHeight());
Log.e(TAG, "value:" + value);
}
});
valueAnimator.start();
效果图如下:
从效果图可看出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());
效果如下:
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 = (int) animation.getAnimatedValue();
mTextView.setBackgroundColor(value);
Log.e(TAG, "value:" + value);
}
});
valueAnimator.start();
我们将动画的取值范围定义为ofInt(0xff000000, 0xffffffff),即从黑色变为白色,在AnimatorUpdateListener中将回传回来的颜色值设置在TextView的背景颜色中。
点击进去看一看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中的颜色值遵循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各个值组合成当前的颜色值。
前面讲了ValueAnimator使用ofInt()和ofFloat()来定义动画,但是ofInt()和ofFloat()分别只能传入Int和Float类型的参数,如果我们需要其他操作类型的怎么办?ValueAnimator还有一个ofObject()类型的参数,可以传入任何类型的变量:
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)
它有两个参数,一个是自定义的Evaluator;一个是可变长参数,object类型
大家可能有疑问,为什么要自定义Evaluator,因为Evaluator是根据动画当前的进度来计算当前对应的值的,如果可变参数类型是object,那么具体的类型就是未知的,所以Evaluator无法根据类型来得出结果,进度值的转换过程也必须由我们来做,我们来看看ofObject()的使用效果:
从效果图可以看出,字母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 = (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 = (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 = (int) (startInt + fraction * (endInt - startInt));
return (char) value;
}
}
在这里,我们就是利用A-Z字母在ASCII字码表中数值递增的原理,先求出对应字符的数值,再将数值转化为具体字符。
上面我们初度使用了ofObject(),ofObject()能初始化任何对像,我们来自定义一个对象,然后利用ofObject()来构造这个对象的动画。
在这里,我们自定义一个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 = (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 void setMyPointView() {
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的使用
以上几篇动画文章是一定要掌握的,写的不好请多多指出!