android Interpolator详解

熟悉android动画的同学大家一定都不会对差值器Interpolator感到陌生,但是他是怎么实现在对动画属性变化规律控制的呢,就让我们从他的源码来一探究竟。
 
Interpolator是一个定义了动画变化率的接口,它允许动画可进行非线性的移动,例如加速运动、减速运动甚至将加速减速进行复杂的组合例如bounce运动
package android.animation;

public interface Interpolator {
    //接口中唯一的方法是在输入当前时间占总运动时间百分比后,根据计算返回一个值 那这个值代表的是什么?往下看^_^
    float getInterpolation(float input);
}

以上是Interpolator的接口定义
我们看一下valueAnimator源码中,这个时间百分比是如何起作用的,
animateBasedOnTime这个函数会由AnimationHandler定时调用,并传入当前时间。
boolean animateBasedOnTime(long currentTime) {
    boolean done = false; 
    if (mRunning) {
        final long scaledDuration = getScaledDuration();
        final float fraction = scaledDuration > 0 ?
                (float)(currentTime - mStartTime) / scaledDuration : 1f; //这时的fraction流逝时间占总动画时间的百分比,是随时间线性增长的
        final float lastFraction = mOverallFraction;
        final boolean newIteration = (int) fraction > (int) lastFraction;
        final boolean lastIterationFinished = (fraction >= mRepeatCount + 1) &&
                (mRepeatCount != INFINITE);
        if (scaledDuration == 0) {
            done = true;
        } else if (newIteration && !lastIterationFinished) {
            if (mListeners != null) {
                int numListeners = mListeners.size();
                for (int i = 0; i < numListeners; ++i) {
                    mListeners.get(i).onAnimationRepeat(this);
                }
            }
        } else if (lastIterationFinished) {
            done = true;
        }
        mOverallFraction = clampFraction(fraction);   
        float currentIterationFraction = getCurrentIterationFraction(mOverallFraction);
        animateValue(currentIterationFraction); //currentIterationFraction也是根据fraction线性增长的
    }
    return done;
}
void animateValue(float fraction) {
    fraction = mInterpolator.getInterpolation(fraction); //调用getInterpolation()
    mCurrentFraction = fraction;
    int numValues = mValues.length;
    for (int i = 0; i < numValues; ++i) {
        mValues[i].calculateValue(fraction);  //这设定利用插值器的返回的值给属性,这里的fraction关系跟属性值的关系也是线性的
    }
    if (mUpdateListeners != null) {
        int numListeners = mUpdateListeners.size();
        for (int i = 0; i < numListeners; ++i) {
            mUpdateListeners.get(i).onAnimationUpdate(this);
        }
    }
}
     上面我们提到了很多线性关系,如果我们要让getInterpolation(fraction)处理函数跟fraction是线性关系,那么就相当于
我们需要一个函数f(fraction) = fraction,也就是说这里getInterpolation返回值等于fraction就好,但注意这里返回值的意义,
它所指代的比例已经不再是时间流逝的比例而是距离流逝的比例,这样就引出了我们开头问题的答案。
     其实android sdk中已经默认实现了f(fraction) = fraction,其实写作s = t更合适,s代表距离,t代表时间,这种线性函数关系的Interpolator,就是LinearInterpolator,我们给出源码
看看它的实现是不是如我们所说。
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {

    public LinearInterpolator() {
    }

    public LinearInterpolator(Context context, AttributeSet attrs) {
    }

    public float getInterpolation(float input) {
        return input;     //这里直接返回了input跟我们的预期一致。
    }

    /** @hide */
    @Override
    public long createNativeInterpolator() {
        return NativeInterpolatorFactoryHelper.createLinearInterpolator();
    }
}
既然这是一个数学函数的关系,我们就可以利用坐标系来展示一下这种情况,利用坐标系也方便我们对其他的加速,减速甚至更复杂的加减速混合反向运动给出说明,
并给出这些对应运动的函数表达式
我们还是从最简单的开始画起,线性情况。很明显,这种情况上过小学的都会画。。。。。
                         
这幅图最重要是反映速度的变化率,同样小学知识告诉我们关于s(t) 的公式关于t求导可以得出速度的随时间的变化规律即 v = 1
     所以我们可以对速度变化公式v(t)求积分(这个小学恐怕不行。。。),来得到我们最终需要的s(t)的公式。
接下来我们就来试一下。。。加速运动:v会随时间增大,例如:v = t
那么求积分得到的s(t) = 1/2 t^2,你会发现不对,
因为时间到达1的时候距离没有到达1。。。。。所以这里的v和t的关系,是不能乱写的, 加速的表达方式是速度变化率,只要v和t是正相关就可以,
最终的结果我们需要利用s(t)的公式来保证,当t=0的时候,s要等于0;当t=1的时候,s也需要等于1,所以这里最后的
			getInterpolator(float input){ return t^2 } 就会返回一个加速的插值器。
     减速就是速度与时间负相关, v = -t ,同样的你会得到是s(t) = -(1/2)t^2,这他喵的一样有问题,那么-kt的k的系数要怎么去算出一个合理的k值呢,
