【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);}
}

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

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

相关文章

876.链表的中间结点 143.重排链表

给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[3,4,5] 解释&#xff1a;链表只有一个中间结点&#xff0c;值为 3 。示…

文献学习-28-Endora: 用于内镜仿真的视频生成模型

Endora : Video Generation Models as Endoscopy Simulators Authors: Chenxin Li, Hengyu Liu, Yifan Liu, Brandon Y. Feng, Wuyang Li, Xinyu Liu, Zhen Chen, Jing Shao, Yixuan Yuan Keywords: Medical Generative AI Video Generation Endoscopy Abstract 生成模型有…

【C++】红黑树讲解及实现

前言&#xff1a; AVL树与红黑树相似&#xff0c;都是一种平衡二叉搜索树&#xff0c;但是AVL树的平衡要求太严格&#xff0c;如果要对AVL树做一些结构修改的操作性能会非常低下&#xff0c;比如&#xff1a;插入时要维护其绝对平衡&#xff0c;旋转的次数比较多&#xff0c;更…

33. UE5 RPG使用增强输入激活GameplayAbility(三)

在前面的文章&#xff0c;我们实现了使用GameplayTag和InputAction的对应绑定的数据&#xff0c;并且添加到了增强输入映射的上下文中&#xff0c;实现了通过按键打印对应的GameplayTag&#xff0c;这只是我们基础需要制作的。目的主要是为了实现在GameplayAblity上面设置对应的…

深入浅出 -- 系统架构之分布式多形态的存储型集群

一、多形态的存储型集群 在上阶段&#xff0c;我们简单聊了下集群的基本知识&#xff0c;以及快速过了一下逻辑处理型集群的内容&#xff0c;下面重点来看看存储型集群&#xff0c;毕竟这块才是重头戏&#xff0c;集群的形态在其中有着多种多样的变化。 逻辑处理型的应用&…

【.Net】DotNetty

文章目录 概述NIO和BIO、AIODotNetty适用场景DotNetty的整体架构和模块DotNetty的使用示例来源 概述 本系列文章主要讲述由微软Azure团队研发的.net的版本的netty&#xff0c;Dotnetty。所有的开发都将基于.net core 3.1版本进行开发。 Dotnetty是什么&#xff0c;原本Netty是…

蓝桥真题--路径之谜DFS解法

路径之谜 思路 前置知识&#xff1a;深度搜索模板搜索所有可以找的路径&#xff0c;将走过的靶子减去一走到最后一个格子的时候&#xff0c;直接去判断所有的靶子只有除最后一个位置的靶子&#xff0c;其余靶子都归零的时候&#xff0c;判断一个最后一个位置横坐标和纵坐标的靶…

PTA C 1050 螺旋矩阵(思路与优化)

本题要求将给定的 N 个正整数按非递增的顺序&#xff0c;填入“螺旋矩阵”。所谓“螺旋矩阵”&#xff0c;是指从左上角第 1 个格子开始&#xff0c;按顺时针螺旋方向填充。要求矩阵的规模为 m 行 n 列&#xff0c;满足条件&#xff1a;mn 等于 N&#xff1b;m≥n&#xff1b;且…

Bayes-RF,基于贝叶斯Bayes优化算法优化随机森林RF分类预测(二分类及多分类皆可)-附代码

Bayesian Optimization&#xff08;贝叶斯优化&#xff09;是一种用于超参数调优的技术&#xff0c;对于类似随机森林&#xff08;Random Forest&#xff0c;简称RF&#xff09;的机器学习算法非常重要。随机森林是一种集成学习方法&#xff0c;它在训练过程中构建多个决策树&a…

漂亮国的无人餐厅的机器人骚操作

导语 大家好&#xff0c;我是智能仓储物流技术研习社的社长&#xff0c;你的老朋友&#xff0c;老K。行业群 新书《智能物流系统构成与技术实践》 知名企业 读者福利&#xff1a; &#x1f449;抄底-仓储机器人-即买即用-免调试 智能制造-话题精读 1、西门子、ABB、汇川&#x…

