天啦噜!原来Android属性动画也不过如此

前两篇《天啦噜!原来Android补间动画可以这么玩》《天啦噜!原来Android帧动画这么简单》重点讲述了Android开发过程中补间动画和帧动画知识点,本篇文章我们重点总结一下属性动画的使用和原理。

Android动画系列:

什么是属性动画

在一段时间内通过修改对象的属性而形成的动画叫属性动画(Property Animation),Google官方在Android 3.0添加Property Animation。属性动画的主要是修改对象的属性,如 View 的背景颜色、透明值、位置等。

属性动画和补间动画的区别

有同学可能会问不是已经有补间动画吗,为什么要引入属性动画?换句话说,Property Animation 到底能干哪些 Tween Animation 不能干的活呢?

Tween Animation 存在的问题:

  1. Tween Animation 只能作用于 View,不能作用于普通 Object 的属性。
  2. Tween Animation 只能改变 View 的一部分属性。Tween Animation 只支持修改 View 的这几个方面:Alpha、Scale、Translate、Rotate 和这些的组合,一旦想要改变的 View 的属性不在这个范围内,Tween Animation 就无能为力了,如 View 的 BackgroundColor。
  3. Tween Animation 只能改变 View 的“表面”位置,不能改变 View 的实际位置。

属性动画相关类

属性动画涉及的类主要有:

  1. Animator,所有 Animator 的父类,主要用于定义通用的接口。
  2. AnimatorSet,主要用于组合多个属性动画。
  3. ValueAnimator,属性动画的一种,主要用于根据起始值和终止值产生动画,只负责产生在起始值和终止值之间的值,
    不负责更新界面,需要用户自己实现更新界面的逻辑。
  4. ObjectAnimator,属性动画的一种,主要用于根据起始值和终止值产生动画,并将动画产生的值设置在目标对象上。
  5. TimeAnimator,提供了一个简单的回调机制,通过 TimeAnimator.TimeListener,在动画的每一帧处通知你。这个动画器没有时间,插值或是对象值设定。回调监听器为每一帧动画接受信息,包括总运行时间和从前一帧到现在的运行时间。

继承结构如下:

属性动画继承结构图

ValueAnimator和ObjectAnimator主要区别

该类作为ValueAnimator的子类不仅继承了ValueAnimator的所有方法和特性,并且还封装很多实用的方法,方便开发人员快速实现动画。同时,由于属性值会自动更新,使用ObjectAnimator实现动画不需要像ValueAnimator那样必须实现 ValueAnimator.AnimatorUpdateListener ,因此实现任意对象的动画显示就更加容易了。我们在大部分的开发工作中,都会使用ObjectAnimator而非ValueAnimator实现我们所需的动画效果。

属性动画实现形式

属性动画的实现形式有两种:xml创建和code实现。其中xml创建的xml动画文件要放在res/animator目录下,注意此处和补间动画(Tween Animation)存放位置不同。

通常情况下属性动画一般建议通过代码进行实现,因为他更灵活,尤其是在自定义View中常常有属性动画的身影。当然也需要根据实际场景自行选择,下边就通过这两种形式来总结一下属性动画几个类的使用。

ValueAnimator

ValueAnimator是Property Animation系统的核心类,它包含了配置Property Animation属性的大部分方法,那要实现一个Property Animation,都需要直接或间接使用ValueAnimator类。

一般使用ValueAnimator实现动画分为以下几个步骤:

  1. 调用ValueAnimation类中的ofInt(int…values)、ofFloat(String propertyName,float…values)等静态方法实例化ValueAnimator对象;
  2. 调用addUpdateListener(AnimatorUpdateListener mListener)方法为ValueAnimator对象设置属性变化的监听器,并在AnimatorUpdateListener 中的实现方法为目标对象的属性设置计算好的属性值。
  3. 创建自定义的插值器(Interpolator),调用setInterpolator(TimeInterpolator value)为ValueAniamtor设置自定义的Interpolator;(可选,不设置默认为缺省值)
  4. 创建自定义的估值器(TypeEvaluator),调用setEvaluator(TypeEvaluator value)为ValueAnimator设置自定义的TypeEvaluator;(可选,不设置默认为缺省值)
  5. 设置动画的持续时间、是否重复及重复次数等属性;
  6. 为ValueAnimator设置目标对象并开始执行动画。

