Android View 的绘制流程


view作为构成android界面的基本元素,深入了解view的绘制流程对开发人员来说是很有必要的。

我们创建一个Activity都会在onCreate方法中写setContentView(layoutResId),把我们的布局传进去,那此时我们的布局就添加到屏幕上了吗?当然不是的。那我们的这个布局是在什么时候添加进屏幕的呢?

在ActivityThread.java 的handleResumeActivity中

//ActivityThread.java
@Override
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,boolean
isForward, String reason) {
//...
wm.addView(decor, l);
//...
}

这里调用wm的addView,才把DecorView添加到窗口,wm其实就是WindowManagerImpl,接着我们跟进到WindowManagerImpl的addView

//WindowManagerImpl.java
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {applyTokens(params);mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,mContext.getUserId());
}

这里调用的是mGlobal的addView方法,mGlobal其实就是WindowManagerGlobal,是管理整个进程所有窗口信息的,接下来看看WindowManagerGlobal的addView方法

public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow, int userId) {//...final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;ViewRootImpl root;if (windowlessSession == null) {root = new ViewRootImpl(view.getContext(), display);} else {root = new ViewRootImpl(view.getContext(), display,windowlessSession);}
//为DecorView设置LayoutParamsview.setLayoutParams(wparams);mViews.add(view);mRoots.add(root);mParams.add(wparams);root.setView(view, wparams, panelParentView, userId);//...  
}

接下来才到关键的地方了,ViewRootImpl的setView方法

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,int userId) {//...//遍历view树逻辑requestLayout();//...//将窗口添加到WMS上面res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,getHostVisibility(),mDisplay.getDisplayId(), userId,mInsetsController.getRequestedVisibilities(), inputChannel,mTempInsets,mTempControls);//...//将ViewRootImpl设置为DecorView的parentview.assignParent(this);
}

在这里的requestLayout,最终会走到performTraversals方法,这里才真正开始遍历view的measure、layout、draw等流程

private void performTraversals() {//...//预测量,最多会执行3次onMeasure// Ask host how big it wants to bewindowSizeMayChange |= measureHierarchy(host, lp, mView.getContext().getResources(),desiredWindowWidth, desiredWindowHeight);//...// Ask host how big it wants to beperformMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//...performLayout(lp, mWidth, mHeight);//...if (!performDraw() && mSyncBufferCallback != null) {mSyncBufferCallback.onBufferReady(null);}//...
}

预测量

private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {//...if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {final DisplayMetrics packageMetrics = res.getDisplayMetrics();//默认320dpres.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);if (baseSize != 0 && desiredWindowWidth > baseSize) {childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width,lp.privateFlags);childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height,lp.privateFlags);//第一次预测量performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);if((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0 {goodMeasure = true;} else {//宽度不够则再将剩余空间一般给child继续测量baseSize = (baseSize + desiredWindowWidth) / 2;childWidthMeasureSpec = getRootMeasureSpec(baseSize,lp.width,lp.privateFlags);performMeasure(childWdithMeasureSpec,childHeightMeasureSpec);if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {if (DEBUG_DIALOG) Log.v(mTag, "Good!");goodMeasure = true;}}}}//如果宽度还不满足,则将全部宽度给child去测量if (!goodMeasure) {childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width,lp.privateFlags);childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height,lp.privateFlags);performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()){windowSizeMayChange = true;}}
}

测量

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {if (mView == null) {return;}Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");try {mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}
}
//View.java
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {//...//最终执行到对应view的onMeasure中onMeasure(widthMeasureSpec, heightMeasureSpec);//...
}

布局

//ViewRootImpl.java
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,int desiredWindowHeight) {//...host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());//...
}//View.java
public void layout(int l, int t, int r, int b) {//...//在这里给左上右下去赋值,之后的getWidth和getHeight才能获取到值boolean changed = isLayoutModeOptical(mParent) ?setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);//...//这里调用到对应view的onLayout方法onLayout(changed, l, t, r, b);//...
}

绘制

