欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android中动画之四属性动画高级用法二

程序员文章站 2022-05-04 12:04:30
...

一.概况

目的

在上一篇中记录了“对任何类的任何属性进行动画”基础,那只是简单的对属性值(int,float类型)进行动画处理。下面我们对非int,float类型的属性值进行处理。

涉及的知识点

  • 自定义估值器
  • ofObjec和ofArgb方法介绍使用
  • ValueAnimator的高级用法
  • ObjectAnimator的高级用法

二.自定义估值器

2.1先看一个系统自带的估值器FloatEvaluator

public class FloatEvaluator implements TypeEvaluator<Number> {
    public Float evaluate(float fraction, Number startValue, Number endValue) {
        float startFloat = startValue.floatValue();
        return startFloat + fraction * (endValue.floatValue() - startFloat);
    }
}

自定义估值器只要重写evaluate方法就好。

从上面的FloatEvaluator的实现我们可知实现自定义估值器刚需就是重写evaluate方法。下面分析一下其各个参数含义。

分析重写方法中的三个参数

  1. fraction:由插值器获得的动画的完成度
  2. startValue:动画的开始值
  3. endValue:动画的结束值

三.View背景颜色动画

目的

通过View背景颜色动画来熟悉一下ofArgb和ofObject方法。

注意

  • ValueAnimator和ObjectAnimator都有ofArgb方法,其是用来实现颜色变化的只不过它引入的比较晚API21以后才引入。
  • 通过ValueAnimator和ObjectAnimator的ofObject也可以实现背景颜色动画,且兼容性比上面ofArgb方法好。

先看效果

Android中动画之四属性动画高级用法二

实现代码

布局文件很简单这里就不贴了,只是包含一个TextView

四种实现方法

第一种:

使用ObjectAnimator.ofArgb,

    private void startAni() {
        //API21以上能用
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            ObjectAnimator ani = ObjectAnimator.ofArgb(showTv,"backgroundColor",0xffff0000,0xff00ff00,0xff0000ff);
            ani.setDuration(4000);
            ani.start();
        }
    }

比较好理解ofArgb前俩个参数一个目标对象,一个属性名称;后面是32位的int值用来表示颜色值。

第二种:

使用ValueAnimator.ofArgb并设置监听器,自己更改背景颜色。

    private void startAni() {
        //API21以上能用
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            ValueAnimator ani = ValueAnimator.ofArgb(0xffff0000,0xff00ff00,0xff0000ff);
            ani.setDuration(4000);
            ani.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    showTv.setBackgroundColor((Integer) animation.getAnimatedValue());
                }
            });
            ani.start();
        }
    }

第三种

使用系统的ArgbEvaluator估值器,这样能提供较好兼容性

    private void startAni() {
        ObjectAnimator ani = ObjectAnimator.ofObject(showTv,"backgroundColor",new ArgbEvaluator(),0xffff0000,0xff00ff00,0xff0000ff);
        ani.setDuration(4000);
        ani.start();
    }

第四种

使用系统的ArgbEvaluator估值器,这样能提供较好兼容性

    private void startAni() {
        ValueAnimator ani = ValueAnimator.ofObject(new ArgbEvaluator(),0xffff0000,0xff00ff00,0xff0000ff);
        ani.setDuration(4000);
        ani.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                showTv.setBackgroundColor((Integer) animation.getAnimatedValue());
            }
        });
        ani.start();
    }

分析

上面的第三种和第四种方法相对第一种,第二种方法提供了较好的兼容性。

四.ValueAnimator的高级用法

4.1背景

通过上一篇Android中动画之三属性动画高级用法一,我们知道对任意类(Object)的任意属性(abc)做动画,想要成功要具备俩个条件如下:
1.Object必须提供setAbc方法;如果动画的时候没有传递初始化值,那么还要有getAbc方法,因为系统要去取abc属性的初始值(如果不满足这个条件,直接crash);
2.Object的setAbc对属性abc所做的改变必须通过某种方法表现出来,最常见的就是UI的变化。(如果不满足这个条件,不会crash,但是没有效果)
我们想要实验一下ValueAnimator的ofObject方法,想要直观的看到动画效果,那我们就要一个使用场景。

4.2场景

在一个自定义View中实现一个圆完成平移动画。可能看起来很简单,但是关键是理解其中的原理。

4.3分析

先看一下ValueAnimator的ofObject方法需要什么

public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)

从上面可知想要对任意类的任意属性做动画,那么我们需要自己的估值器TypeEvaluator,类的属性(这里可以是基本类型如int,float也可以是任何的类)数组;

4.4解决方案

自定义一个View,在里面连续画圆,也就相当于对View的位置类属性进行了动画处理。

4.5效果和具体代码

效果如下图

Android中动画之四属性动画高级用法二

具体代码

圆Pointer(圆就是一个大的点)如下:

主要是坐标属性

public class Pointer {
    private float x;
    private float y;

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

    public float getX() {
        return x;
    }

    public float getY() {
        return y;
    }
}

自定义估值器

坐标各自求值,并取得变化后的Pointer对象。

public class PointerEvaluator implements TypeEvaluator<Pointer>{
    @Override
    public Pointer evaluate(float fraction, Pointer startValue, Pointer endValue) {
        float x = startValue.getX() + fraction*(endValue.getX() - startValue.getX());
        float y = startValue.getY() + fraction*(endValue.getY() - startValue.getY());
        return new Pointer(x,y);
    }
}

