早期Android只有补间动画和帧动画,为了满足各种各样的动画效果,Android推出了属性动画。
补间动画和帧动画
帧动画
帧动画,我们从字面意思来理解,帧:就是影像动画中最小单位的单幅影像画面,相当于电影胶片上的每一格镜头。 一帧就是一副静止的画面,连续的帧就形成动画,如电视图象等。简单点说就是类似幻灯片播放的那种效果,因此帧动画的本质就是将一张张的图片,通过代码对这些图片进行连续的活动(这样就形成了动画)。
补间动画
即Tween动画或View动画, 通过对View的内容进行一系列的图形变换 (包括平移、缩放、旋转、改变透明度)来实现动画效果。动画效果的定义可以采用XML来做也可以采用编码来做。
所谓补间,就是确定2个帧,让计算机帮你计算帧之间的其他帧 ;如位移动画,我们只需要确定位置开始和结束这两个关键帧,计算机帮我们计算动画运行期间其他帧,让位移平缓地进行。
属性动画
一个View有位置,宽度,长度,颜色等各个属性,假如我们需要在手机每一帧都改变其值,就需要属性动画平缓地改变这些属性值;属性动画不仅仅用于View,也可以用任何对象,
属性动画的Animator 有两个
- ValueAnimator 值动画
- ObjectAnimator 对象动画
ObjectAnimator 继承ValueAnimator ,封装一些公共操作,如果只是对View进行简单处理,可以用它,少写一些代码。
ValueAnimator 值动画
要每一帧改变VIew的属性,我们需要通过一个计算机,帮我们计算每一帧的值,和每一帧回调给我们,然后我们去设置view的属性;
如下,ValueAnimator设置动画时长1.6s,并且设置从1到100的变化,在通过插值器和估值器的计算后,addUpdateListener将会在每一帧返回值,以60HZ为例,第二帧就会在16毫秒后得到值1,当然如果是120HZ,会在8毫秒得到值0.5;
private void simple() {
ValueAnimator mValueAnimator = ValueAnimator.ofObject(new FloatEvaluator(), 0, 100);
mValueAnimator.setDuration(1600);
mValueAnimator.addUpdateListener(animation -> {
float color = (int) animation.getAnimatedValue();
mView.setTranslationX(color);
});
mValueAnimator.start();
}
scroller滑动动画与ValueAnimator 属性动画有点类似,但是scroller滑动动画无法设置插值器,所以没法做变速过度,scroller滑动动画用于滑动初始点和结束点频繁变化的情况,如手指跟随。
ObjectAnimator 值动画
很多的时候我们都懒得去自己写监听,然后去设置属性,于是谷歌帮我们写了 ObjectAnimator ,ObjectAnimator 继承 ValueAnimator,在动画过程中帮我们设置每一帧的属性(如位移),当然我们也可以自定义继承ValueAnimator自己写一个属性动画类
private void simple2() {
ObjectAnimator mValueAnimator = ObjectAnimator.ofFloat(mView, "translationX", 0f, 100f);
mValueAnimator.setDuration(1600);
mValueAnimator.start();
}
上面ObjectAnimator 和ValueAnimator 都是实现一模一样的效果,通过使用用封装好的ObjectAnimator ,我们就可以少写很多代码,
多个动画同时执行
补间动画可以多个动画同时执行,属性也可以,参考多个属性动画合成
插值器
插值器其实就是计算进度的一个接口或实现类,根据时间流逝的比例,来得出属性值变化的比例
自定义插值器继承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.
*
* @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);
}
从方法参数可以得知,入参input是0到1.0;我们可以理解为百分比进度,告诉我们动画已经进行到什么时候了,如果是3秒动画,入参0.5,就是告诉我们已经进行了1.5秒了;但是有的时候我们需要做曲线变化,我们就需要重写getInterpolation方法,注意的是虽然入参范围是0~1,但是返回值可以在0 ~ 1范围外,比如一些弹性动画在后面的时候会谈到实际位置之外。
以一个线性差值器为例
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolator {
public float getInterpolation(float input) {
return input;
}
}
我们可以看到getInterpolation直接返回input,因为线性插值器不需要做曲线处理,所以直接入参值了;
android默认帮我们写很多插值器
估值器
插值器在返回进度值后就要算出具体的属性值了;
自定义插值器继承TypeEvaluator ,首先我们看接口定义
public interface TypeEvaluator<T> {
/**
* This function returns the result of linearly interpolating the start and end values, with
* <code>fraction</code> representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: <code>result = x0 + t * (x1 - x0)</code>,
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
* and <code>t</code> is <code>fraction</code>.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue The start value.
* @param endValue The end value.
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
public T evaluate(float fraction, T startValue, T endValue);
}
就是在给出当前进度和初始值和结束值,来计算属性值,Android里面默认提供了如下几种估值器
- ArgbEvaluator
- FloatArrayEvaluator
- FloatEvaluator
- IntArrayEvaluator
- IntEvaluator
- PointFEvaluator
- RectEvaluator
下面是IntEvaluator整形估值器源码
public class IntEvaluator implements TypeEvaluator<Integer> {
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}
实际上就是线性计算进度,比如在进度0.5的时候,就计算返回开始值和结束值中间的值;
我自定义使用线性插值和Int估值器,打印如下,实际上插值器的返回结果就是估值的一个入参。
getInterpolation 0.0
evaluate fraction:0.0 s:-16711936 e:-65536
getInterpolation 0.0
evaluate fraction:0.0 s:-16711936 e:-65536
getInterpolation 0.0056666667
evaluate fraction:0.0056666667 s:-16711936 e:-65536
getInterpolation 0.011
evaluate fraction:0.011 s:-16711936 e:-65536
感觉好像针对线性变换,重写插值器和估值器都可以,但是估值器往往自身就会一些数学计算在里面,计算起来不是很方便。而且有的估值器,估值起来不是很简单,比如做颜色变换的动画,从蓝色变成绿色,如果此时插值器返回当前进度是0.5(动画执行到一半),那么估值器应该返回一个什么颜色呢,插值器只返回一个当前进度;查看ArgbEvaluator估值器,我们可以清楚看到其处理逻辑。
public class ArgbEvaluator implements TypeEvaluator {
/**
* 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);
}
}
分析上面代码,通过5个步骤估值器计算出此时应该显示的颜色:
- 首先计算出初始颜色和结束颜色的ARGB值
- 对各个ARGB值进行2.2的幂次处理
- 根据进度计算值ARGB值,
- 对各个ARGB值开2.2次方
- 重新组合成int型颜色值
总结
- 属性动画是对属性做动画,属性要实现动画
- 插值器就是反应属性变化的快慢,具体快慢的标准可以由插值器函数求导得到;
- 估值器具体计算属性值,一般计算的方法就是start+(end-start)*fraction
- 插值器的返回结果就是估值的入参