当前位置: 首页 > 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;与三星、荣耀等企业达成合作 马斯…...

Struts2的入门:新建项目——》导入jar包——》jsp,action,struts.xml,web.xml——》在项目运行

文章目录 配置环境tomcat 新建项目导入jar包新建jsp界面新建action类新建struts.xml,用来配置action文件配置Struts2的核心过滤器&#xff1a;web.xml 启动测试给一个返回界面在struts.xml中配置以实现页面的跳转&#xff1a;result再写个success.jsp最后在项目运行 配置环境 …...

git 标签功能操作以及回退

Git 标签功能允许开发者为特定的提交打上标签&#xff0c;以便后续能够方便地引用这些提交。标签通常用于标记重要的版本或里程碑&#xff0c;例如软件发布的版本号。与分支不同&#xff0c;标签指向的是固定的提交&#xff0c;一旦设置&#xff0c;就不能轻易更改。下面是一些…...

利用python实现文字转语音

代码如下&#xff1a; import pathlib import tkinter as tk import tkinter.ttk as ttk import tkinter.filedialog as filedialog import tkinter.messagebox as msgbox import pyttsx3class Application(tk.Tk):def __init__(self):super().__init__()self.title("文本…...

拾光坞N3 ARM 虚拟主机 i茅台项目

拾光坞N3 在Dcoker部署i茅台案例 OS&#xff1a;Ubuntu 22.04.1 LTS aarch64 cpu&#xff1a;RK3566 ram&#xff1a;2G 部署流程——》mysql——》java8——》redis——》nginx mysql # 依赖 apt update apt install -y net-tools apt install -y libaio* # 下载mysql wg…...

docker安装nacos,单例模式(standalone),使用mysql数据库

文章目录 前言安装创建文件夹"假装"安装一下nacos拷贝文件夹删除“假装”安装的nacos容器生成nacos所需的mysql表获取mysql-schema.sql文件创建一个mysql的schema 重新生成新的nacos容器 制作docker-compose.yaml文件查看网站 前言 此处有本人写得简易版本安装&…...

【运输层】传输控制协议 TCP

目录 1、传输控制协议 TCP 概述 &#xff08;1&#xff09;TCP 的特点 &#xff08;2&#xff09;TCP 连接中的套接字概念 2、可靠传输的工作原理 &#xff08;1&#xff09;停止等待协议 &#xff08;2&#xff09;连续ARQ协议 3、TCP 报文段的首部格式 1、传输控制协议…...

深入浅出 -- 系统架构之Keepalived搭建双机热备

Keepalived重启脚本双机热备搭建 ①首先创建一个对应的目录并下载keepalived安装包&#xff08;提取码:s6aq&#xff09;到Linux中并解压&#xff1a; [rootlocalhost]# mkdir /soft/keepalived && cd /soft/keepalived [rootlocalhost]# wget https://www.keepalived.…...

如何做好产业园运营?树莓集团:响应政府号召,规划,注重大局观

随着经济的发展和产业结构的调整&#xff0c;产业园区的建设和发展已经成为推动地方经济的重要力量。如何做好产业园运营&#xff0c;提高行业竞争力&#xff0c;现已成为了一个亟待解决的问题。树莓集团作为一家有着丰富产业园运营经验的企业&#xff0c;积极响应政府号召&…...

NIO与BIO

当谈到 Java 网络编程时&#xff0c;经常会听到两个重要的概念&#xff1a;BIO&#xff08;Blocking I/O&#xff0c;阻塞 I/O&#xff09;和 NIO&#xff08;Non-blocking I/O&#xff0c;非阻塞 I/O&#xff09;。它们都是 Java 中用于处理 I/O 操作的不同编程模型。 一、介…...

YOLOv5实战记录05 Pyside6可视化界面

个人打卡&#xff0c;慎看。 指路大佬&#xff1a;【手把手带你实战YOLOv5-入门篇】YOLOv5 Pyside6可视化界面_哔哩哔哩_bilibili 零、虚拟环境迁移路径后pip报错解决 yolov5-master文件夹我换位置后&#xff0c;无法pip install了。解决如下&#xff1a; activate.bat中修改…...

网站侧栏软件排行榜怎么做的/网站推广方案策划书2000

遇到异常后要保存设备参数和具体异常信息到SDCard2. 在 Application 中的 onCreate() 方法内实例化在 Application onCreate()方法中调用CrashCatchHandler.getInstance().init(getApplicationContext());1. 新建CrashCatchHandler处理类件在 >Android>data>包名>c…...

wordpress分类文章/微信运营方案

儿子开学要上二年级了,每天数学做业的都要家长出一些口算题.今天写了一个程序可以出题并打印.自已对.net打印这部分不是很熟&#xff0e;现在只能打印单页&#xff0e;请哪位高手帮我改一下能打印多页&#xff0e;usingSystem;usingSystem.Collections.Generic;usingSystem.Com…...

哪个网站做团购要求低点/广州网站制作服务

您好&#xff01;共享不了的原因是多种多样的&#xff0c;有些是因为安装防火墙阻挡&#xff0c;有些是因为权限设置&#xff0c;有些是因为服务或者计算机设置原因等等。而且解决方法都各不相同。而且因为您有多台电脑&#xff0c;因此他们的原因可能也是有很多种&#xff0c;…...

网站建设五大定位/成都自然排名优化

IOBluetoothUI 提供一个界面&#xff0c;用户可以通过该界面将其设备与其他蓝牙设备配对。 使用教程 BluetoothUI框架提供了允许用户选择蓝牙服务的接口。 推荐 基础文章推荐 《SwiftUI是什么&#xff0c;听听大牛们如何说》 经典教程推荐 更新近百篇SwiftUI教程《SwiftUI2020教…...

软文营销文章范文/湖南seo优化价格

2019独角兽企业重金招聘Python工程师标准>>> 客户端——MemStore——列族——Hfile 1、Hbase在执行写操作的时候&#xff0c;首先会将写动作记录在hlog,memstore中&#xff0c;只有在这两个地方都确认完成后才被记录操作成功。 Memstore是内存里面的缓冲区&#xff…...

怎么做网站页面代码搜索/郑州企业网站seo

解决办法1 cd /etc chown root:shadow shadow chmod ur,gr shadowhttps://blog.csdn.net/newthinker_wei/article/details/8650798 ↩︎...