外贸网站建设深圳,怎么做网站盈利,常州做网站软件,网站开发的选择是什么我来用通俗易懂的语言#xff0c;结合生活中的例子#xff0c;给你讲讲Android动画框架的实现原理。
想象一下#xff0c;我们想让一个方块在屏幕上从左边滑到右边。
1. 动画的本质#xff1a;快速翻页的连环画
动画的核心原理#xff0c;其实就像我们小时候玩的“连环…
我来用通俗易懂的语言结合生活中的例子给你讲讲Android动画框架的实现原理。
想象一下我们想让一个方块在屏幕上从左边滑到右边。
1. 动画的本质快速翻页的连环画
动画的核心原理其实就像我们小时候玩的“连环画”或者“翻书动画”。你快速翻动书页每一页上的图画都只比前一页稍微变动一点点最终就看到了一个会动的画面。 在Android屏幕上这个“翻书”的速度非常快通常每秒翻60次也就是60帧这样人眼就感觉不到一帧一帧的跳跃而是看到了连续流畅的运动。
2. 早期的“假”动画视图动画 (View Animation)
想象你有一个方块你想让它动起来。早期的Android动画叫“视图动画”或“补间动画”是怎么做的呢
它就像在方块的玻璃投影上做手脚。
你有一个方块的“真身”在屏幕左边它是静止不动的。动画系统生成一个投影。然后它以极快的速度每秒60次不断地改变这个投影的位置第一帧投影在左边第二帧稍微往右一点第三帧再往右一点……屏幕上你看到的是这个会动的投影感觉方块在动。
问题来了 如果你点击这个“动起来”的方块你会发现它没反应因为你点的是投影而方块的“真身”还在屏幕的左边老地方待着呢。这就是“幽灵点击”问题因为它只改了绘制效果没改真实属性。
3. 现代的“真”动画属性动画 (Property Animation)
后来的Android系统3.0版本之后觉得这种“假”动画不行它引入了“属性动画”。这次我们不再改变投影而是直接改变方块的“真实属性”。 这就像我们请了两个“动画师”来帮忙
a. 动画值计算师 (ValueAnimator)
这个动画师非常聪明它只做一件事在规定时间内根据你设定的规则帮你计算出一个个“中间值”。
你告诉它我要从“位置0”动到“位置100”用1秒钟。它会说好的0.01秒的时候我在“位置10”0.02秒的时候我在“位置20”以此类推……它就像一个专业的“数学家”只负责计算这些中间值它不关心这些值是用来移动方块的还是用来改变颜色的它只管算。动画速度控制器 (Interpolator / 插值器) 这个计算师还有一个助手叫“动画速度控制器”。你告诉它你是想让动画匀速像跑步机一样还是先慢后快像汽车启动还是先快后慢像电梯停下。这个控制器会根据你的要求调整计算师算值的“节奏”。 匀速线性插值器1秒跑100米0.5秒就跑50米。加速加速插值器0.5秒可能只跑了20米但后0.5秒跑了80米。
b. 对象属性操纵师 (ObjectAnimator)
这个动画师是动画值计算师的进阶版它更厉害它知道怎么把“动画值计算师”算出来的值直接应用到对象的“真实属性”上。
你告诉它我要让“对象”的“X轴位置”动起来。它会监听“动画值计算师”算出来的每一个中间值。每当“动画值计算师”算出一个新值比如“位置90”对象属性操纵师就会立刻找到该对象并真正地把它在X轴上移动到“位置90”。这次该对象的“真身”是真的动了所以你点击它它就在新位置响应了。
4. 动画的“节拍器”Choreographer (节奏大师)
所有这些动画师都需要一个统一的“节拍器”确保大家步调一致而且动画在最恰当的时机显示在屏幕上。这个“节拍器”就是Choreographer我们可以叫它“节奏大师”。
你的手机屏幕有刷新频率通常每秒60次。可以想象成每隔1/60秒屏幕就会问“有没有新的画面要画”Choreographer就像一个经验丰富的舞台总监它会监听屏幕的“刷新信号”这个信号非常精确叫Vsync垂直同步信号。每当屏幕发出“我要刷新了”的信号时Choreographer就会立刻通知所有正在运行的动画“快现在是刷新时间赶紧计算你们的下一帧”这样所有的动画计算和UI更新都会和屏幕的刷新频率完美同步避免了画面撕裂画面上下半部分不同步的丑陋现象让动画看起来极其流畅。
5. 动画的“画图工人”GPU (图形处理器) 和 硬件加速
最后计算出来的动画效果最终要画到屏幕上。这个“画图工人”就是你手机里的GPU图形处理器。
在早期画图都是CPU在干又慢又累。现在Android默认开启了硬件加速。这意味着当你的方块改变位置、大小、透明度时Android会把这些“画图指令”打包直接发给GPU去处理。GPU专门干这个速度飞快。而且它还会把方块的初始样子先“刻”下来叫“显示列表”下次只是移动方块它就不需要重新画一遍直接把已经“刻”好的方块移动到新位置就行了效率极高。
总结
动画本质快速播放一系列微小变化的画面。视图动画只改“投影”不改“真身”有“幽灵点击”的问题。属性动画 ValueAnimator动画值计算师负责计算动画的中间值不关心具体是啥只管算。有Interpolator动画速度控制器调节速度。ObjectAnimator对象属性操纵师把ValueAnimator算出的值真正应用到对象的实际属性上比如方块的X轴位置。 Choreographer节奏大师统一的“节拍器”监听屏幕刷新信号确保动画与屏幕刷新同步流畅不撕裂。硬件加速 GPU画图工人将画图任务交给专业的GPU并利用显示列表等技术大大提高动画渲染效率让动画丝滑流畅。 属性动画的启动过程
1. ValueAnimator.start() 或 ObjectAnimator.start()启动动画。
2. 注册 Choreographer.FrameCallback动画内部ValueAnimator将一个FrameCallback注册到Choreographer。
Choreographer 是动画和UI渲染的心跳节奏大师它将所有UI更新与Vsync信号同步。
模拟源码分析
当你调用ValueAnimator.start()时它最终会注册一个FrameCallback到Choreographer。
// 模拟 ValueAnimator 内部注册 FrameCallback 的逻辑
public void start() {// ...// 获取 Choreographer 实例Choreographer choreographer Choreographer.getInstance();// 注册一个动画帧回调choreographer.postFrameCallback(mAnimationCallback); // mAnimationCallback 是 ValueAnimator 内部的 FrameCallback 实例// ...
}// 模拟 ValueAnimator 内部 mAnimationCallback 的实现
private final FrameCallback mAnimationCallback new FrameCallback() {Overridepublic void doFrame(long frameTimeNanos) {// 在这里进行动画的计算和更新doAnimationFrame(frameTimeNanos);// 如果动画还在进行继续注册下一帧回调if (mRunning) {Choreographer.getInstance().postFrameCallback(this);}}
};Choreographer 内部机制简述
Choreographer 内部有一个Handler (mHandler) 和一个Looper。 当Vsync信号到来时由DisplayManagerService或其他底层服务通知它会向mHandler发送一个消息。当这个消息被处理时mHandler会遍历所有已注册的FrameCallback并调用它们的doFrame()方法。
3. Vsync 信号到来显示器发出Vsync信号Choreographer收到通知。
4. Choreographer 回调 doFrame()Choreographer执行所有已注册的FrameCallback的doFrame()方法。
5. ValueAnimator.doAnimationFrame() 执行
计算动画播放时间。通过mInterpolator.getInterpolation()计算插值进度。通过mEvaluator.evaluate()计算当前动画值。
ValueAnimator 是属性动画的基石动画值计算师它负责在给定的时间和范围内计算出动画的当前值。
模拟源码分析 (doAnimationFrame 核心逻辑):
// ValueAnimator.java (简化版)
void doAnimationFrame(long frameTimeNanos) {if (mStartTime 0) { // 第一次回调mStartTime frameTimeNanos;}// 计算动画已经播放的时间mCurrentPlayTime (frameTimeNanos - mStartTime) / 1_000_000L; // 转换为毫秒// 计算动画的进度 (0.0 to 1.0)float fraction 0f;if (mDuration 0) {fraction (float)mCurrentPlayTime / mDuration;fraction Math.min(fraction, 1.0f); // 确保不超过1.0}// 应用时间插值器 (Interpolator)fraction mInterpolator.getInterpolation(fraction);// 应用类型估值器 (Evaluator) 计算最终动画值mAnimatedValue mEvaluator.evaluate(fraction, mStartValue, mEndValue);// 通知所有注册的更新监听器notifyUpdateListeners();// 检查动画是否结束if (mCurrentPlayTime mDuration) {endAnimation(); // 结束动画停止注册下一帧回调}
}private void notifyUpdateListeners() {for (AnimatorUpdateListener listener : mUpdateListeners) {listener.onAnimationUpdate(this); // 回调监听器ValueAnimator 自身作为参数}
}6. ObjectAnimator 应用属性
如果使用ObjectAnimator它会使用反射或更高效的Property机制调用目标对象的setter方法将计算出的动画值应用到指定属性上。 例如view.setAlpha(newAlphaValue);
ObjectAnimator - 将动画值应用到属性对象属性操纵师 ObjectAnimator 继承自 ValueAnimator它在 ValueAnimator 计算出值的基础上通过反射或直接调用setter将值应用到目标对象的特定属性上。
模拟源码分析 (ObjectAnimator 如何利用 ValueAnimator):
ObjectAnimator 重写了 ValueAnimator 的 animateValue() 方法或者更准确地说ValueAnimator 在调用notifyUpdateListeners()后ObjectAnimator 会在内部处理属性设置。
// ObjectAnimator.java (简化版)
// ObjectAnimator 内部通常会有一个内部的 AnimatorUpdateListener
// 这个监听器会在 ValueAnimator 计算出新值后被回调// 构造函数或 setupProperty 期间
public ObjectAnimator(Object target, String propertyName) {mTarget target;mPropertyName propertyName;// ... 内部会尝试通过反射找到 setter 方法或 Property 对象
}// 假设 ObjectAnimator 内部有一个类似这样的监听器
// 在 ValueAnimator 的 notifyUpdateListeners() 之后被调用
private void applyAnimatedValue() {// 这里的 getAnimatedValue() 是 ValueAnimator 的方法获取当前计算出的动画值Object value getAnimatedValue();// 使用反射或其他方式设置属性值// 以下是伪代码实际实现会更复杂会缓存 Method 对象等try {if (mSetterMethod ! null) { // 如果找到了 setter 方法mSetterMethod.invoke(mTarget, value);} else if (mProperty ! null) { // 如果是 Property 对象mProperty.set(mTarget, value);} else {// Log.e(ObjectAnimator, No setter or Property found for mPropertyName);}} catch (Exception e) {// 处理反射异常}
}7. View.invalidate()被改变属性的View例如setAlpha内部会调用invalidate()方法标记自身为“脏”。
当 ObjectAnimator 调用了View.setX()、View.setAlpha()等方法更新了View的属性后这些setter方法内部通常会调用 invalidate() 或 requestLayout()。
invalidate() 方法并不会立即重绘View。它会标记View为“脏”并发送一个请求到UI线程的ViewRootImpl。
8. ViewRootImpl 的渲染循环在下一个Vsync周期ViewRootImpl的FrameCallback被调用它检测到“脏”View。
9. View 重新绘制ViewRootImpl执行绘制操作调用受影响View的draw()方法。
ViewRootImpl 的渲染循环
ViewRootImpl是连接View层级和窗口管理器Window Manager的关键类。它内部也会注册一个Choreographer.FrameCallback。当Vsync信号到来时ViewRootImpl的FrameCallback会被调用。在这个回调中ViewRootImpl会检查是否有“脏”的View需要重绘。如果有它会执行draw()方法遍历View树调用需要重绘的View的draw()方法。最终绘制指令会被发送到图形系统Skia/OpenGL ES并由GPU进行渲染。
10. 硬件加速渲染绘制指令可能通过更新DisplayList被发送到GPU进行渲染最终显示在屏幕上。
在硬件加速开启的情况下View的draw()方法会将绘制指令记录到DisplayList中。后续的动画更新如果只是改变了View的变换如位置、透明度而不需要重新执行所有复杂的绘制指令GPU可以直接根据更新后的变换矩阵重放DisplayList大大提高了渲染效率。
11. 循环如果动画未结束ValueAnimator会再次注册FrameCallback到Choreographer等待下一个Vsync重复以上步骤。