属性动画原理解析

本文详细探讨了Android的属性动画原理,从视图动画到属性动画的转变,重点分析了属性动画的工作流程,包括ValueAnimator的刷新机制、时间插值器和TypeEvaluator的计算规则。通过对源码的解读,揭示了属性动画如何利用Choreographer与屏幕刷新机制同步执行,以及ObjectAnimator如何通过反射改变对象属性。文章旨在帮助开发者更好地理解和运用属性动画。
摘要由CSDN通过智能技术生成

前言

属性动画作为现在开发中常用的动画,我们需要对他有比较详细的了解,才能够在开发中随心和高效的使用。在了解属性动画之前我们先了解Android前期的视图动画

一、视图动画

视图动画是为View对象添加动画效果的功能,如果想为非对象动画添加效果,则必须自己实现自己的代码才能做到。

视图动画仅公开 对象的部分方面来供您添加动画效果,比如旋转、错放、移动

视图动画只是在绘制视图的位置进行修改,而不会修改实际的视图本身。例如,为某个按钮添加了移动动画,该按钮会正确绘制,但能够点击按钮的实际位置并回修更改,我们必须通过实现自己的逻辑来处理此事件。

二、属性动画

2.1 属性动画概览

属性动画是系统提供的一个强健框架。可以定义一个随时间更改任何对象属性的的动画,属性动画会在指定时长内更改属性(对象中的字段)的值。

属性动画特性:

  • 时长:指定动画执行的时长,默认为300毫秒
  • 时间插值:更多动画当前已经播放的时长来计算属性的值
  • 重复计数和行为:是否重复播放动画以及重复播放动画次数,是否发现播放动画。
  • Animator集:将多个动画分成多个逻辑,他们可以一起播放、按顺序播放或者在指定延迟后播放
  • 帧刷新延迟:您可以指定动画帧的刷新频率。默认设置为每 10 毫秒刷新一次,但应用刷新帧的速度最终取决于整个系统的繁忙程度以及系统为底层计时器提供服务的速度

2.2 属性动画使用

属性的动画的相关类在 android.anmition 中,视图动画的相关类在android.view.animation包中,我们在使用的过程中可以留心观察一下。接下来我们了解属性动画的使用。

2.2.1 对View对象设置属性动画

下面结合ObjectAnimator以及BounceInterpolator来完成一个view的translationY的移动动画

    private void startAnimation() {
        int[] tarViewLocation = new int[2];
        int[] endViewLocation = new int[2];
        targetView.getLocationInWindow(tarViewLocation);
        viewEnd.getLocationInWindow(endViewLocation);
        float endLocation = endViewLocation[1] - tarViewLocation[1] - targetView.getHeight();
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(targetView, "translationY", 0f, endLocation);
        objectAnimator.setInterpolator(new BounceInterpolator());
        objectAnimator.setDuration(2000);
        objectAnimator.start();
    }

image

2.2.2 直接使用属性动画特性

我们了解属性动画处理作用于View视图以外,还可以单独使用,我们可以监听相关的值的变化来处理我们自身的逻辑。下面我们通过ValueAnimator实现一个水杯里装水的效果。

第一步: 自定一个WaterView,完成水的绘制

public class WaterView extends View {
    private static final int DEFAULT_COLOR = Color.BLUE;
    private int color;
    //水位高度-单位像素
    private int waterPx;
    //水位结束高度
    private int waterEndYpx;
    //视图宽
    private int viewWidth;
    //视图宽
    private int viewHeight;
    private Paint paintWater;
    //波浪path
    private Path pathWave;
    //波浪
    private int waveWidth;
    //波浪高度 px
    private int waveHeight = 20;
    //当前绘制水位的起始Y坐标
    private int currentDrawHeight;
    //水位上升变化值
    private int dy;   
    