详解 Redis 在 Centos 系统上的安装

文章目录 详解 Redis 在 Centos 系统上的安装1. 使用 yum 安装 Redis 52. 创建符号链接3. 修改配置文件4. 启动和停止 Redis 详解 Redis 在 Centos 系统上的安装 1. 使用 yum 安装 Redis 5 如果是Centos8&#xff0c;yum 仓库中默认的 redis 版本就是5&#xff0c;直接 yum i…

Premiere Pro 2024:赋予创意翅膀,让你的视频飞翔 mac/win版

Premiere Pro 2024&#xff0c;作为Adobe旗下的旗舰视频编辑软件&#xff0c;自推出以来&#xff0c;一直在视频制作领域占据着重要的地位。随着技术的不断进步和创新&#xff0c;Premiere Pro 2024为用户带来了前所未有的编辑体验&#xff0c;重新定义了视频制作的标准。 Pre…

Unity类银河恶魔城学习记录12-3 p125 Limit Inventory Slots源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili Inventory.cs using Newtonsoft.Json.Linq; using System.Collections; us…

深入go泛型特性之comparable「附案例」

写作背景 如果你经常遇到一些操作&#xff0c;比如将 map 转换为 slice&#xff0c;判断一个字符串是否出现在 map 中&#xff0c;slice 中是否有重复元素等等&#xff0c;那你对下面这个库肯定不陌生。 github.com/samber/lo最近抽业余时间在看了源码&#xff0c;底层用了范…

云计算的安全需求

目录 一、概述 二、云安全服务基本能力要求 三、信息安全服务&#xff08;云计算安全类&#xff09;资质要求 3.1 概述 3.2 资质要求内容 3.2.1 组织与管理要求 3.2.2 技术能力要求 四、云安全主要合规要求 4.1 安全管理机构部门的建立 4.2 安全管理规范计划的编制 4…

【Flutter】Getx设计模式及Provider、Repository、Controller、View等

本文基于Getx 4,x 本本 1、引入 再次接触到Flutter项目&#xff0c;社区俨然很完善和活跃。pubs.dev 寻找状态管理的时候看到很熟悉的Getx时间&#xff0c;俨然发现Getx的版本已到是4.x版本&#xff0c;看到Getx的功能已经非常强大了&#xff0c;庞大的API俨然成为一种开发框架…

Windows Server 2008添加Web服务器(IIS)、WebDAV服务、网络负载均衡

一、Windows Server 2008添加Web服务器&#xff08;IIS&#xff09; &#xff08;1&#xff09;添加角色&#xff0c;搭建web服务器&#xff08;IIS&#xff09; &#xff08;2&#xff09;添加网站&#xff0c;关闭默认网页&#xff0c;添加默认文档 在客户端浏览器输入服务器…

蓝桥杯 十一届C++A组 字符排序 21分(运行超时)

思路&#xff1a; 1. 此题考查的冒泡排序中的交换次数&#xff0c;其实就是考察当前数与后面的逆序对个数问题。而为了最大利用位数&#xff0c;应当使每一位都不小于后面的字符&#xff0c;否则会造成一次逆序对的浪费&#xff08;贪心&#xff0c;为了使总位数最少&#xff…

每日OJ题_优先级队列_堆③_力扣692. 前K个高频单词

目录 力扣692. 前K个高频单词 解析代码 力扣692. 前K个高频单词 692. 前K个高频单词 难度 中等 给定一个单词列表 words 和一个整数 k &#xff0c;返回前 k 个出现次数最多的单词。 返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率&#xff0c…

《QT实用小工具·三》偏3D风格的异型窗体

1、概述 源码放在文章末尾 可以在窗体中点击鼠标左键进行图片切换&#xff0c;项目提供了一些图片素材&#xff0c;整体风格偏向于3D类型&#xff0c;也可以根据需求自己放置不同的图片。 下面是demo演示&#xff1a; 项目部分代码如下所示&#xff1a; 头文件部分&#xff…