需要注意目标对象的需要被设置的属性必须拥有get\set方法,格式类似 set()。

通过XML创建

语法

<animator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="int"
    android:interpolator="@[package:]anim/interpolator_resource"
    android:valueType=["intType" | "floatType"]
    android:valueFrom="float | int | color"
    android:valueTo="float | int | color"
    android:startOffset="int"
    android:repeatCount="int"
    android:repeatMode=["repeat" | "reverse"]
    />

ValueAnimator属性详解

示例

//1. 创建 value_animator.xml
<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1800"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:valueType="floatType"
    android:valueFrom="-100"
    android:valueTo="800"
    android:startOffset="0"
    android:repeatCount="infinite"
    android:repeatMode="reverse"
    />
    
//2. 在代码中使用 value_animator
ValueAnimator mValueAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this, R.animator.value_animator);
mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        mTarget.setY((Float) animation.getAnimatedValue());
    }
});
mValueAnimator.start();

通过代码实现

语法

ValueAnimator valueAnimator = ValueAnimator.ofFloat(float... values);
valueAnimator.setDuration(long duration);
valueAnimator.setInterpolator(TimeInterpolator value);
valueAnimator.addUpdateListener(AnimatorUpdateListener listener);
…
valueAnimator.start();

示例

ValueAnimator mValueAnimator = ValueAnimator.ofFloat(0, 800);
mValueAnimator.setDuration(1800);
mValueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
mValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
mValueAnimator.setRepeatMode(ValueAnimator.REVERSE);
mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    
    
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
    
    
        mTarget.setY((Float) animation.getAnimatedValue());
    }
});
mValueAnimator.start();

ObjectAnimator

要动画显示 View 对象的某个属性,比如颜色或旋转值,我们所有要做的事情就是创建一个 Property animation,并设定对应的 View 属性。那接下来我们就用ObjectAnimator类来分别实现View的透明度渐变、收缩、移动和旋转等动画效果,那在此之前我们也来总结下使用ObjectAnimator实现动画的几个步骤,如下:

  1. 通过调用ofFloat()、ofInt()等方法创建ObjectAnimator对象,并设置目标对象、需要改变的目标属性名、初始值和结束值;
  2. 设置动画的持续时间、是否重复及重复次数等属性;
  3. 启动动画。

常用的几个属性值解释:

  • translationX 和 translationY:这两个属性控制着 View 的屏幕位置坐标变化量,以 layout 容器的左上角为坐标原点;
  • rotation、rotationX 和 rotationY:这三个属性控制着 2D 旋转角度(rotation属性)和围绕某枢轴点的 3D 旋转角度;
  • scaleX、scaleY:这两个属性控制着 View 围绕某枢轴点的 2D 缩放比例;
  • pivotX 和 pivotY: 这两个属性控制着枢轴点的位置,前述的旋转和缩放都是以此点为中心展开的,缺省的枢轴点是 View 对象的中心点;
  • x 和 y:这是指 View 在容器内的最终位置,等于 View 左上角相对于容器的坐标加上 translationX 和 translationY 后的值;
  • alpha:表示 View 的 alpha 透明度。缺省值为 1 (不透明),为 0 则表示完全透明(看不见);

通过XML创建

语法

<objectAnimator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="int"
    android:interpolator="@[package:]anim/interpolator_resource"
    android:propertyName="string"
    android:valueType=["intType" | "floatType"]
    android:valueFrom="float | int | color"
    android:valueTo="float | int | color"
    android:startOffset="int"
    android:repeatCount="int"
    android:repeatMode=["repeat" | "reverse"]
    />

ObjectAnimator属性详解

示例

//1. 创建 object_animator.xml
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1800"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:propertyName="Y"
    android:valueType="floatType"
    android:valueFrom="0"
    android:valueTo="800"
    android:startOffset="0"
    android:repeatCount="infinite"
    android:repeatMode="reverse"
    />
    
