Android动画系列:
- 补间动画详解
- 帧动画
- LayoutAnimation
- LayoutTransition
- 属性动画 - 基本使用
- 属性动画 - Interpolator(内插器)
- 属性动画 - TypeEvaluator
- 属性动画 - Keyframe
- AnimatorSet
介绍
对于Interpolator(内插器),写过动画的都不会陌生,其本身看做是一个时间计算器,用于定义动画的执行的速率。
public class ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback {
***
// The time interpolator to be used if none is set on the animation
private static final TimeInterpolator sDefaultInterpolator =
new AccelerateDecelerateInterpolator();
/**
* The time interpolator to be used. The elapsed fraction of the animation will be passed
* through this interpolator to calculate the interpolated fraction, which is then used to
* calculate the animated values.
*/
private TimeInterpolator mInterpolator = sDefaultInterpolator;
***
}
上面是ValueAnimator源码的一部分,在这里可以清晰的看出来,在我们不为ValueAnimator设置Interpolator为AccelerateDecelerateInterpolator,也就是说系统默认是先加速后减速这样一种动画执行速率。
LinearInterpolator ll = new LinearInterpolator();
ObjectAnimator animator = ObjectAnimator.ofFloat(btnProperty, "rotation",
0f, 360f);
animator.setInterpolator(ll);
animator.setDuration(5000);
animator.setRepeatCount(ValueAnimator.INFINITE);
ObjectAnimator animatorA = ObjectAnimator.ofFloat(btnPropertyA, "rotation",
0f, 360f);
animatorA.setRepeatCount(ValueAnimator.INFINITE);
animatorA.setDuration(5000);
animator.start();
animatorA.start();
先定义两个Button,其中,btnProperty背景色设置为红色,而btnPropertyA的背景色设置为蓝色.对两个Button分别定义了动画,btnProperty的动画的Interpolator设置为LinearInterpolator,即动画的执行速率为匀速运动,而btnPropertyA的动画的Interpolator设置未设置使用的是默认的。从实际运行的效果可以清晰的看出,刚开始时,btnProperty动画运行速率快于btnPropertyA,随着动画的执行,btnPropertyA逐渐追上了btnProperty,在同一轮动画时,它们同时处于结束位置。对于动画的默认Interpolator为AccelerateDecelerateInterpolator,也就有了合理的解释了。
源码分析
对于动画的执行速率,Interpolator是怎么计算的呢?先看Interpolator的源码:
public interface Interpolator extends TimeInterpolator {
}
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.
*/
float getInterpolation(float input);
}
从Interpolator的源码可以看出,Interpolator其实就是一个空的接口,继承与TimeInterpolator。再看看TimeInterpolator这个接口,其内只声明了getInterpolation(float)这一个方法。传入getInterpolation(float)方法的参数实际上是动画的进度,其值在0和1之间,0表示动画开始,1表示动画结束。而其返回值是表示内插器计算出来的值,其可以小于0或者大于1。然后将内插值乘以动画中属性值的差量即为画的当前属性值。
刚才我们用到了LinearInterpolator,表示动画匀速运行,下面看看匀速效果是怎么现实的。
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();
}
}
从LinearInterpolator源码中可以看出,在getInterpolation(float input)方法中并没有对input值做任何处理。由于input值是动画执行的百分比,这个值是均匀的。故而LinearInterpolator计算出的内插值其实就是动画执行的百分比,没有啥变化。
public class AccelerateDecelerateInterpolator extends BaseInterpolator
implements NativeInterpolatorFactory {
public AccelerateDecelerateInterpolator() {
}
@SuppressWarnings({"UnusedDeclaration"})
public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {
}
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
/** @hide */
@Override
public long createNativeInterpolator() {
return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();
}
}
对于AccelerateDecelerateInterpolator而言,其getInterpolation(input)方法中对intput做了处理,下图为其处理方式所对应的函数图像,从图像可以清晰的看出,内插值开始时增长趋势很快,然后在减小。相对应内插值计算动画属性时,应该是起初动画属性值逐渐增大很快,然后增大趋势减弱,意味着表现出来的动画效果为先加速再减速。
自定义Interpolator
1.自定义一个Interpolator,命名为MyInterpolator
public class MyInterpolator implements TimeInterpolator {
@Override
public float getInterpolation(float input) {
float inter;
inter = 1 - input * input;
return inter;
}
}animatorA
2.将animator的Interpolator设置为LinearInterpolator,而animatorA的设置为刚定义的MyInterpolator。
LinearInterpolator ll = new LinearInterpolator();
ObjectAnimator animator = ObjectAnimator.ofFloat(btnProperty, "rotation",
0f, 360f);
animator.setInterpolator(ll);
animator.setDuration(5000);
animator.setRepeatCount(ValueAnimator.INFINITE);
ObjectAnimator animatorA = ObjectAnimator.ofFloat(btnPropertyA, "rotation",
0f, 360f);
animatorA.setInterpolator(new MyInterpolator());
animatorA.setRepeatCount(ValueAnimator.INFINITE);
animatorA.setDuration(5000);
animator.start();
animatorA.start();
从效果图上,可以看出btnPropertyA的实际动画效果是倒着转的,而且是先加速后减速,为啥会出现这种情况呢?百思不得其解,前面已经说过btnPropertyA的动画的内插器为MyInterpolator,那么此种效果必然与内插器有关。先不管效果到底是为啥,先MyInterpolator中,从1 - input * input的函数图像和实际值,来看看getInterpolation(input)的处理结果。
- 函数图像
下图为在0-1之间的函数图像,从函数图像可以看出,其函数值先加速后减速。
- 实际值
input | getInterpolation |
---|---|
0 | 1 |
0.1 | 0.99 |
0.2 | 0.96 |
0.4 | 0.86 |
0.6 | 0.64 |
0.8 | 0.36 |
0.9 | 0.19 |
1.0 | 0 |
从函数图像和实际值的差值,我们清楚的看到,动画属性值的变化幅度先逐渐变大然后再逐渐减少的,这也能够解释动画的执行速率问题了。
接下来看看动画为啥倒转呢。在声明动画时,开始旋转角度为0f,结束的旋转角度为360.0f,也就是动画的实际效果是旋转一周,这个没有问题,实际上也就是选择一周的。而动画的旋转角度差值为360.0f。假如动画的执行百分比为0.1时,MyInterpolator内插器计算出来的内插值为0.99f.
前面说过将内插值乘以动画中属性值的差量即为画的当前属性值,此时动画当前的属性值应为356.4f,也就是此时,btnPropertyA旋转了356.4。同理当动画执行百分比为0.2时,btnPropertyA旋转了345.6度。也就是btnPropertyA的动画效果实际上是先旋转到360度,然后慢慢旋转到0度(初始位置)。这也就是btnPropertyA的动画倒转的原因。
也就是说动画的属性值的计算是由Interpolator和Evaluators共同计算出来的。当动画开始以后,Interpolator先获取动画执行的百分比并计算出来内插值,Evaluators在获取Interpolator计算出的内差值并在evaluate(float fraction, MyPoint pointStart, MyPoint pointEnd)内计算出动画的属性值。有说过fraction为动画的执行的百分比,其实描述的并不准确,其实际值因为Interpolator计算出的内插值。既然了解到了Interpolator、Evaluators和动画属性值之间的计算关系,以后随心所欲的自定义动画了。