//ViewRootImpl.java
private boolean performDraw() {//...boolean canUseAsync = draw(fullRedrawNeeded, usingAsyncReport && mSyncBuffer);//...
}private boolean draw(boolean fullRedrawNeeded, boolean forceDraw) {//...if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,scalingRequired, dirty, surfaceInsets)) {return false;}
}private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,boolean scalingRequired, Rect dirty, Rect surfaceInsets) {//...//执行到对应view的draw方法mView.draw(canvas);//...
}

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【Git必看系列】—— 2024年前后端开发必须要知道的GitFlow工作流

自从 Linux 之父Linus Torvalds对当时的版本控制工具感到不满,亲自动手创造了 Git 以来,Git 已经逐渐在版本控制领域占据了主导地位。不论你的代码仓库托管在 GitHub 还是 GitLab,不论你用的是 SourceTree、GitKraken 这样的图形界面&#xf…

华为 HCIP-Datacom H12-821 题库 (25)

🐣博客最下方微信公众号回复题库,领取题库和教学资源 🐤诚挚欢迎IT交流有兴趣的公众号回复交流群 🦘公众号会持续更新网络小知识😼 1.以下哪些事件会导致 IS-IS 产生一个新的 LSP? A、邻接 Up 或Down B、引入的 IP 路由发送变…

吴恩达深度学习笔记:卷积神经网络(Foundations of Convolutional Neural Networks)2.1-2.2

