Android中的view绘制流程,简单理解
简单理解
Android中的View类代表用户界面中基本的构建块。一个View在屏幕中占据一个矩形区域、并且负责绘制和事件处理。View是所有widgets的基础类,widgets是我们通常用于创建和用户交互的组件,比如按钮、文本输入框等等。子类ViewGroup是所有布局(layout)的基础类。layout是一个不看见的容器,里面堆放着其他的view或者ViewGroup,并且设置他们的布局属性。
所有的view在窗口中是以树状结构来管理。你可以通过代码或者编辑xml布局文件来添加一个view。view有很多的子类来负责控制或者有能力展示图片,文字等。
Android绘制View
当一个Activity启动时,会被要求绘制出它的布局。Android框架会处理这个请求,当然前提是Activity提供了合理的布局。绘制从根视图开始,从上至下遍历整棵视图树,每一个ViewGroup负责让自己的子View被绘制,每一个View负责绘制自己,通过draw()方法.绘制过程分三步走。
- Measure
- Layout
- Draw
整个绘制流程是在ViewRoot中的performTraversals()方法展开的。部分源代码如下。
private void performTraversals() {......//最外层的根视图的widthMeasureSpec和heightMeasureSpec由来//lp.width和lp.height在创建ViewGroup实例时等于MATCH_PARENTint childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);......mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);......mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());......mView.draw(canvas);......
}
在绘制之前当然要知道view的尺寸和绘制。所以先进行measu和layout(测量和定位),如下图。