自定义View

public class AnimatorView extends View{
    private static final float RADIUS = 50;

    private Paint mPaint;
    private Pointer curPointer;

    public AnimatorView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        //初始化画笔
        initPaint();
    }

    private void initPaint() {
        mPaint = new Paint();
        mPaint.setColor(Color.RED);
    }

    @Override
    protected void onDraw(Canvas canvas) {

        if (curPointer == null){
            //最初
            //初始化圆(位置主要是)
            curPointer = new Pointer(RADIUS,RADIUS);
            //画出来
            drawCicle(canvas);
            //开始动画
            startAni();
        }else {
            drawCicle(canvas);
        }
    }

    private void startAni() {
        Pointer startP = new Pointer(RADIUS,RADIUS);
        Pointer endP = new Pointer(getWidth()-RADIUS,getHeight()-RADIUS);
        final ValueAnimator ani = ValueAnimator.ofObject(new PointerEvaluator(),startP,endP);
        ani.setDuration(4000);
        ani.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                curPointer = (Pointer) animation.getAnimatedValue();
                //关键:获取新位置后调用invalidate,执行画圆操作。
                invalidate();
            }
        });
        ani.start();
    }

    private void drawCicle(Canvas canvas) {
        canvas.drawCircle(curPointer.getX(),curPointer.getY(),RADIUS,mPaint);
    }
}

上面的AnimatorView中流程如下:

  1. 构造方法中初始化画笔
  2. onDraw中判断curPointer是否为空,为空刚进入绘制过程。初始化Pointer;开始画圆;开始动画。
  3. 在动画的回调监听中获取现在圆的位置,调用invalidate重新执行onDraw,周而复始随之动画的进行不断改变圆的位置,达到对圆添加动画的目的。

五.ObjectAnimator的高级用法

实现效果

自定义View中圆平移并且颜色发生变化,这里实现的效果比较简单,用现有的属性动画组合可以实现,但是重点是理解ObjectAnimator的ofObject方法以及自定义估值器。

Android中动画之四属性动画高级用法二

看起来简单,实现起来还是有些要注意的地方的

Activity中对自定义View不做处理,xml中添加自定义View设置宽高为填充满就好。下面几个是关键的类。

自定义View

public class ArgbAndPosView extends View{
    //圆的半径
    private static final int RADIUS = 50;

    //画笔
    private Paint mPaint;
    //自定义View的属性值
    private Pointer pointer;

    //动画属性的get方法
    public Pointer getPointer() {
        return pointer;
    }
    //动画属性的set方法
    public void setPointer(Pointer pointer) {
        this.pointer = pointer;

        //设置颜色值
        mPaint.setColor(pointer.getColor());
        //请求刷新
        invalidate();
    }

    public ArgbAndPosView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        //构造方法中初始化画笔
        initPaint();
    }

    private void initPaint() {
        mPaint = new Paint();
        mPaint.setColor(Color.RED);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (pointer == null){
            //初次进入,初始化将被进行动画处理的属性值
            pointer = new Pointer(RADIUS,RADIUS,Color.RED);
            //画出来
            drawCircle(canvas);
            //开始动画
            startAni();
        }else {
            //每次刷新都重画
            drawCircle(canvas);
        }
    }

    private void startAni() {
        //起点属性值
        Pointer startP = new Pointer(RADIUS,RADIUS,0xffff0000);
        //终点属性值
        Pointer endP = new Pointer(getWidth()-RADIUS,getHeight()-RADIUS,0xff00ff00);
        //对本自定义View的pointer属性进行动画处理,动画时间为4000毫秒,从startP到endP,插值器用的默认的
        ObjectAnimator ani = ObjectAnimator.ofObject(this,"pointer",new PointerEvaluator(),startP, endP);
        ani.setDuration(4000);
        ani.start();
    }

    //画圆
    private void drawCircle(Canvas canvas) {
        canvas.drawCircle(pointer.getX(),pointer.getY(),RADIUS,mPaint);
    }
}

自定义View中的属性值Pointer

自定义View中将被进行动画处理的属性,这里是个Pointer类。

public class Pointer {
    private float x;
    private float y;
    private int color;

    public Pointer(float x,float y,int color){
        this.x = x;
        this.y = y;
        this.color = color;
    }

    public float getX() {
        return x;
    }

    public float getY() {
        return y;
    }

    public int getColor() {
        return color;
    }
}

关键,自定义估值器

估值器很重要,分别对Pointer中的属性进行计算处理,获取现在时间点对应的动画属性值。

public class PointerEvaluator implements TypeEvaluator<Pointer>{
    @Override
    public Pointer evaluate(float fraction, Pointer startValue, Pointer endValue) {
        //处理坐标
        float x = startValue.getX() + fraction*(endValue.getX() - startValue.getX());
        float y = startValue.getY() + fraction*(endValue.getY() - startValue.getY());

        //处理颜色,处理过程和ArgbEvaluator中一样
        int startInt = startValue.getColor();
        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 = endValue.getColor();
        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;

        int color = Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);

        return new Pointer(x,y,color);
    }
}

其实上面的Pointer中还可以添加更多的属性以控制更多的动画效果。

五.总结

通过本文可以掌握:

  • 自定义估值器
  • ofObjec和ofArgb方法介绍使用
  • ValueAnimator的高级用法
  • ObjectAnimator的高级用法
    后两个主要是ofObject方法的用法,也就是对任意类的任意属性进行动画操作。