4、属性动画(Property Animation)入门:ValueAnimator核心机制、ObjectAnimator使用、ofInt/ofFloat/ofObject

说实话,刚接触Android动画那会儿,我总觉得View动画已经够用了。不就是平移、缩放、旋转嘛,一个AnimationSet全搞定。直到有一次,我需要让一个按钮从红色渐变到蓝色,同时它的点击区域也要跟着移动——View动画只能改变绘制位置,不能改变真实坐标。嗯,这就是我转向属性动画的契机。

属性动画,说白了就是直接修改对象的属性值。你给它一个起始值、一个结束值,再告诉它跑多久,它就在这段时间里不断计算中间值,然后设置到目标对象上。核心就是三个字:值变化

ValueAnimator:一切动画的发动机

我个人习惯把ValueAnimator叫做「数值发动机」。它不直接操作任何UI控件,只负责一件事:在指定时间内,从一个数值变化到另一个数值,然后通过监听器告诉你「当前值是多少」。

来看个最简单的例子:

val animator = ValueAnimator.ofFloat(0f, 100f)
animator.duration = 1000
animator.addUpdateListener { animation ->
    val value = animation.animatedValue as Float
    // 拿到当前值,你想怎么用就怎么用
    textView.translationX = value
}
animator.start()

这里ofFloat创建了一个从0到100的浮点数动画。动画跑起来后,每一帧都会回调onAnimationUpdate,我拿到当前值,手动设置给textViewtranslationX。你看,ValueAnimator不关心你拿这个值去干嘛,它只管生产数值。

核心机制拆解:

  • 时间插值器(TimeInterpolator):决定动画的节奏,比如匀速、加速、弹跳。默认是加速减速插值器。
  • 估值器(TypeEvaluator):根据进度计算当前值。比如FloatEvaluator做线性插值,ArgbEvaluator做颜色插值。
  • 帧刷新机制:依赖Choreographer,每16ms(60fps)触发一次计算。

我曾经踩过一个坑:在onAnimationUpdate里做耗时操作,结果动画卡成PPT。记住,这个回调是在UI线程执行的,别在里面做数据库查询或者网络请求。

ObjectAnimator:更省事的写法

如果你觉得每次都要写addUpdateListener太麻烦,那ObjectAnimator就是为你准备的。它继承自ValueAnimator,但多了一个能力:直接操作对象的属性。

val animator = ObjectAnimator.ofFloat(
    textView,           // 目标对象
    "translationX",     // 属性名
    0f,                 // 起始值
    200f                // 结束值
)
animator.duration = 1000
animator.start()

你看,代码少了一半。你只需要告诉它「操作谁、改哪个属性、从哪到哪」,剩下的它自己搞定。原理其实很简单:通过反射调用属性的set方法。所以目标对象必须有对应的setTranslationX(float)方法。

注意:如果属性没有set方法,ObjectAnimator会直接崩溃。比如你想动画化TextViewtext属性,但TextView没有setText(float)方法——嗯,我当年就因为这个查了半天bug。

ofInt、ofFloat、ofObject:三种估值方式

这三种方法对应不同的数据类型,背后用的是不同的估值器。

方法 数据类型 默认估值器 适用场景
ofInt Int IntEvaluator 整数变化,比如透明度0→255
ofFloat Float FloatEvaluator 小数变化,比如平移、缩放
ofObject 任意类型 需要自定义TypeEvaluator 颜色、坐标点、自定义对象

举个例子,颜色动画就得用ofObject

val colorAnim = ObjectAnimator.ofObject(
    textView,
    "textColor",
    ArgbEvaluator(),        // 颜色估值器
    Color.RED,
    Color.BLUE
)
colorAnim.duration = 2000
colorAnim.start()

这里ArgbEvaluator会帮我们把红色到蓝色之间的颜色值算出来。如果你要动画化自定义对象,比如一个Point,那就得自己写TypeEvaluator了:

class PointEvaluator : TypeEvaluator<Point> {
    override fun evaluate(fraction: Float, startValue: Point, endValue: Point): Point {
        val x = startValue.x + (endValue.x - startValue.x) * fraction
        val y = startValue.y + (endValue.y - startValue.y) * fraction
        return Point(x.toInt(), y.toInt())
    }
}

说白了,fraction就是当前进度(0到1),你根据这个进度算出中间值就行。

知识体系总览

下面这张图帮你理清属性动画的核心脉络:

属性动画核心机制 ValueAnimator(数值发动机) TimeInterpolator 时间插值器(节奏控制) TypeEvaluator 估值器(数值计算) Choreographer 帧刷新驱动 ObjectAnimator(属性操作器) ofInt IntEvaluator ofFloat FloatEvaluator ofObject 自定义TypeEvaluator

我的小建议:刚开始学属性动画,先用ValueAnimator配合addUpdateListener写一遍,理解数值变化的本质。然后再用ObjectAnimator简化代码。这样即使以后遇到复杂场景,你也能自己用ValueAnimator拼出来。

最后说一句,属性动画的灵活性远不止这些。你还可以设置repeatCount让动画循环,用setInterpolator改变运动曲线,甚至用AnimatorSet组合多个动画。这些我们后面会慢慢展开。今天先把ValueAnimator和ObjectAnimator吃透,后面的路就好走了。


公众号:蓝海资料掘金营,微信deep3321