目录 第四门课 卷积神经网络(Convolutional Neural Networks)第二周 深度卷积网络:实例探究(Deep convolutional models: case studies)2.1 为什么要进行实例探究?(Why look at case studies?&…

PyTorch深度学习快速入门教程【土堆】基础知识篇

Juptyer 版本: Python 3.9.19Pytorch 2.4.1 (pytorch0) C:\Users\25694>conda install nb_conda_kernels(pytorch0) C:\Users\25694>jupyter notebook使用conda环境的pytorch: 成功解决python.exe无法找到程序入口 无法定位程序输入点 shifte…

【Python语言初识(一)】

一、python简史 1.1、python的历史 1989年圣诞节:Guido von Rossum开始写Python语言的编译器。1991年2月:第一个Python编译器(同时也是解释器)诞生,它是用C语言实现的(后面),可以调…

茶思屋直播|TinyEngine+AI:聚焦主航道,在实践中探索低代码技术黑土地

低代码引擎使能开发者定制低代码平台。它是低代码平台的底座,提供可视化搭建页面等基础能力,既可以通过线上搭配组合,也可以通过cli创建个人工程进行二次开发,实时定制出自己的低代码平台。适用于多场景的低代码平台开发&#xff…

【C++ 学习】多态的基础和原理(10)

目录 前言1. 概念2. 多态的定义及实现2.1 多态的构成条件2.2 虚函数2.3 虚函数重写2.4 虚函数重写的例外2.4.1 协变2.4.1 析构函数的重写 2.5 多态调用和普通调用2.6 函数重写/函数隐藏/函数重载 的对比2.6.1 函数重写2.6.2 函数隐藏2.6.3 函数重载 2.7 C11 final 和override 3…

领域驱动DDD三种架构-分层架构、洋葱架构、六边形架构

博主介绍: 大家好,我是想成为Super的Yuperman,互联网宇宙厂经验,17年医疗健康行业的码拉松奔跑者,曾担任技术专家、架构师、研发总监负责和主导多个应用架构。 技术范围: 目前专注java体系,以及…

作为HR,如何考察候选人的沟通能力

如何考察候选人的沟通能力。沟通能力,这个听起来简单,实际上却是一个非常复杂的技能,它关乎到一个人能否有效地传递信息,理解他人,并且在团队中发挥积极的作用。 作为HR,我们应该怎样才能精准地把握住候选…

【Python语言初识(二)】

一、分支结构 1.1、if语句 在Python中,要构造分支结构可以使用if、elif和else关键字。所谓关键字就是有特殊含义的单词,像if和else就是专门用于构造分支结构的关键字,很显然你不能够使用它作为变量名(事实上,用作其他…

网络:UDP协议

个人主页 : 个人主页 个人专栏 : 《数据结构》 《C语言》《C》《Linux》 文章目录 前言UDP协议报头和有效载荷分离的问题有效载荷向上交付的问题,也就是交给哪个进程?怎么确定把报文收全了?UDP报头是如何封装的呢&…

mybatisplus的多记录操作 批量删除和批量查询

1.批量查询 通过in查询 Testpublic void testBatchSelectByIds(){List<Integer> ids Arrays.asList(5,7);List<User> users userMapper.selectBatchIds(ids);log.info(users);} 2.批量删除 Testpublic void testBatchDelete(){List<Integer> ids Arrays…

VM VirtualBox虚拟机装Win11提示这台电脑不符合要求,怎么解决?

环境&#xff1a; VM VirtualBox WIN11 问题描述&#xff1a; VM VirtualBox虚拟机装WIN11提示这台电脑不符合要求 解决方案&#xff1a; 1.进入这个页面 2. 跳过win11安全验证 在键盘上按 Shift F10或者FnShift F10调出命令命令提示符&#xff0c; 键入 Regedit 并…

大数据处理从零开始————3.Hadoop伪分布式和分布式搭建

1.伪分布式搭建&#xff08;不会用&#xff0c;了解就好不需要搭建&#xff09; 这里接上一节。 1.1 伪分布式集群概述 伪分布式集群就是只有⼀个服务器节点的分布式集群。在这种模式中&#xff0c;我们也是只需要⼀台机器。 但与本地模式不同&#xff0c;伪分布式采⽤了分布式…

使用Locust进行接口性能测试:关联与参数化技巧分析

Locust是一款开源的Python性能测试工具&#xff0c;它可以模拟大量并发用户对网站或者其他接口进行压力测试 1. 关联&#xff1a;精确模拟用户操作 在某些场景下&#xff0c;我们需要将之前请求的响应参数关联到后续请求中&#xff0c;以模拟用户操作。这在会话管理&#xff…

数字工厂管理系统与MES系统在实际应用中有哪些区别

随着制造业的数字化转型步伐加快&#xff0c;数字工厂管理系统与制造执行MES系统作为两大关键工具&#xff0c;在实际应用中展现出了明显的差异。本文将从实际应用的角度&#xff0c;详细探讨这两种系统之间的主要区别。 数字工厂管理系统的实际应用 数字工厂管理系统侧重于对…

python检测keycloak证书连接报错问题

最近一直被keycloak的证书问题困扰了很久&#xff0c;老是提示ssl安全连接&#xff0c;由于不会java,只好硬着头皮用python测试。 我这里的证书是自己签注的证书&#xff0c;导入系统的是CA根证书。 from keycloak import KeycloakOpenID# 1. 配置 Keycloak 客户端 keycloak_o…

STM32F407ZGT6驱动sd卡+文件夹 并写入多组实时数据 基于cubemx生成

SD卡介绍 SD卡&#xff0c;全称Secure Digital卡&#xff0c;是一种常见的便携式存储设备&#xff0c;通常用于存储和传输数据。它是一种闪存存储卡。 TF卡&#xff08;TransFlash卡&#xff09; 又称MicroSD卡&#xff0c;MicroSD卡比SD卡小&#xff0c;通常只有SD卡的1/4大小…

LLM(大语言模型)和AIGC入门学习路线图,零基础入门到精通,收藏这一篇就够了

大模型是指网络规模庞大的深度学习模型&#xff0c;其参数量通常在千亿级别。 学习大模型需要具备计算机基础&#xff0c;这一点非常重要&#xff01; 要系统地入门大模型&#xff0c;首先需要学习深度学习的基础知识&#xff0c;包括神经网络&#xff08;NN&#xff09;、卷…

nvm node管理工具常用指令

注&#xff1a;使用nvm之前需要卸载掉原有的node 1.在终端输入 nvm list available&#xff0c; 查看网络可以安装的版本。 点击visit后面的链接可查看完整版本列表 版本区别&#xff1a; CURRENT&#xff1a;当前正在使用的 Node.js 版本。LTS&#xff08;Long Term Suppor…