属性动画 执行流程

Android 动画系统主要分为三大类

1. 视图动画(View Animation)

作用对象View
特点 :仅改变 View 的绘制效果,不改变其真实属性(如位置、大小)。
子分类

类型 描述 示例
补间动画(Tween Animation) 通过插值器计算中间帧,实现平移、旋转、缩放、透明度变化。 AlphaAnimationTranslateAnimationScaleAnimationRotateAnimation
帧动画(Frame Animation) 逐帧播放图片序列(类似GIF)。 AnimationDrawable 加载多张图片。

代码示例(补间动画)

xml

复制

xml 复制代码
<!-- res/anim/fade_in.xml -->
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:fromAlpha="0.0"
    android:toAlpha="1.0" />

运行 HTML

java

复制

ini 复制代码
Animation anim = AnimationUtils.loadAnimation(this, R.anim.fade_in);
view.startAnimation(anim);

缺点

  • 点击事件仍在原始位置(未随动画移动)。
  • 只能作用于 View,无法应用于非 View 对象。

2. 属性动画(Property Animation)

作用对象 :任意对象的属性(包括 View 和非 View)。
特点 :通过动态修改对象的属性值实现动画(真实改变属性值)。
核心类

类/接口 作用
ValueAnimator 计算属性值的变化过程,需手动更新目标对象。
ObjectAnimator 自动更新目标对象的属性(需有对应的 setter/getter)。
AnimatorSet 组合多个动画(顺序/并行播放)。
TimeInterpolator 控制动画变化速率(如加速、减速)。

代码示例

java

复制

ini 复制代码
// 透明度动画(0 → 1)
ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f);
alphaAnim.setDuration(1000);
alphaAnim.start();

// 组合动画:平移 + 旋转
AnimatorSet set = new AnimatorSet();
set.playTogether(
    ObjectAnimator.ofFloat(view, "translationX", 0f, 100f),
    ObjectAnimator.ofFloat(view, "rotation", 0f, 360f)
);
set.start();

优点

  • 真实改变属性值,点击事件跟随动画。
  • 可作用于任意对象的任意属性。

缺点

  • 需要目标属性有 setter 方法(如 setAlpha())。

3. 过渡动画(Transition Animation)

作用场景 :界面切换(Activity/Fragment/ViewGroup)时的动画效果。
子分类

类型 描述
Activity 过渡动画 通过 overridePendingTransition()ActivityOptions 实现。
Fragment 过渡动画 使用 setCustomAnimations() 设置进入/退出动画。
共享元素动画 两个界面共享的 View 平滑过渡(如图片放大)。
场景动画(Scene) ViewGroup 中切换布局时的动画(如 TransitionManager.go())。

代码示例(Activity 过渡)

java

复制

javascript 复制代码
// 启动 Activity 时设置动画
Intent intent = new Intent(this, DetailActivity.class);
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());

// 在 styles.xml 中定义过渡效果
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight">
    <item name="android:windowActivityTransitions">true</item>
    <item name="android:windowEnterTransition">@transition/slide_in</item>
    <item name="android:windowExitTransition">@transition/slide_out</item>
</style>

属性动画/视图动画区别

属性动画

核心机制

  • 修改真实属性值 :直接调用对象的 setter 方法(如 setTranslationX()setAlpha()),永久改变属性
  • 绘制阶段生效 :通过 Viewdraw() 方法实时更新属性值,触发重绘。
  • 点击事件跟随动画:因为属性值真实变化,触摸事件坐标会同步更新。

视图动画

核心机制

  • 矩阵变换(Matrix) :通过 Canvas 的变换(如平移、旋转)临时改变绘制效果,不修改真实属性
  • 布局阶段生效 :在 Viewdraw() 方法中应用变换矩阵,但 View 的布局参数(如 lefttop)不变。
  • 点击事件不跟随:触摸事件仍作用在原始位置(因为属性未变)。

属性动画 源码分析