//2. 在代码中使用 object_animator
ObjectAnimator mObjectAnimator = (ObjectAnimator) AnimatorInflater.loadAnimator(this, R.animator.object_animator);
mObjectAnimator.setTarget(mTarget);
mObjectAnimator.start();

通过代码实现

语法

ObjectAnimator objectAnimator = ObjectAnimator.ofObject(Object target, String propertyName, TypeEvaluator evaluator, Object... values);
objectAnimator.setDuration(long duration);
objectAnimator.setInterpolator(TimeInterpolator value);
…
objectAnimator.start();

示例

ObjectAnimator mObjectAnimator = ObjectAnimator.ofFloat(mTarget, "y", 0, 800);
mObjectAnimator.setDuration(1800);
mObjectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
mObjectAnimator.setRepeatCount(ValueAnimator.INFINITE);
mObjectAnimator.setRepeatMode(ValueAnimator.REVERSE);
mObjectAnimator.start();

AnimatorSet

通过XML创建

语法

<set
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering=["together" | "sequentially"]>

    <objectAnimator
        android:propertyName="string"
        android:duration="int"
        android:valueFrom="float | int | color"
        android:valueTo="float | int | color"
        android:startOffset="int"
        android:repeatCount="int"
        android:repeatMode=["repeat" | "reverse"]
        android:valueType=["intType" | "floatType"]/>

    <animator
        android:duration="int"
        android:valueFrom="float | int | color"
        android:valueTo="float | int | color"
        android:startOffset="int"
        android:repeatCount="int"
        android:repeatMode=["repeat" | "reverse"]
        android:valueType=["intType" | "floatType"]/>

    <set>
        ...
    </set>
</set>

AnimatorSet属性详解

示例

//1. 创建 animator_set.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="together"
    >
    <objectAnimator
        android:duration="@integer/integer_one_thousand_and_eight_hundred"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:propertyName="Y"
        android:valueType="floatType"
        android:valueFrom="0"
        android:valueTo="800"
        android:startOffset="0"
        android:repeatCount="infinite"
        android:repeatMode="reverse"
        />
    <objectAnimator
        android:duration="@integer/integer_one_thousand_and_eight_hundred"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:propertyName="ScaleX"
        android:valueType="floatType"
        android:valueFrom="1"
        android:valueTo="2"
        android:startOffset="0"
        android:repeatCount="infinite"
        android:repeatMode="reverse"
        />
    <objectAnimator
        android:duration="@integer/integer_one_thousand_and_eight_hundred"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:propertyName="ScaleY"
        android:valueType="floatType"
        android:valueFrom="1"
        android:valueTo="2"
        android:startOffset="0"
        android:repeatCount="infinite"
        android:repeatMode="reverse"
        />
</set>

//2. 在代码中使用 animator_set
AnimatorSet mAnimatorSet = (AnimatorSet)AnimatorInflater.loadAnimator(this, R.animator.animator_set);
mAnimatorSet.setTarget(mTarget);
mAnimatorSet.start();

通过代码实现

语法

AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(Animator... items);
animatorSet.playSequentially(Animator... items);
//非必须
animatorSet.setTarget(mTarget);
…
animatorSet.start();

示例

ObjectAnimator translateYObjectAnimator = ObjectAnimator.ofFloat(mTarget, "y", 0, 800);
translateYObjectAnimator.setDuration(1800);
translateYObjectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
translateYObjectAnimator.setRepeatCount(ValueAnimator.INFINITE);
translateYObjectAnimator.setRepeatMode(ValueAnimator.REVERSE);
ObjectAnimator scaleXObjectAnimator = ObjectAnimator.ofFloat(mTarget, "scaleX", 1, 2);
scaleXObjectAnimator.setDuration(1800);
scaleXObjectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
scaleXObjectAnimator.setRepeatCount(ValueAnimator.INFINITE);
scaleXObjectAnimator.setRepeatMode(ValueAnimator.REVERSE);
ObjectAnimator scaleYObjectAnimator = ObjectAnimator.ofFloat(mTarget, "scaleY", 1, 2);
scaleYObjectAnimator.setDuration(1800);
scaleYObjectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
scaleYObjectAnimator.setRepeatCount(ValueAnimator.INFINITE);
scaleYObjectAnimator.setRepeatMode(ValueAnimator.REVERSE);
mAnimatorSet = new AnimatorSet();
mAnimatorSet.playTogether(translateYObjectAnimator, scaleXObjectAnimator, scaleYObjectAnimator);
mAnimatorSet.playSequentially();
//非必须
//        mAnimatorSet.setTarget(mTarget);
mAnimatorSet.start();