我不得不祭出我的小学知识的最终奥义----------方程组(⊙﹏⊙)b来解决这一难题。
v = a - kt
注意这里我们考虑的是正向运动,所以v要有初值a
s(t) = at - 1/2kt^2
带入0。。。甭带了。。。都没了。。。
带入1  a - 1/2k = 1 所以大招得出的是符合条件的ak关系,a = 1/2k + 1 ,注意这里k必须为正值,(若为负值,其实就是加速运动了。。。)
我们随便带入一组数字。。。k=2,a=2,注意这里的k其实就是加速度绝对值,我们需要的加速度越大,把k变大就好了~
 				s(t) = 2t - t^2(getInterpolator(float input){ return 2t - t^2 }
    说完自己理解的减速插值器,看看android sdk里的DecelerateInterpolator是不是和我们的不谋而合呢
    
public float getInterpolation(float input) {
    float result;
    if (mFactor == 1.0f) {
        result = (float)(1.0f - (1.0f - input) * (1.0f - input));
    } else {
        result = (float)(1.0f - Math.pow((1.0f - input), 2 * mFactor));
    }
    return result;
}

private float mFactor = 1.0f;
   。。。我擦嘞,这哪里一样,要翻车呀三塞。。。
   不要慌,让我们来仔细分析一下他的是怎么个意思,看看我们的s(t) = 2t - t^2能否自圆其说。如果不傻的同学,算一下第一个if中mFactor等于1的情况,
  会发现结果是一样的,它提供了接口可以让我们设置mFactor的值,那我们看看mFactor不等于1的情况会怎样。
   s(t) = 1 - (1-t)^(2*mFactor) 
     我输了。。。他这一手果然技高一筹(虽然我们的办法low了一些,但是达到效果是没问题的)
  那我们举一反三一下就该知道,android自带的AccelerateInterpolator是怎么回事了, S(t) = t^(mDoubleFactor)
  (这里有一个疑惑,Decelerate中的mFactor是一个float型,Accelerate中的mDoubleFactor = 2 * mFactor,但是声明DoubleFactor时是一个都double,
   不知道这个脱裤子放屁的行为是要闹那样,当然何必在意这些细节,可能是三塞多虑了)
 
  再看一个先减速后加速的例子吧,同样的我们自己先来抛砖(凭什么我就得是砖。对,不许糟践砖。。。)引玉一把,然后再对照Google大大们的做法自惭形秽一次。
  先加速后减速会存在两种情况
  (1)在某一段时间内加速,某一段时间内减速
  (2)在某一距离内加速,某一段距离内减速 (这个我们用估值器以后用Evaluator来做)
  (1)时间加减速情况:
	我们传入一个变量标识时间百分比,这个好像直接传给我们了。。。,那我们就对fraction进行一个判断,比如说实现一个
   fraction <= 0.5 减速 fraction > 0.5加速
(为了减少脑细胞的坏死,我们就都拿一次幂来解释)
   前半段:速度规律:a + kt
   后半段:速度规律:(a + k/2) - kt (k绝对值相等是为了展现力的对称之美,这里面也有力与美的完美集合)
   出来吧方程组!!!
   s(t) = at + 1/2kt^2                t<0.5
   s(t) = at + 1/2kt - 1/2kt^2        t>=0.5
   1 = a + 1/2k - 1/2k
   通过计算我们得出一种情况,即k可以为任意值,a必须等于1
   我们尝试k=2带入
   s(t) = t + t^2  t<0.5
   s(t) = 2t - t^2 t<=0.5
   我们尝试如果枢轴t不是0.5,可以是[0,1]范围内的任意值的方程又会是怎样呢,我们假设枢轴为Z
   s(t) = at + 1/2kt^2                t<Z
   s(t) = at + 1/2*Zkt - 1/2kt^2      t>=Z
   1   =  a + 1/2*Zk - 1/2kt  这种情况下 k就会和Z、a相关
   
class AcDcInterpolater(val timePivot: Float, val accelebration: Float) : Interpolator {

    val factor: Float = 1 - 1 / 2.0f * timePivot * accelebration + 1 / 2.0f * accelebration

    override fun getInterpolation(fraction: Float): Float {

        when (fraction.compareTo(timePivot)) {
            -1 ->
                return (factor * fraction + 1 / 2f * accelebration * Math.pow(fraction.toDouble(), 2.0)).toFloat()
            else ->
                return (factor * fraction + 1 / 2f * timePivot * accelebration - 1 / 2f * accelebration * Math.pow(fraction.toDouble(), 2.0)).toFloat()
        }
    }
}
上面的代码存在的问题是会导致反向运动,当使用了不合适的参数时。
那看看google大大们的怎么搞
public class AccelerateDecelerateInterpolator extends BaseInterpolator
        implements NativeInterpolatorFactory {

    public float getInterpolation(float input) {
        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
    }
 当我们沉浸在利用小学数学解题的快乐中无法自拔的时候,Google大大们已经开始玩儿起啦高中数学。。。。
就先到这里吧。。。总之,心态已崩


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值