Android属性动画详解(二),插值器和估值器

在上篇博客中主要和你们讨论了属性动画的用法,相信经过上篇博客你们对属性动画的用法已经不陌生了,那么今天就来你们一块儿讨论下属性动画的执行流程。算法

谈到属性动画的执行流程,其实离不开插值器(Interpolator)和估值器(TypeEvaluator)的协同工做,本篇博客就来探究这二者是如何协同工做的。经过本篇博客你讲学到如下知识点: ①理解Interpolator和TypeEvaluator各自的做用 ②理解Interpolator和TypeEvaluator是如何协同的工做的 ③如何自定义TypeEvaluator和Interpolator 好的废话很少说进入正题,以前也谈到过学习这些基础的内容要参考官方文档,那么本篇也是同样,官方文档首先举了一个以下的例子: 以下图所示,描绘了一个假设的对象,它以x属性为动画,表示其在屏幕上的水平位置。 动画的持续时间设置为40 ms,行驶距离为40像素。 每10 ms,这是默认的帧刷新率,对象水平移动10像素。 在40ms结束时,动画中止,物体在水平位置40处结束。这是具备线性插值的动画的示例,意味着物体以恒定的速度移动。 bash

这里写图片描述
什么意思呢?简单来讲就是上图采用了一个匀速动画,在40ms内,咱们假设的对象的位置从0匀速变为40。 咱们能够分析一下中间的某一个时刻,好比(x=20,t=20ms)当t=20ms的时候,时间流逝的百分比是0.5(20/40=0.5),可是这个百分比不是咱们所想要的,咱们关心的是x的变化,那么怎么知道x变化了多少呢?这时候就要用到插值器和估值器了,上述咱们说到是匀速运动,咱们就来看看线性插值器的源码:

/**
 * 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();
    }
复制代码

从getInterpolator能够看到线性插值器的返回值和输入值是相同的。另外getInterpolator()方法接收一个参数input,这个参数的值会随着动画的运动而不断变化,不过它的变化是有规律的,就是根据设定的动画时长匀速增长,变化范围是0到1。也就是说当动画一开始的时候input的值是0,到动画结束的时候input的值是1,而中间的值则是随着动画运行的时长在0到1之间变化的,它表示的是时间流逝的百分比,它的做用就是根据时间流逝的百分比计算出当前属性值改变的百分比,在例中当t=20ms时,由于运行的总时长是40ms因此时间流逝的百分比为20/40=0.5。咱们刚才说过插值器的做用是拿到时间流逝的百分比,那么咱们怎么根据这个百分比来计算属性值改变的百分比呢?这个时候就要用到估值器了,它的做用是根据当前属性改变的百分比来计算改变后的属性值。一样咱们来看看系统提供的整形估算值算法的源码:ide

public class IntEvaluator implements TypeEvaluator<Integer> {
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }
复制代码

能够看到evaluate方法接收三个参数,第一个参数fraction这个参数很是重要它表示动画的完成度,第二和第三个参数分表表示动画的初始值和结束值。所以在上述代码中用结束值减去初始值能够得出它们之间的差,而后乘以fraction这个系数,再加上初始值,那么获得的就是动画当前的值了。 到这里你们应该能够想到上面刚刚提到的input,那么input和fraction有什么关系或者区别呢?答案很简单,input的值决定了fraction。而input的值是由系统通过计算后传入到getInterpolation()方法中的,而后咱们能够本身实现getInterpolator()方法中算法,根据input的值计算出一个返回值,而这个返回值就是fraction。理清楚这两个值的关系特别重要。接着咱们整体来分析一下刚开始的那个实例,当t=20ms的时候,能够计算出input的值是20/40=0.5,即动画已经完成了50%了,getInterpolator()方法的返回值就是fraction,拿到fraction后,咱们就要根据它计算x属性的变化了,IntEvaluator这个估值器中的evaluate的三个分别为:0.5,0,40。代入后计算即:0+0.5*(40-0)=20。能够看出与上图是吻合的当t=20ms时,x=20。这就是属性动画执行的流程,固然咱们说的这种状况是最简单的一种状况。学习

对于插值器和估值器来讲,除了系统提供的外,咱们还能够自定义。实现方式也很简单,由于插值器和估值器都是一个接口,且内部都只有一个方法,咱们只要实现接口就能够了,就能够作出不少绚丽的动画了。其中,自定义插值器须要实现Interpolator或者TimeInterpolator,自定义估值器须要实现TypeEvaluator。可是通常来讲,插值器通常使用系统的就足够了,估值器通常自定义会比较多,另外就是若是要对其余类型(非Int丶float丶color)作动画,必须自定义类型估值算法。接下来咱们就来自定义一个TypeEvaluator来讲下自定义TypeEvaluator的流程,所实现的效果以下: 动画

point
从上述效果能够看到小球作的是抛物线运动,那么如何能让小球作抛物线运动呢?从屋里学的角度来说若是知足x=400t,y=400t²,及x轴作匀速运动y轴作加速运动便可实现小球作抛物线运动。从表达式看只和时间有关系,可是根据时间的变化,横向和纵向的移动速率是不一样的,咱们应该如何实现呢?此时就要重写TypeValue的时候了,由于咱们在时间变化的同时,须要返回给对象两个值,x当前位置,y当前位置,而且须要知足x=400t,y=400t²。咱们能够建立一个PointView类来保存x,y这两个值。PointView的代码以下

public class PointView {

    float x;
    float y;

    public PointView(){

    }

    public PointView(float x, float y) {
        this.x=x;
        this.y=y;
    }

}
复制代码

有了这两个值以后咱们就能够自定义一个TypeEvaluatorui

class PointViewEvaluator implements TypeEvaluator {
    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {

        PointView pointView=new PointView();
        //fraction为时间流逝的百分比
        pointView.x=400*(fraction*2);
        pointView.y=400*(fraction*2)*(fraction*2);
        return pointView;
    }
}
复制代码

从自定义的PointViewEvaluator 中能够看到PointView的x和y是知足x=400t,y=400t²这两个表达式的,另外咱们将时间写死了为2s。自定义好以后咱们就能够直接拿过来用了代码以下this

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private ImageView ivBall;
    private Button btnClick;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ivBall=findViewById(R.id.iv_ball);
        btnClick=findViewById(R.id.btn_click);
        btnClick.setOnClickListener(this);

    }

    @Override
    public void onClick(View view) {

        ValueAnimator valueAnimator=ValueAnimator.ofObject(new PointViewEvaluator(),new PointView(0,0));
        valueAnimator.setDuration(2000);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
            
                PointView pointView= (PointView) valueAnimator.getAnimatedValue();
                ivBall.setX(pointView.x);
                ivBall.setY(pointView.y);
            }
        });
        valueAnimator.start();

    }
}
复制代码

从上述代码中咱们能够发现设置了LinearInterpolator线性的插值器,同时咱们给动画添加了监听,而后获取当前PointView对象(至于为何会返回PointView对象,能够去看上一篇文章讲解的很清晰Android属性动画详解(一),属性动画基本用法)获取的目的就是拿到PointView对象的x和y的值,而后将这两个值设置给小球,就会表现为抛物线运动。以上就是自定义TypeEvaluator的相关流程,理清楚关系逻辑其实很简单,接下来我们一块儿看看自定义Interpolator虽然用的比较少,可是若是用到也要知道怎么去定义它。咱们定义一个动画以最快的速度启动,而后减速运动至一半,最后加速运动至结束效果图以下 lua

自定义插值器
对插值器的定义其实就是一个数学表达式 y=0.5 × ((2t − 1)3+1)与其对应的图形以下
这里写图片描述
从图形中能够看到它是先加速而后加速再加速的一个过程,与其对应的代码以下:

public class PointViewInterpolator implements Interpolator {
    @Override
    public float getInterpolation(float t) {

        float x=2.0f*t-1;
        return 0.5f*(x*x*x + 1.0f);
    }
}
复制代码

不要忘记添加这句话:spa

valueAnimator.setInterpolator(new PointViewInterpolator());
复制代码

而后再运行就会有上面的效果。 好了以上就是对Interpolator和TypeEvaluator的相关讨论。若有谬误欢迎批评指正。 经过本篇博客相信你们对于Interpolator和TypeEvaluator是如何协同工做的,以及对于这二者是如何自定义的相信你们都应该有个清晰的了解了。.net

相关文章
相关标签/搜索