简介
属性动画(Property Animation)是 Android 3.0(API 11)引入的全新动画框架,用于弥补补间动画(View Animation)的不足。补间动画只能对 View 做平移、旋转、缩放、透明度四种变换,而且只是视觉上的变化,View 的实际位置(点击区域)不会改变。属性动画则可以直接修改对象的任意属性,既能改变视觉效果也能改变实际属性值,极大扩展了动画的应用场景。
本文从 AOSP 源码角度深入分析属性动画系统的核心组件和运行机制,涵盖 ValueAnimator、ObjectAnimator、AnimatorSet、ViewPropertyAnimator、SpringAnimation 五大动画类,以及 Interpolator(插值器)、TypeEvaluator(估值器)两大辅助系统。最后讨论动画驱动的底层原理和常见陷阱。
注意:源码分析基于 Android-30(Android 11),核心源码位于 frameworks/base/core/java/android/animation/ 目录。
属性动画体系总览
属性动画的核心类关系如下:
java.lang.Object └── android.animation.Animator (抽象类) ├── android.animation.ValueAnimator │ └── android.animation.ObjectAnimator └── android.animation.AnimatorSet
|
辅助类和接口:
android.animation.TimeInterpolator (接口) — 插值器 android.animation.TypeEvaluator (接口) — 估值器 android.animation.Keyframe — 关键帧 android.animation.PropertyValuesHolder — 属性值持有者
|
属性动画的核心思想是:在指定的时间范围内,根据插值器(Interpolator)计算时间进度(fraction),再通过估值器(TypeEvaluator)计算当前时间点对应的属性值,最后通过 setter 方法将值设置到目标对象上。
ValueAnimator:属性动画的基石
ValueAnimator 是属性动画体系的核心。它本身不操作任何对象,只是单纯地在指定时间内生成一系列动画值。
基本用法
ValueAnimator animator = ValueAnimator.ofFloat(0f, 100f); animator.setDuration(300); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float value = (float) animation.getAnimatedValue(); view.setTranslationX(value); } }); animator.start();
|
ofInt / ofFloat / ofObject
源码位置:frameworks/base/core/java/android/animation/ValueAnimator.java
public static ValueAnimator ofInt(int... values) { ValueAnimator anim = new ValueAnimator(); anim.setIntValues(values); return anim; }
public static ValueAnimator ofFloat(float... values) { ValueAnimator anim = new ValueAnimator(); anim.setFloatValues(values); return anim; }
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) { ValueAnimator anim = new ValueAnimator(); anim.setObjectValues(values); anim.setEvaluator(evaluator); return anim; }
|
ofInt 和 ofFloat 使用内置的 IntEvaluator 和 FloatEvaluator,ofObject 则需要自定义 TypeEvaluator。
参数 values 是可变长度参数,可以传入 2 个以上值来实现多阶段动画。例如 ValueAnimator.ofFloat(0f, 50f, 100f) 会生成一个从 0 到 50 再到 100 的动画序列。
setIntValues / setFloatValues 的内部实现
public void setFloatValues(float... values) { if (values == null || values.length == 0) { return; } if (mValues == null || mValues.length == 0) { setValues(PropertyValuesHolder.ofFloat("", values)); } else { PropertyValuesHolder valuesHolder = mValues[0]; valuesHolder.setFloatValues(values); } mInitialized = false; }
|
PropertyValuesHolder 是 ValueAnimator 的内部容器,每个 PropertyValuesHolder 管理一个属性的动画值序列。ValueAnimator 可以有多个 PropertyValuesHolder,这意味着一个动画可以同时驱动多个属性变化。
ObjectAnimator:自动设置属性值
ObjectAnimator 继承自 ValueAnimator,它在 ValueAnimator 的基础上增加了自动将动画值设置到目标对象属性的能力。
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationX", 0f, 100f); animator.setDuration(300); animator.start();
|
依赖 getter/setter 的机制
ObjectAnimator 通过反射调用目标对象的 getter 和 setter 方法。这就要求目标对象必须具备对应属性的 get<PropertyName>() 和 set<PropertyName>(Type value) 方法。
源码位置:frameworks/base/core/java/android/animation/ObjectAnimator.java
@Override public void start() { super.start(); }
@Override void initAnimation() { if (!mInitialized) { final Object target = getTarget(); if (target != null) { final int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { mValues[i].setupSetterAndGetter(target); } } super.initAnimation(); } }
|
PropertyValuesHolder 中的 setupSetterAndGetter 会通过反射查找对应的方法:
void setupSetterAndGetter(Object target) { Class targetClass = target.getClass(); mSetter = setupSetter(targetClass); for (Class klass : tempClasses) { mGetter = setupGetter(klass); if (mGetter != null) { break; } } }
|
常见命名规则:
- 属性名
alpha → getter: getAlpha(), setter: setAlpha(float)
- 属性名
translationX → getter: getTranslationX(), setter: setTranslationX(float)
- 属性名
backgroundColor → getter: getBackgroundColor(), setter: setBackgroundColor(int) (需要自定义,View 默认没有 setter)
自定义属性动画:没有 getter/setter 怎么办?
方案一:使用 ValueAnimator + addUpdateListener 手动设置
ValueAnimator animator = ValueAnimator.ofInt(currentColor, targetColor); animator.addUpdateListener(animation -> { int color = (int) animation.getAnimatedValue(); myView.setCustomColor(color); }); animator.start();
|
方案二:使用 View 的 setTag / getTag 配合 ObjectAnimator(不推荐)
方案三:使用 Property 包装类
ObjectAnimator animator = ObjectAnimator.ofInt(myView, new IntProperty<MyView>("customColor") { @Override public void setValue(MyView object, int value) { object.setCustomColor(value); }
@Override public Integer get(MyView object) { return object.getCustomColor(); } }, startColor, endColor); animator.start();
|
AnimatorSet:编排多个动画
AnimatorSet 用于将多个动画组合在一起,支持同时播放、顺序播放和延迟播放。
基本用法
ObjectAnimator fadeOut = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f); ObjectAnimator moveUp = ObjectAnimator.ofFloat(view, "translationY", 0f, -200f); ObjectAnimator scale = ObjectAnimator.ofFloat(view, "scaleX", 1f, 1.5f);
AnimatorSet set = new AnimatorSet();
set.play(scale).after(fadeOut).after(moveUp);
set.play(fadeOut).with(moveUp); set.play(scale).after(fadeOut); set.start();
|
核心方法
playTogether 和 playSequentially
AnimatorSet set = new AnimatorSet(); set.playTogether(anim1, anim2, anim3); set.start();
AnimatorSet set = new AnimatorSet(); set.playSequentially(anim1, anim2, anim3); set.start();
|
这些静态便利方法内部创建 AnimatorSet 并调用相应的 Builder 方法。
Builder 模式:before / after / with
源码位置:frameworks/base/core/java/android/animation/AnimatorSet.java
public Builder play(Animator anim) { if (anim != null) { return new Builder(anim); } return null; }
public class Builder { private Node mCurrentNode;
Builder(Animator anim) { mCurrentNode = getNodeForAnimation(anim); }
public Builder with(Animator anim) { Node node = getNodeForAnimation(anim); mCurrentNode.addSibling(node); return this; }
public Builder before(Animator anim) { Node node = getNodeForAnimation(anim); node.addChild(mCurrentNode); return this; }
public Builder after(Animator anim) { Node node = getNodeForAnimation(anim); mCurrentNode.addChild(node); return this; }
public Builder after(long delay) { ValueAnimator delayAnim = ValueAnimator.ofFloat(0f, 1f); delayAnim.setDuration(delay); after(delayAnim); return this; } }
|
内部通过 Node 节点构建一个有向无环图(DAG),每个 Node 代表一个动画,节点之间的边表示播放顺序(先播放 → 后播放)或并行关系(同时播放)。
AnimatorSet 内部的动画依赖图
AnimatorSet 内部使用 AnimatorSet.Builder 构建动画依赖关系:
// 示例:set.play(anim1).before(anim2); set.play(anim1).with(anim3); // 依赖关系: // anim1 ──before──> anim2 // anim1 ──with───> anim3 // // 播放顺序:anim1 和 anim3 同时开始,anim1 结束后 anim2 开始
|
Node 内部维护着 parents(前驱节点列表)和 siblings(兄弟节点列表)。当一个 Node 的所有 parents 都播放完成后,该 Node 才开始播放。siblings 会同时开始。
插值器(Interpolator / TimeInterpolator)
插值器决定动画的时间曲线,即时间进度(elapsed fraction)到动画进度(animated fraction)的映射。
TimeInterpolator 接口
public interface TimeInterpolator { float getInterpolation(float input); }
|
input 参数范围是 [0, 1],表示时间进度。返回值也是 [0, 1](大多数情况),表示动画进度。LinearInterpolator 的输入输出完全一致,AccelerateDecelerateInterpolator 则呈现先慢后快再慢的曲线。
常用插值器深度解析
AccelerateDecelerateInterpolator(加速减速插值器)
源码位置:frameworks/base/core/java/android/view/animation/AccelerateDecelerateInterpolator.java
public float getInterpolation(float input) { return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f; }
|
曲线形状:余弦函数。在 input=0.5 处对称,是一条 S 形曲线。动画开始时缓慢加速,中间段速度最快,结束时缓慢减速。这是 Android 中很多默认动画使用的插值器。
OvershootInterpolator(超出插值器)
源码位置:frameworks/base/core/java/android/view/animation/OvershootInterpolator.java
public OvershootInterpolator(float tension) { mTension = tension; }
public float getInterpolation(float t) { t -= 1.0f; return t * t * ((mTension + 1) * t + mTension) + 1.0f; }
|
mTension 是张力参数,默认值为 2.0f。值越大,超出目标后回弹的幅度越大。动画值会先超过目标值,然后慢慢回到目标值,产生”弹性”效果。
BounceInterpolator(弹跳插值器)
源码位置:frameworks/base/core/java/android/view/animation/BounceInterpolator.java
private static float bounce(float t) { return t * t * 8.0f; }
public float getInterpolation(float t) { t *= 1.1226f; if (t < 0.3535f) return bounce(t); else if (t < 0.7408f) return bounce(t - 0.54719f) + 0.7f; else if (t < 0.9644f) return bounce(t - 0.8526f) + 0.9f; else return bounce(t - 1.0435f) + 0.95f; }
|
模拟物理弹跳效果。物体从高处落下,在地面弹跳多次后逐渐停止。使用分段函数模拟每次弹跳,弹跳幅度逐渐减小。
PathInterpolator(路径插值器,API 21+)
使用三次贝塞尔曲线定义动画节奏。参数 controlX1, controlY1, controlX2, controlY2 定义控制点:
PathInterpolator interpolator = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
|
源码位置:frameworks/base/core/java/android/view/animation/PathInterpolator.java
public PathInterpolator(float controlX1, float controlY1, float controlX2, float controlY2) { initCubic(controlX1, controlY1, controlX2, controlY2); }
private void initCubic(float x1, float y1, float x2, float y2) { Path path = new Path(); path.moveTo(0, 0); path.cubicTo(x1, y1, x2, y2, 1, 1); initPath(path); }
|
使用 Path 对象来灵活定义曲线,包括弧线等复杂路径。
AnticipateOvershootInterpolator(预期超出插值器)
组合了 AnticipateInterpolator(先后退再前进)和 OvershootInterpolator(超出后回弹):
public AnticipateOvershootInterpolator(float tension) { mTension = tension * 1.5f; }
public float getInterpolation(float t) { if (t < 0.5f) return 0.5f * a(t * 2.0f, mTension); else return 0.5f * (o(t * 2.0f - 2.0f, mTension) + 2.0f); }
|
效果:动画开始前先向反方向移动一小段,然后加速前进,超出目标值后再回弹到目标位置。
自定义插值器
实现 TimeInterpolator 接口即可:
public class MyEaseOutInterpolator implements TimeInterpolator { @Override public float getInterpolation(float input) { return 1.0f - (1.0f - input) * (1.0f - input); } }
|
估值器(TypeEvaluator)
估值器负责根据动画进度(fraction)计算当前的属性值。它与插值器的区别在于:
- TimeInterpolator:计算”时间进度 → 动画进度”的映射。输入是时间百分比,输出是动画百分比。
- TypeEvaluator:计算”动画进度 → 具体属性值”的映射。输入是动画百分比(0~1),输出是具体的属性值。
内置估值器
IntEvaluator
public class IntEvaluator implements TypeEvaluator<Integer> { public Integer evaluate(float fraction, Integer startValue, Integer endValue) { int startInt = startValue; return (int)(startInt + fraction * (endValue - startInt)); } }
|
FloatEvaluator
public class FloatEvaluator implements TypeEvaluator<Number> { public Float evaluate(float fraction, Number startValue, Number endValue) { float startFloat = startValue.floatValue(); return startFloat + fraction * (endValue.floatValue() - startFloat); } }
|
ArgbEvaluator(颜色估值器)
public class ArgbEvaluator implements TypeEvaluator { public Object evaluate(float fraction, Object startValue, Object endValue) { int startInt = (Integer) startValue; float startA = ((startInt >> 24) & 0xff) / 255.0f; float startR = ((startInt >> 16) & 0xff) / 255.0f; float startG = ((startInt >> 8) & 0xff) / 255.0f; float startB = ( startInt & 0xff) / 255.0f;
int endInt = (Integer) endValue; float endA = ((endInt >> 24) & 0xff) / 255.0f; float endR = ((endInt >> 16) & 0xff) / 255.0f; float endG = ((endInt >> 8) & 0xff) / 255.0f; float endB = ( endInt & 0xff) / 255.0f;
float a = startA + fraction * (endA - startA); float r = startR + fraction * (endR - startR); float g = startG + fraction * (endG - startG); float b = startB + fraction * (endB - startB);
return Math.round(a * 255) << 24 | Math.round(r * 255) << 16 | Math.round(g * 255) << 8 | Math.round(b * 255); } }
|
自定义估值器
以 PointFEvaluator 为例(对 PointF 做动画):
public class PointFEvaluator implements TypeEvaluator<PointF> { private PointF mPoint = new PointF();
@Override public PointF evaluate(float fraction, PointF startValue, PointF endValue) { float x = startValue.x + fraction * (endValue.x - startValue.x); float y = startValue.y + fraction * (endValue.y - startValue.y); mPoint.set(x, y); return mPoint; } }
ValueAnimator animator = ValueAnimator.ofObject( new PointFEvaluator(), new PointF(0, 0), new PointF(100, 200) ); animator.addUpdateListener(animation -> { PointF point = (PointF) animation.getAnimatedValue(); view.setX(point.x); view.setY(point.y); }); animator.start();
|
ViewPropertyAnimator:为 View 优化的动画接口
ViewPropertyAnimator 是 View 动画的便捷 API,通过 view.animate() 获取。它是专门为 View 的常用属性(translationX/Y/Z、scaleX/Y、rotation/rotationX/Y、alpha、x、y、z)设计的。
基本用法
view.animate() .x(500f) .y(300f) .scaleX(1.5f) .scaleY(1.5f) .alpha(0.5f) .rotation(90f) .setDuration(300) .setInterpolator(new AccelerateDecelerateInterpolator()) .setStartDelay(100) .setListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { }
@Override public void onAnimationEnd(Animator animation) { }
@Override public void onAnimationCancel(Animator animation) { }
@Override public void onAnimationRepeat(Animator animation) { } }) .start();
|
内部实现
源码位置:frameworks/base/core/java/android/view/ViewPropertyAnimator.java
public ViewPropertyAnimator x(float value) { return xBy(value - mView.mLeft); }
public ViewPropertyAnimator xBy(float value) { animateProperty(TRANSLATION_X, value); return this; }
private void animateProperty(int constantName, float toValue) { float fromValue = getValue(constantName); NameValuesHolder holder = new NameValuesHolder(constantName, fromValue, toValue); mPendingAnimations.add(holder); mView.removeCallbacks(mAnimationStarter); mView.postOnAnimation(mAnimationStarter); }
|
mAnimationStarter 是一个 Runnable,它会在下一帧动画回调时执行:
private Runnable mAnimationStarter = new Runnable() { @Override public void run() { startAnimation(); } };
private void startAnimation() { mAnimator = ValueAnimator.ofFloat(0.0f, 1.0f); mAnimator.start(); }
|
ViewPropertyAnimator 的优势
- API 简洁:链式调用,一行代码搞定动画。
- 自动并发:多个属性的动画自动同时进行,无需 AnimatorSet。
- 性能更好:内部自动优化,多个动画共享一个 ValueAnimator 和一次 VSYNC 回调。
- 自动处理起始值:不需要手动指定 fromValue,系统自动读取当前属性值。
- 资源管理更高效:视图卸载时自动取消动画。
动画驱动的底层原理
Choreographer 驱动的帧循环
属性动画的帧驱动最终依赖 Choreographer 的 VSYNC 信号。
源码位置:frameworks/base/core/java/android/animation/AnimationHandler.java
public class AnimationHandler { private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() { @Override public void doFrame(long frameTimeNanos) { doAnimationFrame(getProvider().getFrameTime()); if (mAnimationCallbacks.size() > 0) { getProvider().postFrameCallback(this); } } };
void start() { if (!mAnimationScheduled) { mAnimationScheduled = true; getProvider().postFrameCallback(mFrameCallback); } } }
|
完整调用链:
VSYNC 信号 → Choreographer.doFrame(frameTimeNanos) → Choreographer.doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos) → AnimationHandler.doAnimationFrame(frameTime) → ValueAnimator.doAnimationFrame(frameTime) → ValueAnimator.animateBasedOnTime(currentTime) → 调用 Interpolator.getInterpolation(fraction) → 调用 TypeEvaluator.evaluate(animatedFraction, ...) → 通过 setter 设置属性值 / 回调 AnimatorUpdateListener
|
doAnimationFrame 的详细流程
boolean doAnimationFrame(long frameTime) { if (mStartTime < 0) { mStartTime = mReversing ? frameTime : frameTime + (long)(mStartDelay * sDurationScale); }
if (mPaused) { mPauseTime = frameTime; return false; } else if (mResumed) { mStartTime += (frameTime - mPauseTime); mResumed = false; }
if (!mRunning) { return false; }
final long currentTime = Math.max(frameTime, mStartTime); boolean finished = animateBasedOnTime(currentTime);
if (finished) { endAnimation(); } return finished; }
|
animateBasedOnTime 是核心方法,计算 elapsed fraction,应用插值器,然后通过 PropertyValuesHolder 设置属性值:
boolean animateBasedOnTime(long currentTime) { boolean done = false; if (mRunning) { final long scaledDuration = getScaledDuration(); final float fraction = scaledDuration > 0 ? (float)(currentTime - mStartTime) / scaledDuration : 1f; final float lastFraction = mOverallFraction;
final boolean newIteration = (int) fraction > (int) lastFraction; final boolean lastIterationFinished = (fraction >= mRepeatCount + 1) && mRepeatCount != INFINITE;
if (scaledDuration == 0) { done = true; } else if (fraction >= 1f && mCurrentIteration < mRepeatCount) { if (mRepeatMode == REVERSE) { ... } else { ... } }
mOverallFraction = clampFraction(fraction); float currentIterationFraction = getCurrentIterationFraction( mOverallFraction, mRepeatCount != INFINITE); animateValue(currentIterationFraction); } return done; }
void animateValue(float fraction) { fraction = mInterpolator.getInterpolation(fraction); mCurrentFraction = fraction; int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { mValues[i].calculateValue(fraction); } if (mUpdateListeners != null) { int numListeners = mUpdateListeners.size(); for (int i = 0; i < numListeners; ++i) { mUpdateListeners.get(i).onAnimationUpdate(this); } } }
|
SpringAnimation:物理弹簧动画(androidx)
从 Support Library 25.3.0 开始,Android 引入了基于物理的弹簧动画。
基本原理
SpringAnimation 不基于时间和插值器,而是基于真实的物理模拟:在每一帧中,根据弹簧的阻尼系数(damping ratio)和刚度(stiffness)计算当前位置的加速度,更新速度和位置,当能量耗尽时动画自然停止。
使用方式
SpringAnimation springAnim = new SpringAnimation(view, DynamicAnimation.TRANSLATION_Y, 0f);
springAnim.getSpring().setStiffness(SpringForce.STIFFNESS_MEDIUM); springAnim.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY);
springAnim.setStartVelocity(2000f); springAnim.start();
|
参数说明:
SpringAnimation 的优势是动画过程在物理上自然逼真,并且可以根据手势速度动态调整(例如松手时的初始速度)。
XML 中定义属性动画
属性动画也可以在 XML 中定义,放在 res/animator/ 目录下。
animator 标签(对应 ValueAnimator)
<animator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="300" android:startOffset="100" android:repeatCount="infinite" android:repeatMode="reverse" android:valueFrom="0" android:valueTo="100" android:valueType="floatType" android:interpolator="@android:interpolator/accelerate_decelerate" />
|
objectAnimator 标签(对应 ObjectAnimator)
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="300" android:propertyName="translationX" android:valueFrom="0" android:valueTo="100" android:valueType="floatType" />
|
set 标签(对应 AnimatorSet)
<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> <objectAnimator android:duration="300" android:propertyName="alpha" android:valueFrom="1" android:valueTo="0" /> <objectAnimator android:duration="500" android:propertyName="translationY" android:valueFrom="0" android:valueTo="-200" /> </set>
|
在代码中加载
ValueAnimator animator = (ValueAnimator) AnimatorInflater.loadAnimator(context, R.animator.value_animator); animator.addUpdateListener(animation -> { ... }); animator.start();
ObjectAnimator objAnim = (ObjectAnimator) AnimatorInflater.loadAnimator(context, R.animator.object_animator); objAnim.setTarget(view); objAnim.start();
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(context, R.animator.animator_set); set.setTarget(view); set.start();
|
常见陷阱与最佳实践
内存泄漏:记得取消动画
属性动画持有对目标对象的引用,如果动画无限循环且未在 Activity/Fragment 销毁时取消,会导致目标对象(通常是 View 或 Activity)无法被 GC 回收。
@Override protected void onDestroy() { super.onDestroy(); if (mAnimator != null && mAnimator.isRunning()) { mAnimator.cancel(); } if (mAnimatorSet != null) { mAnimatorSet.cancel(); } }
|
硬件层优化
对于大范围或复杂的动画(尤其是 alpha 和 scale 动画),可以在动画期间启用硬件层:
view.setLayerType(View.LAYER_TYPE_HARDWARE, null); view.animate() .alpha(0f) .setDuration(300) .withEndAction(() -> { view.setLayerType(View.LAYER_TYPE_NONE, null); }) .start();
|
原理:LAYER_TYPE_HARDWARE 会将 View 渲染到一个 GPU 纹理中,动画期间只需对这个纹理做矩阵变换,避免了每帧重新执行绘制命令。动画结束后移除层以释放显存。
不要在动画回调中做耗时操作
onAnimationUpdate 在每个动画帧都会调用(约 16ms 一次),在其中执行耗时操作会导致掉帧:
animator.addUpdateListener(animation -> { Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.huge_image); view.setImageBitmap(bitmap); });
Bitmap preloadedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.huge_image); animator.addUpdateListener(animation -> { view.setImageAlpha((int) ((float) animation.getAnimatedValue() * 255)); });
|
View 属性动画的”点击问题”
补间动画(View Animation)只改变视觉效果,不改变 View 的实际位置。属性动画直接修改属性值,所以实际位置(包括点击区域)会跟随动画变化。但要注意:如果使用 ObjectAnimator 动画的是非位置属性(如 scaleX、scaleY),View 的触摸区域可能不在预期位置。
正确使用 AnimatorListener
如果只需要动画结束回调,可以使用 AnimatorListenerAdapter 避免实现所有方法:
animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { } });
|
关键源码文件汇总
| 文件 |
路径 |
| ValueAnimator.java |
frameworks/base/core/java/android/animation/ValueAnimator.java |
| ObjectAnimator.java |
frameworks/base/core/java/android/animation/ObjectAnimator.java |
| AnimatorSet.java |
frameworks/base/core/java/android/animation/AnimatorSet.java |
| PropertyValuesHolder.java |
frameworks/base/core/java/android/animation/PropertyValuesHolder.java |
| AnimationHandler.java |
frameworks/base/core/java/android/animation/AnimationHandler.java |
| ViewPropertyAnimator.java |
frameworks/base/core/java/android/view/ViewPropertyAnimator.java |
| AccelerateDecelerateInterpolator.java |
frameworks/base/core/java/android/view/animation/AccelerateDecelerateInterpolator.java |
| OvershootInterpolator.java |
frameworks/base/core/java/android/view/animation/OvershootInterpolator.java |
| BounceInterpolator.java |
frameworks/base/core/java/android/view/animation/BounceInterpolator.java |
| PathInterpolator.java |
frameworks/base/core/java/android/view/animation/PathInterpolator.java |
| ArgbEvaluator.java |
frameworks/base/core/java/android/animation/ArgbEvaluator.java |
总结
属性动画的核心框架由三层构成:
- 时间层(TimeInterpolator):控制动画节奏,决定时间如何映射到动画进度。
- 计算层(TypeEvaluator):根据动画进度计算具体的属性值。
- 驱动层(AnimationHandler / Choreographer):通过 VSYNC 信号驱动每一帧的计算和更新。
理解这三层的协作关系,就能灵活运用属性动画实现各种复杂的动画效果。对于日常开发,ViewPropertyAnimator 是最常用的 API,简洁高效;对于需要精确控制的场景,ValueAnimator 和 ObjectAnimator 是更灵活的选择;对于追求自然物理效果的交互,SpringAnimation 是最佳选择。无论使用哪种 API,都要注意在适当的时候取消动画以避免内存泄漏。