Android动画与视图绘制流程的关系

Android动画主要分为三种:帧动画、View动画(补间动画)、属性动画。每种动画的实现原理和它们与视图绘制流程(测量、布局和绘制)之间的关系如下:

1. 帧动画(Frame Animation)

帧动画通过顺序播放一组预先定义好的图片实现动画效果,类似于播放视频。

1.1 实现步骤:

  1. res/drawable目录下定义一个XML文件,根节点为<animation-list>,包含多个<item>,每个<item>定义一帧图片及其持续时间。
  2. 使用AnimationDrawable类播放定义好的Drawable中的图片,形成动画效果。
<!-- res/drawable/frame_animation.xml -->
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"android:oneshot="false"><item android:drawable="@drawable/image01" android:duration="500"/><item android:drawable="@drawable/image02" android:duration="500"/><item android:drawable="@drawable/image03" android:duration="500"/>
</animation-list>Button button = findViewById(R.id.bt_001);
button.setBackgroundResource(R.drawable.frame_animation);
AnimationDrawable animationDrawable = (AnimationDrawable) button.getBackground();
animationDrawable.start();

1.2 与视图绘制流程的关系:

  • 测量(measure):帧动画不会影响视图的测量过程。视图的大小在动画开始前已经确定。
  • 布局(layout):帧动画不会影响视图的布局过程。视图的位置在动画开始前已经确定。
  • 绘制(draw):帧动画通过切换不同的Drawable来实现。每一帧都会重新绘制视图,因此会调用invalidate()方法来触发视图重绘。

2 View动画(补间动画)

View动画通过对视图进行平移、缩放、旋转和透明度变化来实现动画效果,但并不真正改变视图的属性,只是改变了视图在屏幕上的显示效果。

2.1 实现步骤

  1. res/anim目录下定义XML文件,描述动画效果。
  2. 使用AnimationUtils.loadAnimation加载动画资源,并通过startAnimation应用到视图。
<!-- res/anim/translate_animation.xml -->
<translate xmlns:android="http://schemas.android.com/apk/res/android"android:fromXDelta="100"android:toXDelta="0"android:duration="1000"/>Animation animation = AnimationUtils.loadAnimation(context, R.anim.translate_animation);
view.startAnimation(animation);

2.2 与视图绘制流程的关系

  • 测量(measure):View动画不会影响视图的测量过程。视图的大小不变。
  • 布局(layout):View动画不会影响视图的布局过程。视图的位置不变。
  • 绘制(draw):View动画通过变换矩阵(Transformation Matrix)在绘制过程中应用动画效果。例如,平移动画在绘制时通过变换矩阵改变视图在屏幕上的位置,但视图的实际坐标没有改变。

3 属性动画(Property Animation)

属性动画通过改变对象的属性值来实现动画效果,能够对任何对象的任何属性进行动画操作,不仅限于视图的属性。

3.1 实现步骤

  1. 创建ValueAnimatorObjectAnimator对象,定义动画属性和持续时间。
  2. 设置动画的监听器,以便在动画过程中更新属性值。
  3. 启动动画。
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f);
animator.setDuration(1000);
animator.start();

3.2 与视图绘制流程的关系:

  • 测量(measure):属性动画可以影响视图的测量过程。如果动画改变了视图的尺寸(如缩放动画),则可能触发重新测量。
  • 布局(layout):属性动画可以影响视图的布局过程。如果动画改变了视图的位置(如移动动画),则可能触发重新布局。
  • 绘制(draw):属性动画直接改变视图的属性(如透明度、位置等),并通过invalidate()方法触发视图重绘,从而在onDraw()方法中反映动画效果。

4 请求重绘视图invalidate()

在 Android 中用于请求重绘视图。调用 invalidate() 后,视图会被标记为“需要重绘”,这会触发视图的绘制流程。以下是 invalidate() 方法的详细解释及其与视图绘制流程的关系。

4.1 invalidate() 方法的工作原理

invalidate() 方法的主要作用是标记视图为需要重绘,并请求系统在下一个渲染周期进行重绘。具体来说,invalidate() 方法的实现步骤如下:

  1. 标记视图为“脏”状态

    • 调用 invalidate() 后,视图会被标记为“脏”状态,表示需要重绘。
  2. 请求父视图重绘

    • invalidate() 会通知视图的父视图,它的某个子视图需要重绘。如果父视图本身也需要重绘,那么父视图也会被标记为“脏”状态。
  3. 将重绘请求发送到消息队列

    • 最终,invalidate() 会将重绘请求发送到主线程的消息队列,确保在下一个渲染周期处理重绘请求。

