当前位置: 首页 > news >正文

【Android】图解View的工作流程原理

文章目录

  • 入口
    • DecorView如何加载到Window中
    • MeasureSpec
  • Measure
    • View的测量
    • ViewGroup的测量
  • Layout
    • View的`layout()`
  • Draw
    • 1、绘制背景
    • 3、绘制View内容
    • 4、绘制子View
    • 6、绘制装饰

在这里插入图片描述

入口

DecorView如何加载到Window中

在这里插入图片描述


MeasureSpec

该类是View的内部类,封装View的规格尺寸。
在这里插入图片描述
他就是一个32位的int值,高2为代表 specMode(测量模式),低30位代表specSize(测量大小)
specMode:UNSPECIFIED AT_MOST EXACTLY

对于每个View都有对应的MeasureSpec,在测量流程中,通过makeMeasureSpec() 来保存宽和高,通过
getMode()getSize() 得到模式和宽高
MeasureSpec自身的布局参数和父容器的测量规格共同影响

那么顶层View的DecorView没有父容器,怎么得到测量规格呢?
通过getRootMeasureSpec()

/*** 根据窗口大小和根视图尺寸,获取根视图的MeasureSpec** @param windowSize    窗口大小* @param rootDimension 根视图尺寸* @return 根视图的MeasureSpec*/
private static int getRootMeasureSpec(int windowSize, int rootDimension) {int measureSpec;switch (rootDimension) {case ViewGroup.LayoutParams.MATCH_PARENT:// 如果根视图尺寸为MATCH_PARENT(即填充父窗口),窗口无法调整大小。// 强制根视图尺寸为窗口大小,使用MeasureSpec.EXACTLY模式。measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);break;case ViewGroup.LayoutParams.WRAP_CONTENT:// 如果根视图尺寸为WRAP_CONTENT(即自适应内容),窗口可以调整大小。// 设置根视图最大尺寸为窗口大小,使用MeasureSpec.AT_MOST模式。measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);break;default:// 如果根视图尺寸为具体的数值,窗口希望有确定的大小。// 强制根视图尺寸为指定的大小,使用MeasureSpec.EXACTLY模式。measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);break;}return measureSpec;
}

Measure

在某些极端情况下,系统可能需要多次measure才能确定最终的测量宽/高

在这里插入图片描述


View的测量

在这里插入图片描述

ViewGroup的测量

ViewGroup没有onMeasure(),用measureChildren()去递归调用子元素的测量方法measureChild()
在这里插入图片描述


Layout

View的layout()

在这里插入图片描述

在这里插入图片描述


Draw

在这里插入图片描述

官方文档阐述为:

  1. 如果需要,则绘制背景
  2. 保存当前canvas层(可以不执行)
  3. 绘制View的内容
  4. 绘制子View
  5. 如果需要,则绘制View的褪色边缘,类似于阴影效果(可以不执行)
  6. 绘制装饰,例如滚动条
  7. 如果有必要,绘制默认的焦点高亮显示(可以不执行)

1、绘制背景

调用View的drawBackground()来执行

/*** Draws the background onto the specified canvas.** @param canvas Canvas on which to draw the background*/
@UnsupportedAppUsage
private void drawBackground(Canvas canvas) {final Drawable background = mBackground; // 获取背景Drawable对象if (background == null) { // 如果背景Drawable为nullreturn; // 直接返回,不进行绘制}setBackgroundBounds(); // 设置背景Drawable的边界矩形...final int scrollX = mScrollX; // 获取View的当前水平滚动偏移量final int scrollY = mScrollY; // 获取View的当前垂直滚动偏移量if ((scrollX | scrollY) == 0) { // 如果水平和垂直滚动偏移量都为0background.draw(canvas); // 直接绘制背景Drawable在画布上} else { // 如果有滚动偏移量canvas.translate(scrollX, scrollY); // 将画布平移至滚动偏移量的位置background.draw(canvas); // 绘制背景Drawable在平移后的画布上canvas.translate(-scrollX, -scrollY); // 恢复画布的原始位置}
}

3、绘制View内容

onDraw() 需要去自己进行重写实现

4、绘制子View

dispathchDraw() 需要去自己进行重写实现

ViewGroup进行了重写:

@Override
protected void dispatchDraw(Canvas canvas) {...for (int i = 0; i < childrenCount; i++) { // 遍历子Viewwhile (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) { // 如果当前索引为临时索引final View transientChild = mTransientViews.get(transientIndex);if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE || transientChild.getAnimation() != null) { // 如果临时子View可见或者临时子View有动画more |= drawChild(canvas, transientChild, drawingTime); // 在画布上绘制临时子View,并返回是否还有更多绘制}transientIndex++; // 增加临时索引if (transientIndex >= transientCount) { // 如果临时索引超过了临时子View的数量transientIndex = -1; // 重置临时索引}}final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); // 获取并验证预排序的子View索引final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); // 根据索引找到对应Viewif ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { // 如果子View可见或者有动画more |= drawChild(canvas, child, drawingTime); // 在画布上绘制子View,并返回是否还有更多绘制}}...
}

最后调用了drawChild()方法,而该方法其实返回的是child的draw()方法,即View的draw():

/*** This method is called by ViewGroup.drawChild() to have each child view draw itself.** This is where the View specializes rendering behavior based on layer type,* and hardware acceleration.*/
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {...if (!drawingWithDrawingCache) { // 1. 没有使用绘制缓存if (drawingWithRenderNode) { // 使用RenderNode进行绘制mPrivateFlags &= ~PFLAG_DIRTY_MASK;((RecordingCanvas) canvas).drawRenderNode(renderNode);} else {// 对于没有背景的布局,快速路径if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { // 子View标记为不需要被绘制mPrivateFlags &= ~PFLAG_DIRTY_MASK;dispatchDraw(canvas); // 调用dispatchDraw()方法进行绘制} else {draw(canvas); // 调用draw()方法进行绘制}}} else if (cache != null) { // 2. 存在绘制缓存mPrivateFlags &= ~PFLAG_DIRTY_MASK;if (layerType == LAYER_TYPE_NONE || mLayerPaint == null) {// 没有图层画笔,使用临时画笔绘制位图...} else {// 使用图层画笔绘制位图,合并两个透明度,并恢复...}}...return more; // 返回是否还有更多需要绘制的内容
}

在这里插入图片描述

6、绘制装饰

View的DrawForeground()

/*** 绘制视图的前景内容。** <p>前景内容可以包括滚动条、前景绘制或其他视图特定的装饰。前景绘制在主视图内容之上。</p>** @param canvas 用于绘制的画布*/
public void onDrawForeground(Canvas canvas) {onDrawScrollIndicators(canvas); // 调用绘制滚动指示器的方法onDrawScrollBars(canvas); // 调用绘制滚动条的方法final Drawable foreground = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;if (foreground != null) {// 如果存在前景就绘制...foreground.draw(canvas);}
}

相关文章:

【Android】图解View的工作流程原理

文章目录 入口DecorView如何加载到Window中MeasureSpec MeasureView的测量ViewGroup的测量 LayoutView的layout() Draw1、绘制背景3、绘制View内容4、绘制子View6、绘制装饰 入口 DecorView如何加载到Window中 MeasureSpec 该类是View的内部类&#xff0c;封装View的规格尺寸…...

记工时流程

记工时流程 加入团体 加入观古鉴古服务队 登录成功后&#xff0c;点击我的-我的成员 添加成员 进入小程序 扫描后登录&#xff0c;我的-我的团体&#xff0c;可以看到观古鉴古服务队&#xff0c; 进入后点项目 选择观古鉴古文化志愿者招募 -> 我要报名 -> 选择文化志…...

Ubuntu20.04使用Neo4j导入CSV数据可视化知识图谱

1.安装JDK&#xff08; Ubuntu20.04 JDK11&#xff09; sudo apt-get install openjdk-11-jdk -y java -version which java ls -l /usr/bin/java ls -l /etc/alternatives/java ls -l /usr/lib/jvm/java-11-openjdk-amd64/bin/java确认安装路径为/usr/lib/jvm/java-11-openjd…...

vue-cli打包 nodejs内存溢出 vue2.x Last few GCs

遇到这种情况百度各种博客&#xff0c;什么改package.json里的配置&#xff0c;什么安装increase-memory-limit &#xff0c;都尝试了并没什么用处&#xff0c;最后解决方案为执行下方名单&#xff0c;再次打包就成功了&#xff1a; export NODE_OPTIONS--max_old_space_size4…...

SpringBoot整合Flowable/Activiti

SpringBoot版本: 2.0.1.RELEASE Flowable版本: 6.3.1 Activiti版本: 6.0.0 一.添加pom依赖 因为之前我整合的时候有报错关于sqlsession的错误,后面查询文章才发现flowable要排除掉mybatis,又没说具体排除哪一个,所以我这干脆全部排除了 <!-- Flowable dependencies -->…...

基础总结篇:Activity生命周期

private int param 1; //Activity创建时被调用 Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.i(TAG, “onCreate called.”); setContentView(R.layout.lifecycle); Button btn (Button) findViewById(R.id.…...

【鸿蒙 HarmonyOS】@ohos.promptAction (弹窗)

一、背景 创建并显示文本提示框、对话框和操作菜单。 文档地址&#x1f449;&#xff1a;文档中心 说明 本模块首批接口从API version 9开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 该模块不支持在UIAbility的文件声明处使用&#xff0c;即…...

ElasticSearch的常用数据类型

常见的数据类型 Text类型&#xff08;文本数据类型&#xff09; 用于全文检索的字段&#xff0c;例如电子邮件的正文或产品的描述。这些字段是analyzed&#xff0c;也就是说&#xff0c;它们通过分析器传递&#xff0c;以便 在被索引之前将字符串转换为单个术语的列表。通过分…...

C/C++预处理过程

目录 前言&#xff1a; 1. 预定义符号 2. #define定义常量 3. #define定义宏 4. 带有副作用的宏参数 5. 宏替换的规则 6. 宏和函数的对比 7. #和## 8. 命名约定 9. #undef 10. 命令行定义 11. 条件编译 12. 头文件的包含 13. 其他预处理指令 总结&#x…...

客服电话系统:专业、便捷的服务沟通桥梁

一、引言 1.客服电话系统在现代服务中的重要性 在信息化时代&#xff0c;服务行业的竞争日益激烈&#xff0c;提供高效、便捷的服务成为企业赢得市场、获取用户信任的关键。客服电话系统作为企业与用户之间的重要沟通桥梁&#xff0c;不仅承载着解答疑问、处理问题的职责&…...

IP地址与子网掩码

1 IP地址 1.1 IPv4与IPv6 1.2 IPv4地址详解 IPv4地址分4段&#xff0c;每段8位&#xff0c;共32位二进制数组成。 1.2.1 地址分类 这32位又被分为网络号和主机号两部分&#xff0c;根据网络号占用位数的不同&#xff0c;又可分为以下几类&#xff1a; A类地址&#xff1a;…...

Python爬取公众号封面图(零基础也能看懂)

&#x1f4da;博客主页&#xff1a;knighthood2001 ✨公众号&#xff1a;认知up吧 &#xff08;目前正在带领大家一起提升认知&#xff0c;感兴趣可以来围观一下&#xff09; &#x1f383;知识星球&#xff1a;【认知up吧|成长|副业】介绍 ❤️感谢大家点赞&#x1f44d;&…...

2024.4.6学习笔记

今日学习韩顺平java0200_韩顺平Java_对象机制练习_哔哩哔哩_bilibili 今日学习p315-p328 动态绑定机制 当调用方法对象的时候&#xff0c;该方法会和该对象的内存地址/运行类型绑定 当调用对象属性时&#xff0c;没有动态绑定机制&#xff0c;哪里声明&#xff0c;哪里使用 …...

2024年华为OD机试真题-查找一个有向网络的头节点和尾节点-Java-OD统一考试(C卷)

题目描述: 给定一个有向图,图中可能包含有环,图使用二维矩阵表示,每一行的第一列表示起始节点,第二列表示终止节点,如[0, 1]表示从0到1的路径。每个节点用正整数表示。求这个数据的首节点与尾节点,题目给的用例会是一个首节点,但可能存在多个尾节点。同时,图中可能含有…...

【Django开发】0到1美多商城项目md教程第5篇:短信验证码,1. 避免频繁发送短信验证码逻辑分析【附代码文档】

美多商城完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;欢迎来到美多商城&#xff01;&#xff0c;项目准备。展示用户注册页面&#xff0c;创建用户模块子应用。用户注册业务实现&#xff0c;用户注册前端逻辑。图形验证码&#xff0c;图形验证码接口设…...

云原生:应用敏捷,华为视角下的应用现代化

Gartner 也提出&#xff0c;到 2023 年&#xff0c;新应用新服务的数量将达到 5 亿&#xff0c;也即是说&#xff1a;“每个企业都正在成为软件企业”。据IDC 预测&#xff0c;到 2025 年三分之二的企业将成为多产的“软件企业”&#xff0c;每天都会发布软件版本。越来越多的企…...

【测试篇】接口测试

接口测试&#xff0c;可以用可视化工具 postman。 如何做接口测试&#xff1f;&#xff1f; 我们可以先在浏览器中随机进入一个网页&#xff0c;打开开发者工具&#xff08;F12&#xff09;。 随便找一个接口Copy–>Copy as cURL(bash) 打开postman 复制地址 进行发送。 …...

突破校园网限速:使用 iKuai 多拨分流负载均衡 + Clash 代理(内网带宽限制通用)

文章目录 1. 简介2. iKuai 部署2.1 安装 VMware2.2 安装 iKuai(1) 下载固件(2) 安装 iKuai 虚拟机(3) 配置 iKuai 虚拟机(4) 配置 iKuai(5) 配置多拨分流 2.3 测试速度 3. Clash 部署(1) 配置磁盘分区(2) 安装 Docker(3) 安装 Clash(4) 设置代理 4. 热点&#xff1a;一起瓜分互…...

03-JAVA设计模式-工厂模式详解

工厂模式 工厂设计模式是一种创建型设计模式&#xff0c;它提供了一种封装对象创建过程的机制&#xff0c;将对象的创建与使用分离。 这种设计模式允许我们在不修改客户端代码的情况下引入新的对象类型。 在Java中&#xff0c;工厂设计模式主要有三种形式&#xff1a;简单工厂…...

百度文心大模型推理成本降至1% / 马斯克起诉OpenAI |魔法半周报

我有魔法✨为你劈开信息大海❗ 高效获取AIGC的热门事件&#x1f525;&#xff0c;更新AIGC的最新动态&#xff0c;生成相应的魔法简报&#xff0c;节省阅读时间&#x1f47b; &#x1f525;资讯预览 百度文心大模型推理成本降至1%&#xff0c;与三星、荣耀等企业达成合作 马斯…...

别再瞎写 Prompt 了:2026年最实用的10条LLM提示词技巧

别再瞎写 Prompt 了&#xff1a;2026年最实用的10条LLM提示词技巧强烈推荐收藏&#xff01;从 OpenAI 官方指南到社区实践精华&#xff0c;每条技巧都附带 ❌ 错误示范 → ✅ 正确示范 → &#x1f4a1; 原理说明。这个问题你肯定遇到过 你打开 ChatGPT&#xff0c;输入&#x…...

k8s——RBAC认证中心

一、整体流程&#xff1a;认证 → 授权 → 准入控制在 Kubernetes 中&#xff0c;所有操作都要通过 API Server。当你&#xff08;或某个程序&#xff09;想对集群做任何事&#xff08;比如创建一个 Pod&#xff09;&#xff0c;必须经过三步检查&#xff1a;认证&#xff1a;你…...

Arm CoreSight TPIU-M调试架构与寄存器配置详解

1. Arm CoreSight TPIU-M架构概述 在嵌入式系统调试领域&#xff0c;Arm CoreSight架构提供了一套完整的调试与跟踪解决方案。作为该架构中的关键组件&#xff0c;Trace Port Interface Unit-Modified&#xff08;TPIU-M&#xff09;承担着将处理器内部跟踪数据输出到外部调试工…...

(5月最新版)OpenClaw 小龙虾 Windows 一键安装与问题排查

OpenClaw&#xff08;小龙虾&#xff09;Windows 11 一键部署教程&#xff5c;2026 新版&#xff5c;零代码・免配置・解压即用 适用系统&#xff1a;Windows 11 专业版 / 家庭版 / 正式版&#xff08;全版本兼容&#xff09;当前版本&#xff1a;v2.7.1 下载地址&#xff1a;…...

sdd-riper:专业磁盘镜像工具在数据恢复中的原理与实践

1. 项目概述与核心价值最近在整理一些老旧存储设备时&#xff0c;遇到了一个挺典型的问题&#xff1a;手头有几块年代久远的硬盘&#xff0c;里面可能还存着一些早年间的照片、文档&#xff0c;但硬盘本身已经不太稳定&#xff0c;系统里能识别&#xff0c;但拷贝文件时动不动就…...

AI Agent思维文件版本控制:mindkeeper工具的设计原理与实战指南

1. 项目概述&#xff1a;为AI的“大脑”打造时光机如果你正在使用像OpenClaw这样的AI助手框架&#xff0c;或者任何基于Markdown文件来定义AI行为、记忆和技能的项目&#xff0c;那么你一定经历过这样的时刻&#xff1a;为了优化AI的回复风格&#xff0c;你反复调整了SOUL.md里…...

为什么你的Gemini写作总像“AI腔”?资深技术文档架构师揭秘3层语义校准法

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;为什么你的Gemini写作总像“AI腔”&#xff1f;资深技术文档架构师揭秘3层语义校准法 Gemini 生成的技术文档常被诟病为“语法正确但语义失焦”——术语堆砌、逻辑断层、人机语感割裂。根本原因在于模…...

惠普开发了一架3D打印无人机,超轻、超快组装、成功试飞!

3D打印技术参考注意到&#xff0c;惠普于日前自行开发了一架基于增材制造设计的结构优化无人机&#xff0c;来展示使用其MJF技术进行3D打印制造的巨大潜力。它的核心观点是&#xff0c;无人机开发与制造的一个重大挑战&#xff0c;是团队花了几个月时间进行的优化设计&#xff…...

别再只盯着密钥了!深入ESP32 eFuse,看懂flash加密背后的硬件安全逻辑

别再只盯着密钥了&#xff01;深入ESP32 eFuse&#xff0c;看懂flash加密背后的硬件安全逻辑 当你在ESP32项目中使用flash加密功能时&#xff0c;是否曾疑惑过&#xff1a;为什么简单地烧录几个eFuse位就能实现固件保护&#xff1f;那些看似神秘的DISABLE_DL_DECRYPT、FLASH_CR…...

别再只用VGG19做分类了!手把手教你用PyTorch提取4096维图像特征向量(实战教程)

突破分类局限&#xff1a;用PyTorch解锁VGG19的深度特征提取实战 当你第一次接触VGG19时&#xff0c;可能被它的ImageNet分类能力所震撼。但如果你只把它当作一个分类器&#xff0c;那就如同用瑞士军刀只开瓶盖——大材小用。在计算机视觉领域&#xff0c;预训练模型真正的价值…...