本帖最后由 大山哥哥 于 2017-7-25 16:50 编辑
在Android中学习动画的时候有一个东西大家应该都不陌生,就是Interpolator这个类,翻译过来是插值器的意思。但是大多数同学还是对它一头雾水,不知道是什么,也不知道用来干什么,所以在设置动画的时候就从来不设置这个值。但是在实际开发应用的过程中,这个插值器却是非常有用的,我们经常会用它来实现一些酷炫的动画效果。那我们今天就从源码开始分析,看一看Interpolator究竟怎么用。
Interpolator 这个时间插值类,其主要使用在动画中,其作用主要是控制目标变量的变化值进行对应的变化。通俗一点来解释,举个生活中的例子,一辆车要行驶10公里到达目的地,刚开始速度为0,要一点点加速到某个值,再匀速行驶一段时间,最后再慢慢减速,最终到达目的地,速度为0.那我们如果用动画来描述的话,怎么来表示加速、减速的过程呢,例如位移动画,就是说了下时间,位移多长,根本没有速度的设置啊,所以Interpolator就登场了,用它我们就可以控制动画运行的进度。
首先看一下这个类的源码。
[AppleScript] 纯文本查看 复制代码 /**
* An interpolator defines the rate of change of an animation. This allows
* the basic animation effects (alpha, scale, translate, rotate) to be
* accelerated, decelerated, repeated, etc.
*/
public interface Interpolator extends TimeInterpolator {
// A new interface, TimeInterpolator, was introduced for the new android.animation
// package. This older Interpolator interface extends TimeInterpolator so that users of
// the new Animator-based animations can use either the old Interpolator implementations or
// new classes that implement TimeInterpolator directly.
}
What?是个接口,没有代码,还没有方法声明,那岂不是个废物,仔细一看它有个父接口,我们看一看父接口的源码。
[AppleScript] 纯文本查看 复制代码 /**
* 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);
} 总算是有个方法了,在所继承的接口中有一个方法 float getInterpolation(float input)。在这个方法中,传入的值是一个0.0~1.0的值,返回值可以小于0.0也可以大于1.0。你可以这么理解:如果在Animation中时间是正常走的,你设置了200ms,现在走到了100ms了,那么按照线性来说现在应该是走了一半的路程也就是0.5。现在就把这0.5传递给Interpolator 让 Interpolator 告诉我走到一半时间的时候此时在哪里,这也就是 Interpolator 的原理。那如果我传的值是0.3,那说明进度是比线性的少的,但后面我们可以加速把这个进度补回来。所以可以灵活的控制这个方法的返回值达到我们的目的。而input的值是系统回调的时候传入的,也就是时间的进度,例如传过来0.5代表时间过去一半了,这时候你设置的方法的返回值就是运行效果的总进度。旋转动画就是角度,位移就是总距离等。
那让我们自己写不会写啊,上学的时候数学没好好听啊,怎么办?AndroidSDK里有几个已经实现好的,我们来捋一捋。
1.AccelerateDecelerateInterpolator 在动画开始与结束的地方速率改变比较慢,在中间的时候加速
2.AccelerateInterpolator 在动画开始的地方速率改变比较慢,然后开始加速
3.AnticipateInterpolator 开始的时候向后然后向前甩
4.AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值
5.BounceInterpolator 动画结束的时候弹起
6.CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线
7.DecelerateInterpolator 在动画开始的地方快然后慢
8.LinearInterpolator 以常量速率改变
9.OvershootInterpolator 向前甩一定值后再回到原来位置
具体的效果用文字描述可能大家想象不出来,可自行写个示例用一用看看效果,会有更直观的感受。接下来,我们挑选几个看一看源码实现。
LinearInterpolator 线性的插值器,就是匀速的插值器,也是动画默认的插值器,不设置就会默认选择它。执行的函数图如下:
[AppleScript] 纯文本查看 复制代码 /**
* An interpolator where the rate of change is constant
*/
@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();
}
}
主要关注getInterpolation这个方法,Linear就是线性,匀速的,所以方法直接把input的值返回,也就是动画的效果进度和时间的进度保持一致,匀速播放完毕。
BounceInterpolator
再来看一个复杂一点的,这个效果也是非常常用的一个,就是小球自由落体掉下去然后弹起来,再落下去弹起来,逐渐停止。
[AppleScript] 纯文本查看 复制代码 /**
* An interpolator where the change bounces at the end.
*/
@HasNativeInterpolator
public class BounceInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
public BounceInterpolator() {
}
@SuppressWarnings({"UnusedDeclaration"})
public BounceInterpolator(Context context, AttributeSet attrs) {
}
private static float bounce(float t) {
return t * t * 8.0f;
}
public float getInterpolation(float t) {
// _b(t) = t * t * 8
// bs(t) = _b(t) for t < 0.3535
// bs(t) = _b(t - 0.54719) + 0.7 for t < 0.7408
// bs(t) = _b(t - 0.8526) + 0.9 for t < 0.9644
// bs(t) = _b(t - 1.0435) + 0.95 for t <= 1.0
// b(t) = bs(t * 1.1226)
t *= 1.1226f;
if (t < 0.3535f) return bounce(t);
else if (t < 0.7408f) return bounce(t - 0.54719f) + 0.7f;
else if (t < 0.9644f) return bounce(t - 0.8526f) + 0.9f;
else return bounce(t - 1.0435f) + 0.95f;
}
/** @hide */
@Override
public long createNativeInterpolator() {
return NativeInterpolatorFactoryHelper.createBounceInterpolator();
}
}
看源码,这个getInterpolation就是一个关于时间的分段函数,在时间的不同阶段函数体是不一样的,所以最终得到了这个效果。由此我们可以大胆的想象一下,如果我们自己定义类实现这个接口,然后实现getInterpolation方法,用我们自己的函数来控制,不就能自己来编写动画插值器了吗。这里我们放出material design里非常火的一张图片。
这里有一大堆的动画插值器的函数图,每一个都非常实用,选择合适的插值器就能让动画做的更加细腻,用户体验更佳,所以如果我们能实现这些插值器,就差不多覆盖所有的使用场景了。那光看图也想不出来函数怎么实现啊,不要着急,我这里都准备好了,就在附件里面,自行取用吧。
|