Android系统编程入门系列之界面Activity响应多元的属性动画

响应丝滑动画一篇文章中,分别介绍了作用于普通视图、绘制视图的绘制对象、和界面这三种对象的动画效果,但是都有一些使用的局限性。比如这些动画都只是以屏幕上绘制更新的方式绘制动画,并没有真实改变作用对象的实际位置或属性,这种问题在视图动画中尤为明显,在没有特别设置时,动画结束后的视图状态会还原到动画前,也就是说动画中及动画后的视图对象是没有保存动画中用到的一些属性的。这种情况就需要使用本文所介绍的属性动画了。

属性动画本质是对某个对象的属性提供一组变化更新的属性值,他的作用对象不仅可以是视图和界面,也可以是任何具有上述需求的对象类。在Android3.0 API 11开始的AndroidSDK版本中,定义了android.animation.Animator类作为抽象父类来描述属性动画的结构,并提供了android.animation.ValueAnimator作为只变化属性值的属性动画类、android.animation.ObjectAnimator可以变化属性值并更新到对象属性的属性动画类、和android.animation.AnimatorSet包含多个属性动画集合的属性动画类。

与普通动画类似,属性动画同样可以在资源文件中静态声明,也可以在代码中动态声明,但是由于其作用对象可以是任何对象,而且属性动画的优势在于为作用对象的属性提供一组变化的属性值,所以属性动画只能在需要绑定作用对象的代码位置动态使用。由于静态声明的属性动画可以针对多个对象使用,在开发过程中推荐使用静态声明+动态使用的方式。

只变化属性值的属性动画类

ValueAnimator作为最核心的属性动画类,可以调用ValueAnimator.ofArgb(int... values)ValueAnimator.ofFloat(float... values)ValueAnimator.ofInt(int... values)ValueAnimator.ofObject(TypeEvaluator evaluator, Object... values)系列静态方法,获取初始化后的实例化对象。其中的参数 values 是可变长参数,作为可变化属性值的范围区间。而参数 evaluatorandroid.animation.TypeEvaluator估值器接口的实现类,用以计算估计属性值的具体变化数值的类。而AndroidSDK中已经定义了上述系列静态方法前三种类型对应的估值器子类,分别是android.animation.ArgbEvaluatorandroid.animation.FloatEvaluatorandroid.animation.IntEvaluator,除此之外还有计算二维坐标系的估值器android.animation.PointFEvaluator和计算矩形坐标的估值器android.animation.RectEvaluator。另外,在实际开发中针对作用对象要变化的属性值类型不同,也可以自定义实现TypeEvaluator接口的估值器。

这里提到的ArgbEvaluator是用以计算颜色的 ARGB 色值估值器,与Android系统定义的屏幕坐标类似,这里的 ARGB 色值也是一套系统颜色规范。在Java中int类型是用4个字节存储的,这4个字节按顺序从大端到小端,刚好分别表示Alpha(颜色值中的透明度),Red(颜色值中的红色值),Green(颜色值中的绿色值),Blue(颜色值中的蓝色值),每个字节的数值可以表示范围是[ 0x0, 0xFF ],所以用十六进制表示颜色值中的纯红色为 0xFFFF0000 ,纯黑色为 0xFF000000 ,纯白色为 0xFFFFFFFF ,而完全透明色为 0x000000000x00FFFFFF 之间的任一值。也可以借助android.graphics.Color类使用已经定义的颜色值和系统颜色规范的相关方法。

在创建ValueAnimator实例化对象后,
可以调用setDuration(long duration)设置完成一次动画效果的持续时间,单位为 ms 毫秒。
调用setRepeatCount(int value)设置完成一次动画效果后的重复次数,默认 value = ValueAnimator.INFINITE 为无限循环。
调用setRepeatMode(int value)设置在完成一次动画效果后重复时的动画效果,其 value 只能为
ValueAnimator.RESTART 表示从头开始重新完成一次一模一样的动画效果,
ValueAnimator.REVERSE 表示从上次动画结尾开始完成一次倒放的动画效果。
调用 setInterpolator(TimeInterpolator value)设置动画效果的时间插值器。与上面的估值器定义结构类似,参数 valueandroid.animation.TimeInterpolator插值器接口的实现类,用以确定每次更新动画效果的时间。同样的,AndroidSDK已经定义了一系列插值器,包括但不限于持续加速插值器android.view.animation.AccelerateInterpolator、持续减速插值器android.view.animation.DecelerateInterpolator、线性匀速插值器android.view.animation.LinearInterpolator等,同样在实际开发中针对动画效果展示时间进度,也可以自定义实现TimeInterpolator接口的插值器。

最终在需要启动当前属性动画的位置调用start()即开始播放当前动画效果,如果不想在调用属性动画的启动方法后立即执行相关动画效果,可以在启动方法之前调用setStartDelay(long startDelay)设置延时动画的延长时间,单位是 ms 毫秒。

ValueAnimator是根据TypeEvaluator估值器来更新属性值的,而更新的时间则是由TimeInterpolator插值器决定的,那么怎么才能拿到每次更新后的属性值呢?这里就可以用调用ValueAnimator对象的addUpdateListener(ValueAnimator.AnimatorUpdateListener listener)方法添加属性动画更新监听,在AnimatorUpdateListener类中重写onAnimationUpdate(ValueAnimator animation)方法,该方法中的参数animation即更新后的属性动画对象,通过调用animation.getAnimatedValue()来接收每次更新后的属性值,并可以将更新后的属性值赋值给需要属性动画的对象中。