监听属性动画

Property Animation 中一共有三种监听事件:

  • AnimatorListener;
  • AnimatorPauseListener;
  • AnimatorUpdateListener;

AnimatorListener

AnimatorListener 接口主要用于监听 Property Animation 的开始、结束、取消、重复状态,需要实现的方法分别是:

@Override
public void onAnimationStart(Animator animation) {
    
    }

@Override
public void onAnimationEnd(Animator animation) {
    
    }

@Override
public void onAnimationCancel(Animator animation) {
    
    }

@Override
public void onAnimationRepeat(Animator animation) {
    
    }

AnimatorPauseListener

AnimatorPauseListener 主要用于监听 Property Animation 的暂停、恢复状态,需要实现的方法分别是:

@Override
public void onAnimationPause(Animator animation) {
    
    }

@Override
public void onAnimationResume(Animator animation) {
    
    }

AnimatorUpdateListener

AnimatorUpdateListener 是 ValueAnimator 及其子类特有的接口,主要用于监听动画中值的变化,用于手动更新界面,需要实现的方法是:

@Override
public void onAnimationUpdate(ValueAnimator animation) {
    
    }

属性动画工作原理

当 ValueAnimator 调用 start 方法之后,ValueAnimator 会根据 Property Animation 当前运行时间与总的动画持续时间计算出一个时间消耗百分数(The elapsed fraction)。紧接着,ValueAnimator 将这个时间消耗百分数交给当前 ValueAnimator 的插值器(Interpolator),不同的 Interpolator 会根据不同的算法将这个时间消耗百分数转换成插值百分数(The interpolated fraction)。紧接着,ValueAnimator 会将这个插值百分数交给当前 ValueAnimator 的估值器(TypeEvaluator),不同的 TypeEvaluator 会根据不同的算法将这个插值百分数转换最终的动画值(The final value)。

拿AccelerateDecelerateInterpolator插值器举例:

public class AccelerateDecelerateInterpolator extends BaseInterpolator
        implements NativeInterpolatorFactory {
    
    
    public AccelerateDecelerateInterpolator() {
    
    
    }

    @SuppressWarnings({
    
    "UnusedDeclaration"})
    public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {
    
    
    }

    /**
     * param input (The elapsed fraction)
     * return (The interpolated fraction)
     */
    public float getInterpolation(float input) {
    
    
        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
    }

    /** @hide */
    @Override
    public long createNativeInterpolator() {
    
    
        return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();
    }
}

AccelerateDecelerateInterpolator属性动画工作原理

上面这个属性动画的 Duration 为 40ms,Intepolator 为 AccelerateDecelerateInterpolator,Distance 为 40。

在 t = 10ms 时,The elapsed fraction 为 0.25 = 10/40,The interpolated fraction = (float)(Math.cos((0.25 + 1) * Math.PI) / 2.0f) + 0.5f = 0.14644662,The final value 为 5.8578648 = (40 - 0) * 0.14644662。

自定义插值器

自定义插值器要实现 Interpolator 接口,上篇文章已经有所说明,不做过多阐述。

public class DecelerateAccelerateInterpolator implements Interpolator {
    
    

    @Override
    public float getInterpolation(float input) {
    
    
        return (float) ((Math.tan(Math.PI/2 * input - Math.PI/4) + 1)/2);
    }
}

自定义估值器

自定义估值器,只要实现 TypeEvaluator 接口,并实现其中定义的 evaluate 方法即可:

public class CustomTypeEvaluator implements TypeEvaluator {
    
    