Measure过程
public final void measure(int widthMeasureSpec, int heightMeasureSpec) { //....
//回调onMeasure()方法 onMeasure(widthMeasureSpec, heightMeasureSpec);
//more
}
计算view的实际大小,获得高宽存入mMeasuredHeight和mMeasureWidth,measure(int, int)传入的两个参数。MeasureSpec是一个32位int值,高2位为测量的模式,低30位为测量的大小。测量的模式可以分为以下三种。
- EXACTLY
精确值模式,当layout_width或layout_height指定为具体数值,或者为match_parent时,系统使用EXACTLY。
- AT_MOST
最大值模式,指定为wrap_content时,控件的尺寸不能超过父控件允许的最大尺寸。
- UNSPECIFIED
不指定测量模式,View想多大就多大,一般不太使用。
根据上面的源码可知,measure方法不可被重写,自定义时需要重写的是onMeasure方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));}
查看源码可知最终的高宽是调用setMeasuredDimension()设定的,如果不重写,默认是直接调用getDefaultSize获取尺寸的。
使用View的getMeasuredWidth()和getMeasuredHeight()方法来获取View测量的宽高,必须保证这两个方法在onMeasure流程之后被调用才能返回有效值。
Layout过程
Layout方法就是用来确定view布局的位置,就好像你知道了一件东西的大小以后,总要知道位置才能画上去。
mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
layout获取四个参数,左,上,右,下坐标,相对于父视图而言。这里可以看到,使用了刚刚测量的宽和高。
public void layout(int l, int t, int r, int b) {int oldL = mLeft;int oldT = mTop;int oldB = mBottom;int oldR = mRight;boolean changed = setFrame(l, t, r, b);if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {.....onLayout(changed, l, t, r, b);.....
}
通过setFrame设置坐标。如果坐标改变过了,则重新进行定位。如果是View对象,那么onLayout是个空方法。因为定位是由ViewGroup确定的。
当layout结束以后getWidth()与getHeight()才会返回正确的值。
这里出现一个问题,getWidth/Height() 和 getMeasuredWidth/Height()有什么区别?
- getWidth():View在设定好布局后整个View的宽度。
- getMeasuredWidth():对View上的內容进行测量后得到的View內容占据的宽度

Draw过程
public void draw(Canvas canvas) {....../** Draw traversal performs several drawing steps which must be executed* in the appropriate order:** 1. Draw the background* 2. If necessary, save the canvas' layers to prepare for fading* 3. Draw view's content* 4. Draw children* 5. If necessary, draw the fading edges and restore layers* 6. Draw decorations (scrollbars for instance)*/
// Step 1, draw the background, if needed......if (!dirtyOpaque) {drawBackground(canvas);}
// skip step 2 & 5 if possible (common case)......
// Step 2, save the canvas' layers......if (drawTop) {canvas.saveLayer(left, top, right, top + length, null, flags);}......
// Step 3, draw the contentif (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the childrendispatchDraw(canvas);
// Step 5, draw the fade effect and restore layers......if (drawTop) {matrix.setScale(1, fadeHeight * topFadeStrength);matrix.postTranslate(left, top);fade.setLocalMatrix(matrix);p.setShader(fade);canvas.drawRect(left, top, right, top + length, p);}......
// Step 6, draw decorations (scrollbars)onDrawScrollBars(canvas);......}
重点是第三步调用onDraw方法。其它几步都是绘制一些边边角角的东西比如背景、scrollBar之类的。其中dispatchDraw,是用来递归调用子View,如果没有则不需要。本文主要解析了Android view的绘制,更深入的学习或者Android开发进阶,可以前往《Android核心架构笔记》查看详细的学习类目。

总结
- View是Android中可视化UI组件的实体。
- View的呈现依赖于Activity,是Activity所容纳的基本元素。
- View主要提供了组件绘制和事件处理的方法。
- View可以分为容器类型和实体类型。
- 容器类型的View(ViewGroup)可容纳其它的容器类型View和实体类型View。
- 实体类型的View主要用于用户交互,如:按钮,文本框。
相关文章:
Android中的view绘制流程,简单理解
简单理解 Android中的View类代表用户界面中基本的构建块。一个View在屏幕中占据一个矩形区域、并且负责绘制和事件处理。View是所有widgets的基础类,widgets是我们通常用于创建和用户交互的组件,比如按钮、文本输入框等等。子类ViewGroup是所有布局&…...
商城开发:店铺管理系统应具备哪些功能?
电子商务的迅猛发展,越来越多的企业选择在线商城作为业务拓展的重要渠道。而要实现一个成功的在线商城,一个强大而高效的店铺管理系统是不可或缺的。店铺管理系统作为商城的核心管理工具,应具备一系列功能,以提供卓越的用户体验和…...
小白学go基础04-命名惯例对标识符进行命名
计算机科学中只有两件难事:缓存失效和命名。 命名是编程语言的要求,但是好的命名却是为了提高程序的可读性和可维护性。好的命名是什么样子的呢?Go语言的贡献者和布道师Dave Cheney给出了一个说法:“一个好笑话,如果你…...
使用iCloud和Shortcuts实现跨设备同步与自动化数据采集
在如今的数字时代,跨设备同步和自动化数据采集对于提高工作效率和便利性至关重要。苹果的iCloud和Shortcuts App为我们提供了强大的工具,可以实现跨设备同步和自动化数据采集的功能。本文将详细介绍如何利用iCloud和Shortcuts App实现这些功能࿰…...
Spring框架-基于STOMP使用Websocket
文章目录 前言一、范例演示1.注解方式2.XML方式二、可能出现错误错误: WebSocket代理中断错误: 缺少EventExecutor类错误: 缺少Publisher类错误: 缺少Scheduler类错误: WebSocket调用失败总结前言 Spring框架提供了多种WebSock消息机制,不仅包含了模拟SockJS,还提供了基…...
kafka-- 安装kafka manager及简单使用
一 、安装kafka manager 管控台: # 安装kafka manager 管控台: ## 上传 cd /usr/local/software ## 解压 unzip kafka-manager-2.0.0.2.zip -d /usr/local/ cd /usr/local/kafka-manager-2.0.0.2/conf vim /usr/local/kafka-manager-2.0.0.2/conf/appl…...
深圳-海岸城购物中心数据分析
做数据分析的时候,如果要对商场进行分析,可以从这些数据纬度进行分析,如下图所示: 截图来源于数位观察:https://www.swguancha.com/...
vue3 + elementplus Cannot read properties of null (reading ‘isCE‘)
使用命令行直接下载的element-plus,使用时会报错。 卸载掉,然后在项目根目录下,使用vue ui安装依赖, 即可使用...
易云维®医院后勤管理系统软件利用物联网智能网关帮助实现医院设备实现智能化、信息化管理
近年来,我国医院逐渐意识到医院设备信息化管理的重要性,逐步建立医院后勤管理系统软件,以提高信息化管理水平。该系统是利用数据库技术,为医院的中央空调、洁净空调、电梯、锅炉、医疗设备等建立电子档案,把设备监控、…...
c# 定期重启程序操作
1 先说说重启//这部分是转载的 一、Restart方法 System.Windows.Forms.Application.Restart();经测试发现有时候只会关闭程序,并不会重新启动 二、Process.Start()和Exit() System.Diagnostics.Process.Start(System.Reflection.Assembly.GetExecutingAssembly()…...
ps beta 2.5的妙用
1、https://pan.baidu.com/s/1CCw6RGlzEJ7TPWou8pPADQ?pwd2023 2、下载新便携版。 3、解压到c:\myapp文件夹下。 4、运行。 5、登录us账号。 6、使用智能移除。 效果如下: 使用滤镜。 先将C:\myApp\(新便携版)Adobe Photoshop (25.0.0 m22…...
IDEA无效发行版本17
IDEA无效发行版本17 idea开发工具依赖的 jdk版本 和 项目依赖的jdk版本一定要保持 一致,不然会报错。 setting-->build-->compiler-》javaCompiler project->structure 这个也要保持一样。 在porm.xml文件中,你配置jdk版本是1.8,这…...
Ubuntu22.04安装ROS
Ubuntu22.04安装ROS_笔记大全_设计学院 Excerpt 在安装ROS之前,需要先安装Ubuntu22.04操作系统。您可以从Ubuntu官网下载Ubuntu22.04的最新版本镜像文件,并创建一个可启动的USB。您可以参考以下步骤: 一、安装Ubuntu22.04操作系统 在安装ROS…...
Linux 学习笔记(2)—— 关于文件和目录
目录 1、切换目录 2、查看系统信息 3、文本的创建和编辑 3-1)创建文件 3-2)查看文件 3-3)输出重定向和追加重定向 3-4)使用 vi 编辑器编辑文件 4、文件和文件夹的处理 4-1)对文件的处理 4-2)查看…...
[重要] 如何在桌面上生成一个指定网址的快捷方式
方法一: 要在桌面上生成一个指定网址的快捷方式,可以按照以下步骤进行操作: 首先,打开你的浏览器,并确保已经访问了你想要创建快捷方式的网址。 在浏览器中,将鼠标悬停在地址栏上的网址上,然后…...
PyQt和Qt的其他绑定(如PySide)相比有什么优势和劣势?
作为一个新手,你可能会对PyQt和Qt的其他绑定(如PySide)之间的优势和劣势感到困惑。没问题,这很正常。我们先来谈谈优势吧。 首先,PyQt是由C编写的,因此它具有强大的跨平台支持。这意味着无论你使用的是Win…...
4K三路虚拟情景实训教学系统VR4300:实现“微课录制+课堂实训”双教学需求
如今,ChatGPT横空出世,产生了极大的破圈效应。各种AI、大模型概念风起云涌,给千行百业带来了极大的机遇与挑战。 4K三路虚拟情景实训教学系统VR4300基于计算机技术,虚拟现实技术,抠像合成技术,AI大模型等优…...
python逆向还原dnspy反编译的C#算法
dnspy反编译中的代码如下: private void method_1(byte[] byte_2, byte[] byte_3, byte[] byte_4) {...
数学建模--最短路径算法的Python实现
目录 1.算法流程简介 2.算法核心代码 3.算法效果展示 1.算法流程简介 #最短路径算法 #针对有向图的最短路径问题,我们有很多的算法能解决. """ 目前主流算法如下所示: Dijkstra算法:Dijkstra算法是一种单源最短路径算法,用于计算从起点到其它所有节点的最短…...
webpack学习(一)基本配置
webpack学习(一)基本配置 目录 webpack学习(一)基本配置 webpack简介 五大核心配置 1.入口(entry) 2.输入(output) 3.加载器(loader) 4.插件(plugins…...
接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...