也可以在资源文件中静态定义属性动画,在 res/animator 目录下,定义xml格式的属性动画资源文件,该资源文件内以<animator />为根标签,其中可以设置android:duration作为完成一次动画效果的持续时间等一系列属性,与代码中动态设置的相关方法类似。之后在代码使用位置加载该资源文件,通过调用AnimatorInflater.loadAnimator(Context context, int id)的静态方法,参数context作为调用位置所在的上下文环境,参数id则是要加载的属性动画类型的资源文件名。将得到Animator抽象类的返回结果,根据所加载资源文件中的根标签名判断,如果与<animator />一致,则可将返回的Animator抽象类直接转换为ValueAnimator类型的对象。

可变化属性值并更新到对象属性的属性动画类

ObjectAnimator作为ValueAnimator的子类,定义和使用方式都有类似的地方,只是ObjectAnimator类中封装了属性动画的更新监听方法,因此只要绑定对象及其要变化的属性,在属性动画每次变化属性值时,都会主动将属性值更新到绑定对象的相关属性上。

因此在初始化实例对象时需要调用ofArgb(Object target, String propertyName, int... values)ofFloat(Object target, String xPropertyName, String yPropertyName, Path path)ofInt(T target, Property<T, Integer> property, int... values)ofObject(T target, Property<T, V> property, TypeConverter<PointF, V> converter, Path path)等系列静态方法。
其中参数 target 即为要绑定的动画效果作用对象;
参数 propertyNametarget 对象中的某个属性,而且该属性必须要有符合驼峰命名规则的 getset 方法;
参数 values 仍然作为可变化属性值的范围区间;
参数 path 作为android.graphics.Path类,则表示一段动画效果的执行路径,以此替换参数 values 表示的单一变化区间;
参数 property 作为android.util.Property抽象类,同样描述某种属性类型,以此替换String类型的参数 propertyName
参数 converter 作为android.animation.TypeConverter抽象类,与上文的参数 property 同时使用,当 property 中声明的属性类型与实际变化更新的属性值不一致时,使用参数 converter 所表示的强制类型转换方式。

在创建实例化对象后,ObjectAnimator有与ValueAnimator一致的setXXX()getXXX()系列方法,另外还有setTarget(Object target)方法可以直接绑定要变化属性值的目标对象,同时有setPropertyName(String propertyName)setProperty(Property property)两种调用方法,都能实现对目标对象中的目标属性的绑定。

最后仍然是在需要启动当前属性动画的位置调用start()开始播放当前动画效果。

在资源文件中静态定义时,同样在 res/animator 目录下,定义xml格式的属性动画资源文件,不过该资源文件内根标签为<objectAnimator />以标记关联对象的属性动画,其中的属性设置不仅与只变化属性值的ValueAnimator对应的<animator />标签中的属性一致,还可单独设置android:propertyName绑定属性名称。之后同样可以在代码中调用AnimatorInflater.loadAnimator(Context context, int id)静态方法加载当前属性动画资源文件,同样地,针对得到的Animator抽象类的返回结果,根据所加载资源文件中的根标签名判断,如果与<objectAnimator />一致,则可将返回的Animator抽象类直接转换为ObjectAnimator类型的对象。

包含多个属性动画集合的属性动画类

SetAnimator是将一系列上述单独的属性动画组合起来的属性动画合集,其使用目的主要是为了讲不同的属性动画按照同一条时间线整理播放,因此其相关方法主要与其中子动画的播放顺序相关。

通过直接创建SetAnimator()构造方法可以获得SetAnimator实例化对象。
得到的对象调用playTogether(Animator... items)方法可以在同一段时间内播放添加的子动画,
调用playSequentially(Animator... items)方法可以将其中的子动画按照添加顺序播放,
而这两个系列方法中的可变长参数 items 即是要添加的系列子动画对象。

也可以将得到的对象调用play(Animator anim)方法,参数 anim 为添加的基本动画,返回android.animation.AnimatorSet.Builder类型,可以调用该类的before(Animator anim)方法设置针对基本动画之前播放的动画 anim 、调用该类的with(Animator anim)方法设置与基本动画同时播放的动画 anim 、调用该类的after(Animator anim)方法设置针对基本动画之后播放的动画 anim 、以及该类的after(long delay)方法设置在基本动画之后延时 delay 毫秒之后继续播放之后动画。

最后仍然是在需要启动当前属性动画的位置调用start()开始播放当前动画效果。

在资源文件中静态定义时,同样在 res/animator 目录下,定义xml格式的属性动画资源文件,在资源文件内根标签为<set></set>以标记为集合属性动画,该标签内可以设置属性android:ordering,其值只能为默认的together表示子动画同时执行,或者为sequentially表示子动画按顺序执行。在<set></set>标签内部,可以嵌入上述<animator /><objectAnimator />两种属性动画标签。同样可以在代码中调用AnimatorInflater.loadAnimator(Context context, int id)静态方法加载当前属性动画资源文件,同样地,针对得到的Animator抽象类的返回结果,根据所加载资源文件中的根标签名判断,如果与<set></set>一致,则可将返回的Animator抽象类直接转换为AnimatorSet类型的对象。

点赞