    /**
     * param fraction 插值器最终值
     * param startValue 属性开始值
     * param endValue 属性结束值
     */
    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
    
    
        float startFloat = ((Number) startValue).floatValue();
        return 200 + fraction * (((Number) endValue).floatValue() - startFloat);
    }
}

ViewPropertyAnimator 使用简介

ViewPropertyAnimator、ObjectAnimator、ValueAnimator 这三种 Animator,它们其实是一种递进的关系:从左到右依次变得更加难用,也更加灵活。

它们的性能是一样的,因为 ViewPropertyAnimator 和 ObjectAnimator 的内部实现其实都是 ValueAnimator,ObjectAnimator 更是本来就是 ValueAnimator 的子类,它们三个的性能并没有差别。

它们的差别只是使用的便捷性以及功能的灵活性。所以在实际使用时候的选择,只要遵循一个原则就行:尽量用简单的。能用 View.animate() 实现就不用 ObjectAnimator,能用 ObjectAnimator 就不用 ValueAnimator。

当需要同时更改 View 的多个属性的时候,一般有三种方法:

  1. ObjectAnimator + AnimatorSet;
  2. PropertyValuesHolder + ObjectAnimator;
  3. ViewPropertyAnimator;

接下来,分别用三种方法分别实现同一种效果:View 的 Y 值从当前位置增到 400,Alpha 值 从 1.0f 变成 0.1f。

ObjectAnimator + AnimatorSet

ObjectAnimator alphaObjectAnimator = ObjectAnimator.ofFloat(mTarget, "alpha", 1.0f, 0.1f);
ObjectAnimator yObjectAnimator = ObjectAnimator.ofFloat(mTarget, "y", 400f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(alphaObjectAnimator, yObjectAnimator);
animatorSet.start();

PropertyValuesHolder + ObjectAnimator

PropertyValuesHolder alphaPropertyValuesHolder = PropertyValuesHolder.ofFloat("alpha", 1.0f, 0.1f);
PropertyValuesHolder yPropertyValuesHolder = PropertyValuesHolder.ofFloat("y", 400f);
ObjectAnimator.ofPropertyValuesHolder(mTarget, alphaPropertyValuesHolder, yPropertyValuesHolder).start();

ViewPropertyAnimator

ViewPropertyAnimator viewPropertyAnimator = mTarget.animate();
viewPropertyAnimator.alpha(0.1f);
viewPropertyAnimator.y(400f);

//也可以写成一句:
mTarget.animate().alpha(0.1f).y(400f);

PropertyValuesHolder

细心的同学可能会注意到,ValueAnimator、ObjectAnimator除了这些创建Animator实例的方法以外,都还有一个方法:

/**
 * valueAnimator的
 */
public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values) 
/**
 * ObjectAnimator的
 */
public static ObjectAnimator ofPropertyValuesHolder(Object target,PropertyValuesHolder... values)

PropertyValuesHolder这个类的意义就是,它其中保存了动画过程中所需要操作的属性和对应的值。我们通过ofFloat(Object target, String propertyName, float… values)构造的动画,ofFloat()的内部实现其实就是将传进来的参数封装成PropertyValuesHolder实例来保存动画状态。在封装成PropertyValuesHolder实例以后,后期的各种操作也是以PropertyValuesHolder为主的。

使用举例:

PropertyValuesHolder rotationHolder = PropertyValuesHolder.ofFloat("Rotation", 60f, -60f, 40f, -40f, -20f, 20f, 10f, -10f, 0f);
PropertyValuesHolder colorHolder = PropertyValuesHolder.ofInt("BackgroundColor", 0xffffffff, 0xffff00ff, 0xffffff00, 0xffffffff);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(mTextView, rotationHolder, colorHolder);
animator.setDuration(3000);
animator.setInterpolator(new AccelerateInterpolator());
animator.start();

参考

  • https://developer.android.com/reference/android/animation/Animator
  • https://carsonho.blog.csdn.net/article/details/72909894
  • https://blog.csdn.net/harvic880925/article/details/50752838
  • https://juejin.cn/post/6844903798687678478#heading-34

猜你喜欢

转载自blog.csdn.net/li0978/article/details/114242929