其中,帧动画因为资源占用大、性能差、内存溢出等不足,已被废弃。

属性动画是Android最常用的动画方案,具有以下优势:

  1. 动画效果包括但不限于平移、旋转、缩放、透明;
  2. 作用对象包括但不限于View;
  3. 同等条件下,属性动画性能&资源占用优于视图动画;
  4. 高可定制化

2. 原理

ValueAnimator类是通过不断控制 值 的变化,再实时赋给对象,从而实现动画效果

ObjectAnimator类直接改变对象的属性值,从而实现动画效果

3. ValueAnimator使用

ValueAnimator有三个动画构造器方法:(int values)、ofFloat(float values)、ofObject(int values)

三个方法的区别可以理解为只是使用的估值器不一样,原理都是值的改变过程。

属性动画建议以Java动态代码的方式获取动画对象,方便灵活设定值;

3.1 ofFloat

使用浮点型估值器FloatEvaluator ,以float类型的数值,将初始值变化到结束值

public static Animator startValueAnim(float... changeArr ,){
//        ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);//从0平滑过渡到1
        ValueAnimator anim = ValueAnimator.ofFloat(changeArr);//传入几个就是连续过渡到每个值,直至最后一个值,如0,1,0.8,1
        anim.setDuration(300);
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {

            }
        });
        return anim;
    }

3.2 ofInt

与ofFloat使用方式一致,ofInt是使用整型估值器IntEvaluator ,以int类型的数值,将初始值变化到结束值

public static Animator startValueAnim(int... changeArr ,){
//        ValueAnimator anim = ValueAnimator.ofFloat(0, 100);//从0平滑过渡到100
        ValueAnimator anim = ValueAnimator.ofInt(changeArr);//传入几个就是连续过渡到每个值,直至最后一个值,如0,100,20,100
        anim.setDuration(300);
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {

            }
        });
        return anim;
    }

3.3 ofObject

ofObject可以说是ValueAnimator的核心成员,用法与上述一致,需自定义进度值变化,即编写实现TypeEvaluator类:

public class FloatOutEvaluator implements TypeEvaluator {
    /**
     *
     * @param fraction 进度值
     * @param startValue 起始值
     * @param endValue 终点值
     * @return
     */
    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        float startFloat = ((Number) startValue).floatValue();
        float cf = fraction;
        if (fraction<0.3f){
            cf = fraction*2;//前30%进度-进度加倍
        }else if (fraction <0.6f){
            cf = 0.6f;//暂停一会
        }
        return startFloat +  cf* (((Number) endValue).floatValue() - startFloat);
    }
}

使用

ValueAnimator animator = ValueAnimator.ofObject(new FloatOutEvaluator() , 0f,1f);
        animator.setDuration(3000);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                //使用变化值,赋值,刷新,产生动画
                view.setAlpha((Float) valueAnimator.getAnimatedValue());
            }
        });

4. ObjectAnimator

ObjectAnimator 是Android开发实际使用最多的动画,可以直接创建的几种动画及组合动画

4.1 Java代码方式

动画:透明渐变两次

ObjectAnimator animator = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f, 1f,0f);
animator.setDuration(3000);
animator.start();

同理有:

  1. 旋转rotation
  2. 平移translationX、translationY
  3. 缩放scaleX、scaleY
  4. 透明变化alpha

4.2 xml方式

属性动画xml资源文件的目录是 res/animator ,与视图动画的资源目录 res/anim不是同一个

举例

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:valueFrom="0"
    android:valueTo="1.0"
    android:valueType="floatType"
    android:propertyName="alpha"/>

加载使用动画

Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_alpha);
animator.setTarget(view);
animator.start();

同理其他动画rotateion、scale、translate…

4.3 ObjectAnimator扩展 & 自定义TypeEvaluator估值器

扩展“color”动画


1.定义一个View,包含setColor()方法

class ColorTextView extends TextView {

    public void setColor(String color){
        setTextColor(Color.parseColor(color));
    }

