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

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

程序员文章站 2022-03-08 15:24:57
前两篇重点讲述了Android开发过程中补间动画和帧动画知识点,本篇文章我们重点总结一下属性动画的使用和原理。什么是属性动画在一段时间内通过修改对象的属性而形成的动画叫属性动画(Property Animation),Google官方在Android 3.0添加Property Animation。属性动画的主要是修改对象的属性,如 View 的背景颜色、透明值、位置等。属性动画和补间动画的区别有同学可能会问不是已经有补间动画吗,为什么要引入属性动画?换句话说,Property Animation...

前两篇《天啦噜!原来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,在动画的每一帧处通知你。这个动画器没有时间,插值或是对象值设定。回调监听器为每一帧动画接受信息,包括总运行时间和从前一帧到现在的运行时间。

继承结构如下:

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

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"]
    />

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

示例

//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"]
    />

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

示例

//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>

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

示例

//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();
    }
}

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

上面这个属性动画的 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

本文地址:https://blog.csdn.net/li0978/article/details/114242929