    public WaterView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.WaterView, 0, 0);
        try {
            color = typedArray.getColor(R.styleable.WaterView_color, DEFAULT_COLOR);
        } finally {
            typedArray.recycle();
        }
        init();
    }    

    private void init() {
        paintWater = new Paint();
        paintWater.setColor(color);
        paintWater.setAntiAlias(true);
        pathWave = new Path();
    }
    
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        viewWidth = w;
        viewHeight = h;
        waveWidth = viewWidth;
        currentDrawHeight = viewHeight;
    }    

    @Override
    protected void onDraw(Canvas canvas) {
        pathWave.reset();
        if (currentDrawHeight == waterEndYpx) {
            pathWave.moveTo(0, currentDrawHeight);
            pathWave.lineTo(waveWidth, currentDrawHeight);
        } else {
            pathWave.moveTo(-waveWidth + dy, currentDrawHeight);
            for (int i = -waveWidth; i < viewWidth + waveWidth; i += waveWidth) {
                pathWave.rQuadTo(waveWidth / 4, -waveHeight, waveWidth / 2, 0);
                pathWave.rQuadTo(waveWidth / 4, waveHeight, waveWidth / 2, 0);
            }
        }
        pathWave.lineTo(viewWidth, viewHeight);
        pathWave.lineTo(0, viewHeight);
        if(waterPx > 0){
            canvas.drawPath(pathWave, paintWater);
        }
        super.onDraw(canvas);
    }

}


第二步: 添加属性动画,实施改变水位的高度

    /**
     * @param duration    动画执行时长
     * @param waterHeight 水位高度,单位像素
     */
    public void pourWater(long duration, int waterHeight) {
        waterEndYpx = viewHeight - waterHeight;
        waterPx = waterHeight;
        start(duration);
    }

    private void start(long duration) {
        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, waterPx);
        valueAnimator.setInterpolator(new AccelerateInterpolator());
        valueAnimator.setDuration(duration);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                dy = (int) animation.getAnimatedValue();
                currentDrawHeight = viewHeight - dy;
                invalidate();
            }
        });
        valueAnimator.start();
    }

第三步: 效果图

image

2.3 属性动画工作原理

了解完属性的动画的概念和使用后,接下就是其核心的实现原理和本质,只有了解了背后的逻辑后,我们在开发中就能更随心所欲的使用。

2.3.1 了解ValueAnimatro的刷新机制

了解背后的原理就需要从源码入手,下面结合具体的使用来分析使用逻辑。

2.3.1.1 ValueAnimator对象的创建

我们可以通过下面的方法来创建ValueAnimator对象

ValueAnimator valueAnimator = ValueAnimator.ofInt(0, waterPx);

源码的实现:

    public static ValueAnimator ofInt(int... values) {
        ValueAnimator anim = new ValueAnimator();
        anim.setIntValues(values);
        return anim;
    }
    //设置int属性值
    public void setIntValues(int... values) {
        if (values == null || values.length == 0) {
            return;
        }
        if (mValues == null || mValues.length == 0) {
            setValues(PropertyValuesHolder.ofInt("", values));
        } else {
            PropertyValuesHolder valuesHolder = mValues[0];
            valuesHolder.setIntValues(values);
        }
        // New property/values/target should cause re-initialization prior to starting
        mInitialized = false;
    }
    //设置属性值
    public void setValues(PropertyValuesHolder... values) {
        int numValues = values.length;
        mValues = values;
        mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
        for (int i = 0; i < numValues; ++i) {
            PropertyValuesHolder valuesHolder = values[i];
            mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
        }
        // New property/values/target should cause re-initialization prior to starting
        mInitialized = false;
    }    

ofInt()方法返回一个ValueAnimator对象,同时将values按照自己的方式存储起来,

2.3.1.2 设置动画执行时间
valueAnimator.setDuration(duration);
    @Override
    public ValueAnimator setDuration(long duration) {
        if (duration < 0) {
            throw new IllegalArgumentException("Animators cannot have negative duration: " +
                    duration);
        }
        mDuration = duration;
        return this;
    }

这个方法没有做什么操作,只是将动画的执行时间存储了起来

2.3.1.3 设置动画插值器
    valueAnimator.setInterpolator(new AccelerateInterpolator());
    private TimeInterpolator mInterpolator = sDefaultInterpolator;

    private static final TimeInterpolator sDefaultInterpolator =
            new AccelerateDecelerateInterpolator();

    @Override
    public void setInterpolator(TimeInterpolator value) {
        if (value != null) {
            mInt
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值