    public ColorTextView(Context context) {
        super(context);
    }

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

2.布局使用

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <com.cupster.animation.ColorTextView
        android:id="@+id/anim_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="测试颜色动画的文字"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

3.编写估值器ColorEvaluator,控制颜色随着进度值改变而改变的规则

package com.cupster.animation;

import android.animation.TypeEvaluator;

/**
 * 此处代码为郭霖大神编写的自定义Evaluator,用于扩展属性动画:"color",
 * 使用该动画的View需设置对应public void setColor,并在其中调用invalidate()
 */
public class ColorEvaluator implements TypeEvaluator {

    private int mCurrentRed = -1;

    private int mCurrentGreen = -1;

    private int mCurrentBlue = -1;

    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        String startColor = (String) startValue;
        String endColor = (String) endValue;
        int startRed = Integer.parseInt(startColor.substring(1, 3), 16);
        int startGreen = Integer.parseInt(startColor.substring(3, 5), 16);
        int startBlue = Integer.parseInt(startColor.substring(5, 7), 16);
        int endRed = Integer.parseInt(endColor.substring(1, 3), 16);
        int endGreen = Integer.parseInt(endColor.substring(3, 5), 16);
        int endBlue = Integer.parseInt(endColor.substring(5, 7), 16);
        // 初始化颜色的值
        if (mCurrentRed == -1) {
            mCurrentRed = startRed;
        }
        if (mCurrentGreen == -1) {
            mCurrentGreen = startGreen;
        }
        if (mCurrentBlue == -1) {
            mCurrentBlue = startBlue;
        }
        // 计算初始颜色和结束颜色之间的差值
        int redDiff = Math.abs(startRed - endRed);
        int greenDiff = Math.abs(startGreen - endGreen);
        int blueDiff = Math.abs(startBlue - endBlue);
        int colorDiff = redDiff + greenDiff + blueDiff;
        if (mCurrentRed != endRed) {
            mCurrentRed = getCurrentColor(startRed, endRed, colorDiff, 0,
                    fraction);
        } else if (mCurrentGreen != endGreen) {
            mCurrentGreen = getCurrentColor(startGreen, endGreen, colorDiff,
                    redDiff, fraction);
        } else if (mCurrentBlue != endBlue) {
            mCurrentBlue = getCurrentColor(startBlue, endBlue, colorDiff,
                    redDiff + greenDiff, fraction);
        }
        // 将计算出的当前颜色的值组装返回
        String currentColor = "#" + getHexString(mCurrentRed)
                + getHexString(mCurrentGreen) + getHexString(mCurrentBlue);
        return currentColor;
    }

    /**
     * 根据fraction值来计算当前的颜色。
     */
    private int getCurrentColor(int startColor, int endColor, int colorDiff,
                                int offset, float fraction) {
        int currentColor;
        if (startColor > endColor) {
            currentColor = (int) (startColor - (fraction * colorDiff - offset));
            if (currentColor < endColor) {
                currentColor = endColor;
            }
        } else {
            currentColor = (int) (startColor + (fraction * colorDiff - offset));
            if (currentColor > endColor) {
                currentColor = endColor;
            }
        }
        return currentColor;
    }

    /**
     * 将10进制颜色值转换成16进制。
     */
    private String getHexString(int value) {
        String hexString = Integer.toHexString(value);
        if (hexString.length() == 1) {
            hexString = "0" + hexString;
        }
        return hexString;
    }

}

4.封装通用方法

public static void propColorAnimator(ColorTextView target , String startColor , String endColor){
        ObjectAnimator animator = ObjectAnimator.ofObject(target ,"color",new ColorEvaluator() ,startColor,endColor);
        animator.setDuration(3000);
        animator.start();
    }

5.使用

tv = findViewById(R.id.anim_view);
        tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                PropAnimatorHelper.propColorAnimator((ColorTextView) view,"#a7dbf7" ,"#ff8696");
            }
        });