属性动画 执行流程

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);

流程图

相关推荐
故事与他6455 小时前
Thinkphp(TP)框架漏洞攻略
android·服务器·网络·中间件·tomcat
每次的天空8 小时前
项目总结:GetX + Kotlin 协程实现跨端音乐播放实时同步
android·开发语言·kotlin
m0_748233179 小时前
SQL之delete、truncate和drop区别
android·数据库·sql
CYRUS_STUDIO11 小时前
OLLVM 增加 C&C++ 字符串加密功能
android·c++·安全
帅次12 小时前
Flutter 输入组件 Radio 详解
android·flutter·ios·kotlin·android studio
&有梦想的咸鱼&13 小时前
Android Compose 框架的状态与 ViewModel 的协同(collectAsState)深入剖析(二十一)
android
开开心心就好13 小时前
高效PDF翻译解决方案:多引擎支持+格式零丢失
android·java·网络协议·tcp/ip·macos·智能手机·pdf
路上阡陌14 小时前
docker 安装部署 canal
android·adb·docker
&有梦想的咸鱼&15 小时前
入剖析 Android Compose 框架的关键帧动画(keyframes、Animatable)(二十三)
android
thinkMoreAndDoMore16 小时前
android音频概念解析
android·音视频