**重要结论:
既可以通过重写插值器
改变数值进度
来改变数值位置,
也可以通过改变Evaluator
中数值进度所对应的具体数值
来改变数值位置。自定义Interpolator
与自定义Evaluator
,
这两种方法都可以成为属性动画的驱动力!!!
**本文完整项目代码敬请见GitHub
系统插值器种类
1.AccelerateDecelerateInterpolator
加速减速插值器,
表示在开始与结束的地方速率改变比较慢,在中间的时候加速。
利用数学绘图工具将它的整个变化过程绘制出来,如下图所示:
- 整幅图像表示的是
动画进行的时刻(x轴)
与进行的程度(y轴)
的关系图,**
y轴的值域
为0 ~ 100
,表示动画进行的程度
;
最左侧(线的点坐标 y = 0)表示动画进度为0,即动画刚开始;
最右侧(线的点坐标 y = 100)表示动画完成,进度为1;线上的每一个点
表示在某个时间点
的动画进行的程度
。**
**所以某个点
的切线斜率
就表示该点对应的时间点的动画
的速率
;
该点的切线斜率越大
,
该点对应的时间点的动画的速率
则越大;
下面就用这种图来演绎诸中系统插值器的特性;**
2.AccelerateInterpolator
- 加速插值器,表示在动画开始的地方速率改变比较慢;
- 动画一直加速,在停止时是突然停止的,
而不像AccelerateDecelerateInterpolator先减速再停止;
3.DecelerateInterpolator
是减速插值器,
表示在动画开始的一瞬间加速到最大值,然后逐渐变慢
4.LinearInterpolator
线性插值器,也称匀速加速器,很显然,它的速率是保持恒定的
5.BounceInterpolator
弹跳插值器,模拟了控件自由落地后回弹的效果
- 上面的4幅图像虽然速率有变化,
但是随着时间的推移,动画进度一直是增长的:
而在这幅图像中,在结束部分,
随着时间的推移,动画进度会回退。
这也就是出现回弹效果的原因,它把动画进度回退了。
6. AnticipateInterpolator
初始偏移插值器,表示在动画开始的时候向前偏移一段距离,然后应用动画。
- AnticipateInterpolator还有一个构造函数。
public AnticipateInterpolator(float tension)
参数float tension
对应的XML属性为android:tension
,表示张力值
,默认值为2,
值越大,初始的偏移量越大,而且速度越快;
当直接使用new AnticipateInterpolator()
构造时,使用的是tension的默认值2。
下图展示了当tension(图中的T)分别取0.5、2、4时的数学图像。
7.OvershootInterpolator
结束偏移插值器,表示在动画结束时,沿动画方向继续运动一段距离后再结束动画。
- OvershootInterpolator也有另一个构造函数。
public OvershootInterpolator(float tension)
参数float tension
对应的XML属性为android:tension
,
表示张力值,默认值为2,值越大,结束时的偏移量越大,而且速度越快;
8. AnticipateOvershootInterpolator
AnticipateInterpolator与OvershootInterpolator的合体,
即在动画开始时向前偏移一段距离,在动画结束时向后偏移一段距离。
- AnticipateOvershootInterpolator也有其他的构造函数
参数float tension对应的XML属性为android:tension,
表示张力值,默认值为2,
值越大,起始和结束时的偏移量越大,而且速度越快。
参数float extraTension对应的XML属性为android:extraTension,
表示额外张力值,默认值为1.5。
9. CycleInterpolator
循环插值器,表示动画循环播放特定的次数,速率沿正弦曲线改变。
- 其构造函数:
public CycleInterpolator(float cycles)
参数cycles
表示循环次数;
- android:fillAfter="true"属性
对于CycleInterpolator而言没有影响。
案例:镜头由远及近效果
- **实现其实就是一个ScaleAnimation缩放动画而已,
再配合一下插值器;**
- 完整项目代码敬请见GitHub
......
case 6:
ImageView cameraStretchView = new ImageView(this);
cameraStretchView.setBackgroundResource(R.drawable.imagetest1);
ll_nextParent.addView(cameraStretchView, layoutParams);
ScaleAnimation scaleAnimation = getTheCameraStretchAnim();
cameraStretchView.startAnimation(scaleAnimation);
break;
default:
}
}
private ScaleAnimation getTheCameraStretchAnim() {
ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 1.2f, 1.0f, 1.2f,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setFillAfter(true);
scaleAnimation.setInterpolator(new BounceInterpolator());
scaleAnimation.setDuration(6000);
return scaleAnimation;
}
自定义插值器
前言
- 通过
ofInt(0,400)
定 义 了 动 画 的 区 间 值 是 0~400,
然后通过添加AnimatorUpdateListener
来监听动画的实时变化。
- 那么
0~400
的值
是怎么变化的呢?
插值器就是用来控制动画的区间值如何被计算出来的。
比如
LinearInterpolator插值器表示匀速返回区间内的值;
而DecelerateInterpolator插值器则表示开始变化快,后期变化慢;
其他插值器与此类似。
- 在 ValueAnimator 中使用插值器很简单,
直接调用 ValueAnimator.setInterpolator(TimeInterpolator value)
即可。
先看看系统自带的插值器是如何实现的,比如LinearInterpolator
- LinearInterpolator 实现了 Interpolator 接口,
而 Interpolator 接口则直接继承自TimeInterpolator,
而且并没有添加任何其他的方法。
看看TimeInterpolator接口都有哪些函数![](https://upload-images.jianshu.io/upload_images/9125154-d3ebf8461bf5c267.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240&x-oss-process=image/resize,w_1400/format,webp)
- 里面只有一个函数
float getInterpolation(float input)
。
该函数
的含义如下。
- 参数input:
input参数是Float类型的,它的取值范围是0~1,
表示当前动画的进度,
取0时表示动画刚开始,
取1时表示动画结束,
取0.5时表示动画中间的位置,其他以此类推。
- 返回值(
getInterpolation()
的return
返回的东西):
表示当前实际想要显示的进度;
取值可以超过1,也可以小于0。
超过1表示已经超过目标值,小于0表示小于开始位置。
**
getInterpolation(flaot input)
中的input参数
代表一个匀速增加的当前动画的进度,
这个进度是自然进度,自然的,没有经过修改的,
区分于实际显示的动画进度,
与任何设置无关,随着时间的推移,
动画的进度自然会从0到1逐渐增加。****input参数相当于时间的概念,
正如我们生活中参照时钟来知晓时间,
这里我们参照匀速、自然、不可改变的自然进度input
(input
类似于数学函数意义
中匀速增长
的自变量 x
),
通过向各种数学计算
输入input
(各种数学计算
则类似于数学函数意义
中的函数关系f()
),
以输出各种变化速率的数值
作为返回值
(最后作为getInterpolation(flaot input)
的return返回值的数值
,
则类似于因变量y
,y = f ( x )
),
从而实现各种形式的插值器
(插值器
就是基于函数规则
,
把匀速增长的自变量input
,
计算转换
成具有函数规则
的输出返回值
)****input参数与任何我们设定的值没有关系,
只与时间有关,随着时间的推移,动画的进度也自然地增加,
input参数就代表了当前动画的自然进度
,
而返回值则表示当前动画显示的数值进度
。**
- 返回值(
getInterpolation()
的return
返回的东西)
则表示动画的数值显示进度,
它可以通过Evaluator的计算,得到一个对应的数值范围,
对应的数值范围是我们通过ofInt()、ofFloat()函数来指定的。如上例程,
在添加了AnimatorUpdateListener的监听事件以后,
**通过在监听函数中调用animation.getAnimatedValue()函数,
就可以得到当前的值。当前的值=100+(400-100)× 显示进度
(这个公式其实就模拟了Evaluator的计算过程,Evaluator的内容稍后会进行详解)
其中,
100和400就是我们设置的ofInt(100,400)中的值,
而显示进度,就是返回值(getInterpolation()
的return
返回的东西)**
- 通过上面的讲解,
我们知道了input参数与getInterpolation()函数返回值的关系(y = f(x)),
下面看看LinearInterpolator是如何重写TimeInterpolator的:
- 以上,
LinearInterpolator在getInterpolation()函数中直接把input值返回,
即以当前动画进度作为动画的数值进度,
这也就表示当前动画的数值进度与动画的时间进度一致。
由于动画进度是随时间匀速前进的,
所以LinearInterpolator的数值进度也是匀速增加的,
这便是基于LinearInterpolator的动画进度是匀速进行的原理;
自定义Interpolator
- 自定义插值器其实很容易,
只需实现TimeInterpolator接口,
重写getInterpolation()
,
在里面返回自己需要的计算值和方式
即可:
- 下面例程,
自定义插值器的getInterpolation()
函数中,
将进度反转过来,
当传入0的时候,让它的数值进度在完成的位置;
当完成的时候,让它的数值进度在开始的位置。
public class MyInterpolator implements TimeInterpolator {
@Override
public float getInterpolation(float input) {
return 1 - input;
}
}
- 使用:
private void startAnimationTestInterpolator() {
ValueAnimator animator = ValueAnimator.ofInt(0, 400);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int curValue = (int)animation.getAnimatedValue();
tv_text.layout(tv_text.getLeft(),curValue,
tv_text.getRight(), curValue+tv_text.getHeight());
}
});
animator.setDuration(1000);
animator.setInterpolator(new MyInterpolator());
animator.start();
}
接下来笔记Evaluator
Evaluator
- 下图讲述了
从定义动画的数值区间到在AnimatorUpdateListener中
得到当前动画所对应数值的整个过程:
- ofInt(0,400):表示指定动画的数值区间,从0运动到400。
- 插值器:
在动画开始后
通过插值器会返回当前动画进度所对应的数值进度,
这个数值进度是以小数表示的,如0.2。
- Evaluator:
我们通过监听器拿到的是当前动画所对应的具体数值,
而不是用小数表示的数值。
那么必须有一个地方会根据当前的数值进度
将其转换为对应的数值
,
这个地方就是Evaluator
。
**Evaluator用于将
从插值器返回的数值进度(小数,0 - 1.0)
转换成对应的数值
。**
- 监听器返回:
在
AnimatorUpdateListener
监听器中
使用animation.getAnimatedValue()
函数
拿到Evaluator
中返回的数值。
插值器
返回的小数值表示的是当前动画的数值进度,这对于无论是使用
ofFloat()
函数
还是使用ofInt()
函数定义的动画都是适用的。
因为无论是什么动画,它的进度必然在0~1
之间。0表示还没开始,1表示动画结束
,这对于任何动画都是适用的。
- 而
Evaluator
则不一样,
它把插值器返回的小数进度转换成当前数值进度所对应的值。
如果使用ofInt()
函数来定义动画,
动画中的值应该都是Integer类型的,
所对应的Evaluator在返回值时,必然返回Integer类型的值;
如果使用ofFloat()
函数来定义动画,
动画中的值都是Float类型的,
Evaluator在返回值时,必然返回Float类型的值。
- **所以,每种定义方式所对应的Evaluator必然是它专用的。
Evaluator专用的原因
在于动画数值类型
不一样,
在通过Evaluator返回时会报强转错误,
所以只有在动画数值类型一样时,所对应的Evaluator才能通用。ofInt()
函数对应的Evaluator类名为IntEvaluator
,
而ofFloat()
函数对应的Evaluator类名为FloatEvaluator
。**
- 通过
animator.setEvaluator()函数
来设置Evaluator
,
(下面例程第三行)
private void startAnimationArgbEvaluator() {
ValueAnimator animator = ValueAnimator.ofInt(0xffffff00, 0xff0000ff);
animator.setEvaluator(new ArgbEvaluator());//设置Evaluator
animator.setDuration(3000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int curValue = (Integer) animation.getAnimatedValue();
tv_text.setBackgroundColor(curValue);
}
});
animator.start();
}
**可以使用ValueAnimator.ofInt()函数构造ValueAnimator,
显式设置了它所对应的IntEvaluator,
用来计算数值进度所对应的数值。
但其实,
ofInt()
和ofFloat()
都是系统直接提供的函数,
所以会有默认的插值器和Evaluator可供使用。
ofInt()
函数的默认Evaluator 是IntEvaluator
,
而ofFloat()
函数的默认Evaluator则是FloatEvaluator
。**
Ctrl + 左键 看一下IntEvaluator的源码
package android.animation;
public class IntEvaluator implements TypeEvaluator<Integer> {
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)
函数中start
和end
的数值。**
假设当我们定义的动画ofInt(100,400)
进行到数值进度20%
的时候,
那么此时在evaluate()
函数中,fraction
的值就是0.2
,startValue
的值是100
,endValue
的值是400
。
**返回值
(即return (int)(startInt + fraction * (endValue - startInt));
)
就是当前数值进度
所对应的具体数值
,
这个数值就是
我们在AnimatorUpdateListener
监听器中
通过animation.getAnimatedValue()
函数得到的数值。**
evaluate(float fraction,Integer startValue,Integer endValue)
函数根据
return (int)(startInt + fraction * (endValue - startInt));
,
也就是根据进度数值来计算出具体数值,
这跟前面插值器中提到的当前的值=100+(400-100)× 显示进度
是相对应:
**结论:
既可以通过重写插值器改变数值进度
来改变数值位置,
也可以通过改变Evaluator中数值进度所对应的具体数值
来改变数值位置。
这两种方法都可以成为属性动画的驱动力!!!
**
自定义Evaluator
public class MyEvaluator implements TypeEvaluator<Integer> {
@Override
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int) (200 + startInt + fraction * (endValue - startInt));
}
}
- 首先实现TypeEvaluator接口;(注意这里的理解泛型的概念);
这里实现TypeEvaluator时,指定它的泛型是Integer类型的,
这样就可以在ofInt()函数中使用这个Evaluator了。
- **注意:
只有定义动画时的数值类型与Evaluator的返回值类型一样,
才能使用这个Evaluator。
很显然,ofInt()函数定义的数值类型是Integer,
这里定义的MyEvaluator只能适用于ofInt()
函数了。
同理,
如果把实现的TypeEvaluator
接口泛型
设置为Float类型,
那么这个Evaluator也就只能适用于ofFloat()
函数了。**
- 然后简单实现其中的
evaluate()
函数:在IntEvaluator的基础上修改了一下,
让它返回值时增加了200。
所以,
当我们定义的区间是ofInt(0,400)时,
它的实际返回值区间应该是(200,600)。
使用自定义的Evaluator
private void startAnimation() {
ValueAnimator animator = ValueAnimator.ofInt(0, 400);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int curValue = (int)animation.getAnimatedValue();
tv_text.layout(tv_text.getLeft(),curValue,
tv_text.getRight(), curValue+tv_text.getHeight());
}
});
animator.setDuration(1000);
animator.setEvaluator(new MyEvaluator());
animator.start();
}
自定义Evaluator实现倒序输出
- 除
IntEvaluator
和FloatEvaluator
外,
在android.animation
包下还有另一个Evaluator
,
名为ArgbEvaluator
,它是用来实现颜色值过渡转换的。
ArgbEvaluator
的实现原理- 这段代码分为三部分:
第一部分根据startValue
求出A、R、G、B中各个色彩的初始值;
第二部分根据endValue
求出A、R、G、B中各个色彩的结束值;
第三部分根据当前动画的百分比进度求出对应的数值。
注意0xff是八个二进制位而已,刚好留下一个色彩的值!!
使用ArgbEvaluator
private void startAnimationArgbEvaluator() {
ValueAnimator animator = ValueAnimator.ofInt(0xffffff00, 0xff0000ff);
animator.setEvaluator(new ArgbEvaluator());//设置Evaluator
animator.setDuration(3000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int curValue = (Integer) animation.getAnimatedValue();
tv_text.setBackgroundColor(curValue);
}
});
animator.start();
}
本文完整项目代码敬请见GitHub