天啦噜!原来Android属性动画也不过如此
前两篇《天啦噜!原来Android补间动画可以这么玩》和《天啦噜!原来Android帧动画这么简单》重点讲述了Android开发过程中补间动画和帧动画知识点,本篇文章我们重点总结一下属性动画的使用和原理。
Android动画系列:
什么是属性动画
在一段时间内通过修改对象的属性而形成的动画叫属性动画(Property Animation),Google官方在Android 3.0添加Property Animation
。属性动画的主要是修改对象的属性,如 View 的背景颜色、透明值、位置等。
属性动画和补间动画的区别
有同学可能会问不是已经有补间动画吗,为什么要引入属性动画?换句话说,Property Animation 到底能干哪些 Tween Animation 不能干的活呢?
Tween Animation 存在的问题:
- Tween Animation 只能作用于 View,不能作用于普通 Object 的属性。
- Tween Animation 只能改变 View 的一部分属性。Tween Animation 只支持修改 View 的这几个方面:Alpha、Scale、Translate、Rotate 和这些的组合,一旦想要改变的 View 的属性不在这个范围内,Tween Animation 就无能为力了,如 View 的 BackgroundColor。
- Tween Animation 只能改变 View 的“表面”位置,不能改变 View 的实际位置。
属性动画相关类
属性动画涉及的类主要有:
- Animator,所有 Animator 的父类,主要用于定义通用的接口。
- AnimatorSet,主要用于组合多个属性动画。
- ValueAnimator,属性动画的一种,主要用于根据起始值和终止值产生动画,只负责产生在起始值和终止值之间的值,
不负责更新界面,需要用户自己实现更新界面的逻辑。 - ObjectAnimator,属性动画的一种,主要用于根据起始值和终止值产生动画,并将动画产生的值设置在目标对象上。
- 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实现动画分为以下几个步骤:
- 调用ValueAnimation类中的ofInt(int…values)、ofFloat(String propertyName,float…values)等静态方法实例化ValueAnimator对象;
- 调用addUpdateListener(AnimatorUpdateListener mListener)方法为ValueAnimator对象设置属性变化的监听器,并在AnimatorUpdateListener 中的实现方法为目标对象的属性设置计算好的属性值。
- 创建自定义的插值器(Interpolator),调用setInterpolator(TimeInterpolator value)为ValueAniamtor设置自定义的Interpolator;(可选,不设置默认为缺省值)
- 创建自定义的估值器(TypeEvaluator),调用setEvaluator(TypeEvaluator value)为ValueAnimator设置自定义的TypeEvaluator;(可选,不设置默认为缺省值)
- 设置动画的持续时间、是否重复及重复次数等属性;
- 为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"]
/>
示例:
//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实现动画的几个步骤,如下:
- 通过调用ofFloat()、ofInt()等方法创建ObjectAnimator对象,并设置目标对象、需要改变的目标属性名、初始值和结束值;
- 设置动画的持续时间、是否重复及重复次数等属性;
- 启动动画。
常用的几个属性值解释:
- 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"]
/>
示例:
//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>
示例:
//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();
}
}
上面这个属性动画的 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 的多个属性的时候,一般有三种方法:
- ObjectAnimator + AnimatorSet;
- PropertyValuesHolder + ObjectAnimator;
- 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