以下是一个简单的 invalidate() 方法调用示例:

public void setAlpha(float alpha) {if (this.alpha != alpha) {this.alpha = alpha;invalidate(); // 标记视图需要重绘}
}

在这个示例中,当视图的透明度发生变化时,会调用 invalidate() 方法,触发视图重绘。

4.2 视图绘制流程

视图的绘制流程主要包括三个阶段:测量(measure)、布局(layout)和绘制(draw)。invalidate() 方法主要影响绘制阶段,但也可能间接影响测量和布局阶段。具体流程如下:

  1. 测量阶段(measure)

    • measure() 方法确定视图的尺寸。调用 requestLayout() 会触发测量阶段。
    • invalidate() 方法不会直接触发测量阶段,但如果视图的某些属性(如尺寸)改变,可能会间接触发测量阶段。
  2. 布局阶段(layout)

    • layout() 方法确定视图在其父视图中的位置。调用 requestLayout() 会触发布局阶段。
    • invalidate() 方法不会直接触发布局阶段,但如果视图的位置或尺寸改变,可能会间接触发布局阶段。
  3. 绘制阶段(draw)

    • draw() 方法负责将视图绘制到屏幕上。调用 invalidate() 会直接触发绘制阶段。
    • invalidate() 会触发 draw() 方法,从而调用视图的 onDraw() 方法。

4.3 invalidate() 方法与绘制流程的关系

当调用 invalidate() 方法时,系统会在下一个渲染周期内处理重绘请求。具体步骤如下:

  1. 调用 invalidate()

    • 视图被标记为“脏”状态,并请求重绘。
  2. 将重绘请求添加到消息队列

    • invalidate() 方法会将重绘请求添加到主线程的消息队列中,确保在下一个渲染周期处理。
  3. 处理重绘请求

    • 在下一个渲染周期,主线程的消息循环会处理重绘请求,调用 ViewRootImplperformTraversals() 方法。
  4. 执行 performTraversals()

    • performTraversals() 方法会依次执行测量(performMeasure())、布局(performLayout())和绘制(performDraw())操作。
    • 对于仅需要重绘的情况,通常只会执行 performDraw() 阶段。
  5. 调用 draw() 方法

    • performDraw() 方法会调用视图的 draw() 方法。
    • draw() 方法内部会调用 onDraw() 方法,执行实际的绘制操作。

5 主线程(也称为 UI 线程)的消息队列

在 Android 中,主线程(也称为 UI 线程)的消息队列负责处理一系列与用户界面和应用逻辑相关的任务。消息队列由 LooperHandler 机制来管理,通过消息(Message)和可运行的任务(Runnable)来调度和执行任务。

5.1 主线程消息队列中常见的请求类型:

1. UI 更新请求

这些请求主要用于更新用户界面元素,包括重绘视图、调整布局等。

  • 重绘请求:通过调用 invalidate()postInvalidate() 方法触发视图重绘,会将重绘请求添加到消息队列中。
  • 布局请求:调用 requestLayout() 方法触发视图的重新测量和布局。

2. 输入事件

这些事件是由用户的交互引发的,包括触摸事件、键盘事件等。

  • 触摸事件:如点击、滑动等,通过 MotionEvent 处理。
  • 键盘事件:如按键按下和释放,通过 KeyEvent 处理。

3. 定时任务

通过 Handler 发送延迟消息或定时执行的任务。

  • 延迟消息:使用 HandlersendMessageDelayed() 方法发送的消息。
  • 定时任务:使用 postDelayed() 方法调度的 Runnable

4. 动画请求

这些请求用于执行属性动画、视图动画等。

  • 属性动画:如 ObjectAnimatorValueAnimator,会定期更新属性值并重绘视图。
  • 视图动画:如平移、缩放、旋转等补间动画,通过 Animation 类实现。

5. 布局变化

这些请求用于处理视图层次结构的变化。

  • 视图添加/移除:如 addView()removeView() 方法引发的布局更新。
  • 布局参数变化:如 setLayoutParams() 方法引发的重新布局。

6. 系统事件

包括与应用生命周期和系统资源相关的事件。

  • Activity 生命周期事件:如 onCreate()onResume()onPause() 等。
  • 系统广播:如网络状态变化、电量低等广播事件。

7. 后台任务结果

异步任务完成后,将结果发送回主线程更新 UI。

  • AsyncTask:通过 onPostExecute() 将结果传递回主线程。
  • 线程池:通过 Handler 将结果传递回主线程。

5.2 视图绘制流程与消息队列的关系

当我们调用 invalidate()requestLayout() 等方法时,这些请求会被加入到主线程的消息队列中,等待处理。消息队列会按顺序处理这些请求,确保视图在正确的时间被重新测量、布局和绘制。以下是这些方法与消息队列的具体关系:

  1. invalidate()

    • invalidate() 标记视图为需要重绘,并将重绘请求添加到消息队列中。
    • 消息队列在下一个渲染周期处理重绘请求,调用 ViewRootImpl.performTraversals() 执行绘制阶段。
  2. requestLayout()

    • requestLayout() 标记视图为需要重新测量和布局,并将布局请求添加到消息队列中。
    • 消息队列在下一个渲染周期处理布局请求,调用 ViewRootImpl.performTraversals() 执行测量和布局阶段。

6 渲染周期

在 Android 中,“下一个渲染周期”是指系统在处理主线程消息队列中的绘制请求时,下一个执行绘制操作的时间点

6.1 渲染周期的概念

Android 的渲染周期基于帧率(Frame Rate)进行调度。典型的渲染帧率是 60 帧每秒(FPS),也就是说,每帧的时间间隔约为 16.67 毫秒(1000 毫秒 / 60 帧)。在每一帧中,系统会执行一系列操作,包括处理输入事件、更新动画、测量和布局视图、绘制视图等。

6.2 主线程消息循环

主线程(UI 线程)通过消息循环(Looper 和 Handler)来管理和处理各种任务。消息循环会不断从消息队列中取出消息并处理这些消息。

在 Android 的消息队列中,绘制请求(如 invalidate())会被添加到消息队列,并在合适的时间点进行处理。为了确保流畅的 UI 渲染,Android 会尽量在每一帧的时间间隔内完成所有的绘制请求。

6.2 Choreographer

Choreographer 是 Android 系统中的一个关键类,它用于协调 UI 线程的渲染工作。Choreographer 会根据系统的刷新频率来调度回调,确保在正确的时间点进行绘制操作。每当需要进行绘制时,Choreographer 会在下一帧到来之前安排一次绘制回调。

当调用 invalidate() 方法时,系统会将重绘请求添加到消息队列中。然后,Choreographer 会在下一个渲染周期到来时调用 doFrame() 方法,执行绘制操作。以下是一个简化的示例:

// View 的 invalidate 方法
public void invalidate() {if (mParent != null && mAttachInfo != null) {mParent.invalidateChild(this, null);}
}// ViewParent 的 invalidateChild 方法
public void invalidateChild(View child, Rect dirty) {ViewRootImpl viewRoot = getViewRootImpl();if (viewRoot != null) {viewRoot.invalidate();}
}// ViewRootImpl 的 invalidate 方法
public void invalidate() {if (!mWillDrawSoon) {mWillDrawSoon = true;Choreographer.getInstance().postFrameCallback(mTraversalRunnable);}
}// Choreographer 的 postFrameCallback 方法
public void postFrameCallback(FrameCallback callback) {mCallbackQueue.add(callback);scheduleFrameLocked();
}// 调度下一帧
private void scheduleFrameLocked() {if (!mFrameScheduled) {mFrameScheduled = true;// 通过 Handler 安排在下一帧调用 doFrame 方法Message msg = Message.obtain(mHandler, mFrameHandlerCallback);mHandler.sendMessageAtTime(msg, nextFrameTime);}
}

完整的绘制流程

  1. 触发重绘请求:调用 invalidate() 方法,标记视图为需要重绘,并将重绘请求添加到消息队列中。
  2. 调度下一帧Choreographer 调用 postFrameCallback() 安排在下一帧调用 doFrame() 方法。
  3. 处理绘制回调:在下一帧到来时,Choreographer 调用 doFrame() 方法,执行所有的绘制回调。
  4. 执行绘制操作ViewRootImpldoTraversal() 方法依次执行测量(measure)、布局(layout)和绘制(draw)操作,最终调用视图的 onDraw() 方法进行绘制。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/13303.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实锤,阿里云盾会拦截百度云防护的IP!

今天凌晨&#xff0c;一位站长联系上云加速客服&#xff0c;反馈说&#xff0c;网站突然出现了502的情况。 在检查云防护子域名配置没有问题、本地强制回源没有问题的情况下&#xff0c;我们得出结论是要么服务器内防火墙拦截了云防护的IP段&#xff0c;要么服务器商拦截了云防…

分布式计算、并行计算、网格计算、边缘计算

分布式计算 分布式计算是一种计算方法&#xff0c;它将一个大型的计算任务分解成多个子任务&#xff0c;并将这些子任务分布在网络上的多台计算机&#xff08;节点&#xff09;上同时执行。这些节点通过通信网络协同工作&#xff0c;共同完成任务。每个节点可以独立处理自己的…

[muduo网络库]——muduo库EventLoopThread类(剖析muduo网络库核心部分、设计思想)

接着之前我们[muduo网络库]——muduo库Thread类&#xff08;剖析muduo网络库核心部分、设计思想&#xff09;&#xff0c;我们接下来继续看muduo库中的EventLoopThread类&#xff0c;它和Thread类息息相关。 EventLoopThread类 封装了eventloop线程也就是IO线程&#xff0c;e…

如何使用AzurEnum快速枚举Microsoft Entra ID(Azure AD)

AzurEnum是一款针对Azure的安全工具&#xff0c;在该工具的帮助下&#xff0c;广大研究人员可以轻松快速地枚举Microsoft Entra ID&#xff08;Azure AD&#xff09;。 该工具基于纯Python 3开发&#xff0c;可以在Windows和Linux系统上运行&#xff0c;但考虑到性能和稳定性&a…

CSS字体修饰

1&#xff09;文字大小 &#xff08; font-size &#xff09; /* 设置文字大小为24个像素 */ font-size: 24px; 2&#xff09;字体粗细 &#xff08; font-weight &#xff09; /* 字体粗细在100-900之间可以进行调整 */ /* 字体加粗 */ font-weight: bolder; /* 或 fon…

FFmpeg开发笔记(二十八)Linux环境给FFmpeg集成libxvid

XviD是个开源的视频编解码器&#xff0c;它与DivX一同被纳入MPEG-4规范第二部分的视频标准&#xff0c;但DivX并未开源。早期的MP4视频大多采用XviD或者DivX编码&#xff0c;当时的视频格式被称作MPEG-4。现在常见的H.264后来才增补到MPEG-4规范的第十部分&#xff0c;当然如今…

远程桌面如何配置?使用快解析远程访问

远程桌面如何设置&#xff1f; 远程桌面作为windows系统内置的一个组件&#xff0c;多年来深受用户喜爱。使用此功能&#xff0c;我们能够轻而易举的控制我们想要控制的电脑。下面我就简单的介绍一下远程桌面的设置方法。 在讲具体设置方法之前&#xff0c;首先应该给大家普及…

dfs记忆化搜索,动态规划

动态规划概念&#xff1a; 给定一个问题&#xff0c;将其拆成一个个子问题&#xff0c;直到子问题可以直接解决。然后把子问题的答案保存起来&#xff0c;以减少重复计算。再根据子问题的答案反推&#xff0c;得出原问题解。 821 运行时间长的原因&#xff1a; 重复大量计算…

pytorch-11 神经网络的学习

一、梯度下降中的两个关键问题 1 找出梯度向量的方向和大小 2 让坐标点移动起来(进行一次迭代) 二、找出距离和方向:反向传播 1 反向传播的定义与价值 我们是从左向右,从输出向输入,逐渐往前求解导数的表达式,并且我们所使用的节点上的张量,也是从后向前逐渐用到,…

Oracle通过触发器实现自增长字段

在Oracle数据库中&#xff0c;没有像SQL Server中的IDENTITY或MySQL中的AUTO_INCREMENT这样的直接方式来为表中的字段实现自增长功能。但是&#xff0c;你可以使用序列&#xff08;Sequence&#xff09;和触发器&#xff08;Trigger&#xff09;的组合来实现类似的功能。 以下是…

MLM之GPT-4o:GPT-4o(多模态/高智能/2倍速/视觉改进/128K的大窗口)的简介、安装和使用方法、案例应用之详细攻略

MLM之GPT-4o&#xff1a;GPT-4o(多模态/高智能/2倍速/视觉改进/128K的大窗口)的简介、安装和使用方法、案例应用之详细攻略 导读&#xff1a;2024年5月13日&#xff0c;OpenAI重磅发布新旗舰模型GPT-4o&#xff0c;这是一个全新的旗舰模型&#xff0c;可以实时跨越音频、视觉和…

Unity Render入门

概述 在unity中渲染相关的组件是和Render关联的&#xff0c;比如我们常见的3D模型中的MeshRender&#xff0c;UI中的RenderCanvas等都是和Render相关联的&#xff0c;相信在unity的学习过程中&#xff0c;一定看到过非常多和Render相关的内容&#xff0c;那让我们学习一下这部…

JavaEE之线程 (6)—— 等待方法wait 和唤醒方法notify、notifyAll

线程间等待与唤醒机制 由于线程之间是抢占式执行的&#xff0c; 因此线程之间执行的先后顺序难以预知。但是实际开发中有时候我们希望合理的协调多个线程之间的执行先后顺序。比如 球场上的每个运动员都是独立的“执行流” &#xff0c;可以认为是一个 ”线程“。而完成一个具体…

【设计模式】单例模式-学习记录

什么是单例 单例模式是一种创建型设计模式&#xff0c;核心思想为一个类只有一个实例&#xff0c;并提供一个全局访问点来访问这个实例。 只一个实例就是在整个应用程序中&#xff0c;只存在该类的一个实例对象&#xff0c;而不是创建多个相同类型的对象。 全局访问点的意思…

python批量生成防伪识别二维码

欢迎关注我👆,收藏下次不迷路┗|`O′|┛ 嗷~~ 目录 一.前言 二.代码 三.使用 四.总结 一.前言 二维码(QR Code)是一种矩阵条码技术,它使用黑白矩形图案来表示二进制数据,这些矩形图案可以被设备扫描并解读。二维码可以被用来存储

每日一题——PAT(乙级)1002 写出这个数(举一反三、思想解读)

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三题目链接 我的写法&#xff1a; nums_sounds{0:ling,1:yi,2:er,3:san,4:si,5:wu,6:liu,7:qi,8:ba,9:jiu, }sum0inputsinput() input_nums[] for i i…

人工智能-深度学习-PyTorch数据读取实战【含详细源代码+数据集+图示分析】

(以RMB人民币二分类为例) 介绍 在深度学习任务中,数据读取是至关重要的一环。它影响着模型的训练速度和训练效果。本文将以PyTorch框架为例,介绍如何读取RMB人民币二分类数据集,并进行详细的代码解析和图示分析。 原理详解 PyTorch提供了多种数据读取方法,包括: 手动…

绝地求生:29.2商城更新内容预览:挣脱尘网通行证,经典皮肤返场,空投活动

就在今天历经9小时维护&#xff0c;29.2版本终于上线&#xff0c;柠檬茶带大家一起看看&#xff0c;这次游戏里都更新了哪些内容吧。 挣脱尘网通行证 豪华版&#xff1a;$14.99 普通版&#xff1a;$4.99 豪华版比普通版多10级升级券和2套生存者宝箱 分支一 分支二 分支三 额外…

现代加密技术(对称和非对称加密)

1.分类 现代加密技术&#xff1a;对称和非对称加密&#xff0c;对称加密即共享密钥&#xff0c;非对称加密是公钥加密算法。 2.基础总结 AES是什么算法&#xff1f; 分组加密算法&#xff0c;对称加密算法AES的分组长度是&#xff1f;固定128位AES密钥长度是多少&#xff1f;支…

树莓派3B+入门(无外设)

昨日刚到一块树莓派3B&#xff0c;甚是喜爱&#xff0c;然半宿未眠 1、下载 在官网先下载烧录文件https://www.raspberrypi.com/software/ 下载完毕打开&#xff0c;选择&#xff0c;根据自己板子型号定 操作系统用最新的就行&#xff0c;64位不太稳定 储存卡&#xff0c;需…