Android 属性动画——插值器、估值器、关键帧

   动画中除了一些简单和组合的动画效果,还有很多其它的小功能,比如说这里的插值器、估值器、关键帧。

  • 插值器 

    对 Android 动画来说,不管是视图动画还是属性动画,都是有插值器的,那什么是插值器呢?就是控制动画随着时间轴的变化而变换的效果。

    而 Android 动画也自带了一些插值器,如:加速插值器、减速插值器、循环插值器等等,接下来我们看看 Android 动画自带的插值器,我们以循环插值器为例:

    代码:

// 设置自带插值器
    private void setInterpolator(){
        ObjectAnimator animator = ObjectAnimator.ofFloat(imageView,"translationX",0f,200f);
        animator.setDuration(2000);
        // 设置自带的循环差值器,循环 2 次
        animator.setInterpolator(new CycleInterpolator(2));
        animator.start();
    }

    看一下效果:


    我们来看一下它的源码,先看一下 setInterpolator() 方法的源码:

@Override
    public void setInterpolator(TimeInterpolator value) {
        if (value != null) {
            mInterpolator = value;
        } else {
            mInterpolator = new LinearInterpolator();
        }
    }

    可以看到,它是在为 mInterpolator 赋值,那么什么时候会用到 mInterpolator 呢?我们继续看看:

void animateValue(float fraction) {
        fraction = mInterpolator.getInterpolation(fraction);
        mCurrentFraction = fraction;
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            mValues[i].calculateValue(fraction);
        }
        if (mUpdateListeners != null) {
            int numListeners = mUpdateListeners.size();
            for (int i = 0; i < numListeners; ++i) {
                mUpdateListeners.get(i).onAnimationUpdate(this);
            }
        }
    }

    我们可以看到,上面这个方法中使用了 mInterpolator  的 getInterpolation() 方法,并把获取到的值赋给了动画的属性,那么我们看看 CycleInterpolator 的 getInterpolation() 方法:

public float getInterpolation(float input) {
        return (float)(Math.sin(2 * mCycles * Math.PI * input));
    }

    我们看到,其实就是随着传进来的值,再返回一个随着公式变化的值,而这个 input 就代表着时间,0.0f 表示动画开始时,1.0f 表示动画结束时。

    既然我们知道了这个是怎么实现的,那我们就可以自己来实现一个插值器,我们新建一个 MyInterpolator 实现 Interpolator 接口,重写 getInterpolation() 方法,然后自己找一个公式:

public class MyInterpolator implements Interpolator {
    @Override
    public float getInterpolation(float input) {
        // 这里我们使用高中经常使用的正弦表达式
        return (float) (2*Math.sin(2*Math.PI*input+0.5f*Math.PI));
    }
}

    然后我们给动画设置我们自定义的插值器,来看看效果:


    可以看到,我们已经实现了我们自定义的插值器。

  • 估值器

    说了插值器了,那什么是估值器呢?插值器是通过时间来得到位置,并对其进行操作的一种功能,那么估值器与之对应,估值器是返回动画当前时间的属性值,让我们来进行有些操作。

    Android 动画中也有自带的估值器,如 FloatEvaluator、IntEvaluator 等等,在 API 21,Android 5.0 以后也推出了 PointFEvaluator,返回一个点的属性,可以对其 X 轴、Y 轴同时进行操作,那么如果是 Android 5.0  以前呢?我们要怎么来实现它呢?所以接下来我们通过自定义一个 PointEvaluator 来了解一下估值器的使用:

    首先我们定义一个 PointT.java 文件:

public class PointT {
    // 横纵坐标
    private float x;
    private float y;

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

    public float getX() {
        return x;
    }

    public void setX(float x) {
        this.x = x;
    }

    public float getY() {
        return y;
    }

    public void setY(float y) {
        this.y = y;
    }
}

    接下来,我们定义一个 PointTEvaluator.java 实现 TypeEvaluator<PointTEvaluator> 接口,重写 evaluate() 方法:

public class PointTEvaluator implements TypeEvaluator<PointT> {

    // 传进来的 fraction 为动画已经运行的时间,0.0 为开始,1.0 为结束
    // startValue 为动画起始点坐标,endValue 为动画结束点坐标
    @Override
    public PointT evaluate(float fraction, PointT startValue, PointT endValue) {
        // X 轴已运行距离
        float d = fraction*(endValue.getX()-startValue.getX());
        // 设置 X 轴坐标,为起始 X 坐标加上已经运动的 X 距离
        float x = startValue.getX()+d;
        // Y 轴按照正弦函数图像运行,d/200 是为了防止像素过大,运动出边界
        // * 200 是因为正弦函数值域为 -1 到 1,像素大小无法分辨动画
        float y = (float) (startValue.getY() + (Math.sin((d/200)*Math.PI)) * 200);
        return new PointT(x,y);
    }
}

    接下来我们来设置估值器:

// 设置估值器
    private void setEvaluator(){
        ValueAnimator animator = new ValueAnimator();
        animator.setDuration(2000);
        // 设置起止属性
        animator.setObjectValues(new PointT(imageView.getX(),imageView.getY()),
                new PointT(imageView.getX()+400f,imageView.getY()));
        // 设置估值器
        animator.setEvaluator(new PointTEvaluator());
        // 设置动画更新监听器
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 拿到估值器设置的 X、Y
                PointT pointT = (PointT) animation.getAnimatedValue();
                // 为 imageView 设值
                imageView.setX(pointT.getX());
                imageView.setY(pointT.getY());
            }
        });
        animator.start();
    }

    来看看效果图:


    可以看到,我们平移的时候,图像按照我们想要正弦函数图像运动,那么估值器的简单使用就是这样,还有一种写法来实现这个估值器,我们看看,比较容易懂就不做注解了:

/**
          * 第二种方法设置估值器
         */
        animator.setEvaluator(new TypeEvaluator<PointT>() {
            @Override
            public PointT evaluate(float fraction, PointT startValue, PointT endValue) {
                float d = fraction*(endValue.getX()-startValue.getX());
                float x = startValue.getX()+d;
                float y = (float) (startValue.getY() + (Math.sin((d/200)*Math.PI)) * 200);
                return new PointT(x,y);
            }

        });

  • 关键帧

    说了插值器和估值器,那么什么是关键帧呢?先看一组代码:

private void keyFrame(){
        Keyframe keyframe1 = Keyframe.ofFloat(0,0);
        Keyframe keyframe2 = Keyframe.ofFloat(0.25f,200);
        Keyframe keyframe3 = Keyframe.ofFloat(0.75f,100);
        Keyframe keyframe4 = Keyframe.ofFloat(1,300);
        PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("translationX",keyframe1,keyframe2,keyframe3,keyframe4);
        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(imageView,holder);
        animator.setDuration(2000);
        animator.start();
    }

    然后放效果图:


      可以看到,我们的动画先向前,再向后,又向前,所以我们的代码什么意思呢?我们来看一下 Keyframe.ofFloat() 这个方法:

public static Keyframe ofFloat(float fraction, float value) {
        return new FloatKeyframe(fraction, value);
    }

    这个方法有两个参数,一个 fraction,上面我们经常使用这个参数,相信大家已经知道了,这个就是动画运行的时间,0.0 表示动画开始时,1.0 表示动画结束时,value 表示属性,这个方法就是说在给动画定义一些关键的节点赋予属性来实现动画。

    好了,插值器、估值器和关键帧的简单讲解就是这么多,主要是提供一个思路,大家可以按照自己的需求实现更加复杂丰富的动画效果。

    项目地址:源代码

    

猜你喜欢

转载自blog.csdn.net/young_time/article/details/80888799