scss 复制代码
ObjectAnimator.ofFloat(view, "translationX", 0f, 100f).apply {
    duration = 1000
    start()
}

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
    //创建 ObjectAnimator
    ObjectAnimator anim = new ObjectAnimator(target, propertyName);
    anim.setFloatValues(values);
    return anim;
}

设置目标view,属性

scss 复制代码
private ObjectAnimator(Object target, String propertyName) {
    setTarget(target);
    setPropertyName(propertyName);
}
ini 复制代码
public void setFloatValues(float... values) {
    
    if (mValues == null || mValues.length == 0) {
        //创建 PropertyValuesHolder
        setValues(PropertyValuesHolder.ofFloat("", values));
    } else {
        PropertyValuesHolder valuesHolder = mValues[0];
        valuesHolder.setFloatValues(values);
    }
    mInitialized = false;
}

FloatPropertyValuesHolder

scss 复制代码
public FloatPropertyValuesHolder(String propertyName, float... values) {
    super(propertyName);
    setFloatValues(values);
}

KeyframeSet

ini 复制代码
public void setFloatValues(float... values) {
    mValueType = float.class;
    mKeyframes = KeyframeSet.ofFloat(values);
}

start() 开启动画

scss 复制代码
addAnimationCallback(0);

startAnimation();
initAnimation();

addAnimationCallback 设置sync回调

scss 复制代码
addAnimationCallback(0);

getProvider().postFrameCallback(mFrameCallback);

MyFrameCallbackProvider

java 复制代码
private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {

    final Choreographer mChoreographer = Choreographer.getInstance();

    @Override
    public void postFrameCallback(Choreographer.FrameCallback callback) {
        mChoreographer.postFrameCallback(callback);
    }

    @Override
    public void postCommitCallback(Runnable runnable) {
        mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, runnable, null);
    }

mFrameCallback

java 复制代码
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
    @Override
    public void doFrame(long frameTimeNanos) {
        doAnimationFrame(getProvider().getFrameTime());
        if (mAnimationCallbacks.size() > 0) {
            getProvider().postFrameCallback(this);
        }
    }
};

initAnimation()

ini 复制代码
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

ini 复制代码
Class targetClass = target.getClass();
if (mSetter == null) {
    //反射获取 method 
    setupSetter(targetClass);
}

callback.doAnimationFrame(frameTime);

当 同步信号来到,执行 FrameCallback,

animateValue(currentIterationFraction);

ini 复制代码
void animateValue(float fraction) {
    final Object target = getTarget();
    super.animateValue(fraction);
    int numValues = mValues.length;
    for (int i = 0; i < numValues; ++i) {
        mValues[i].setAnimatedValue(target);
    }
}

FloatPropertyValuesHolder

arduino 复制代码
void calculateValue(float fraction) {
    //通过插值器mInterpolator拿到当前动画的完成度,
    mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
}

FloatPropertyValuesHolder.setAnimatedValue

ini 复制代码
mTmpValueArray[0] = mFloatAnimatedValue;
mSetter.invoke(target, mTmpValueArray);

流程图

相关推荐
robotx2 小时前
安卓线程相关
android
消失的旧时光-19433 小时前
Android 面试高频:JSON 文件、大数据存储与断电安全(从原理到工程实践)
android·面试·json
dalancon4 小时前
VSYNC 信号流程分析 (Android 14)
android
dalancon4 小时前
VSYNC 信号完整流程2
android
dalancon4 小时前
SurfaceFlinger 上帧后 releaseBuffer 完整流程分析
android
用户69371750013845 小时前
不卷AI速度,我卷自己的从容——北京程序员手记
android·前端·人工智能
程序员Android5 小时前
Android 刷新一帧流程trace拆解
android
墨狂之逸才6 小时前
解决 Android/Gradle 编译报错:Comparison method violates its general contract!
android
阿明的小蝴蝶6 小时前
记一次Gradle环境的编译问题与解决
android·前端·gradle
汪海游龙7 小时前
开源项目 Trending AI 招募 Google Play 内测人员(12 名)
android·github