Android中View的绘制流程

一、View绘制的起点

        当建立好了decorView与ViewRoot的关联后,ViewRoot类的requestLayout()方法会被调用,以完成应用程序用户界面的初次布局。实际被调用的是ViewRootImpl类的requestLayout()方法

@Override
public void requestLayout() {if (!mHandlingLayoutInLayoutRequest) {// 检查发起布局请求的线程是否为主线程checkThread();mLayoutRequested = true;scheduleTraversals();}
}

        requestLayout()方法调用了scheduleTraversals()方法来调度一次完整的绘制流程,该方法会向主线程发送一个“遍历”消息,最终会导致ViewRootImpl的performTraversals()方法被调用

二、View的绘制流程

        View的绘制,有三个步骤:测量(measure),布局(layout),绘制(draw), 从DecorView自上而下遍历整个View树

注意:是所有View执行完一个步骤后,再进行下一步,而不是一个View执行完所有步骤再遍历下一个View

  • Measure:测量视图大小。从顶层父View到子View递归调用measure方法,measure方法又回调OnMeasure。

  • Layout:确定View位置,进行页面布局。从顶层父View向子View的递归调用view.layout方法的过程,即父View根据上一步measure子View所得到的布局大小和布局参数,将子View放在合适的位置上。

  • Draw:绘制视图。ViewRoot创建一个Canvas对象,然后调用OnDraw()。
    六个步骤:

    1. 绘制视图的背景

    2. 保存画布的图层(Layer)

    3. 绘制View的内容

    4. 绘制View子视图,如果没有就不用

    5. 还原图层(Layer)

    6. 绘制滚动条

三、MeasureSpec详解

MeasureSpec由两部分组成,一部分是测量模式,另一部分是测量的尺寸大小

Mode模式分为三类:

  • EXACTLY:对应LayoutParams中的match_parent和具体数值这两种模式。检测到View所需要的精确大小,这时候View的最终大小就是SpecSize所指定的值,

  • AT_MOST :对应LayoutParams中的wrap_content。View的大小不能大于父容器的大小。

  • UNSPECIFIED :不对View进行任何限制,要多大给多大,一般用于系统内部,如ListView,ScrollView

MeasureSpec的确定:

子View的MeasureSpec由父View根据自身的MeasureSpec和子View的LayoutParams来共同确定子View的MeasureSpec

注意:即使确定了子View的MeasureSpec并不一定决定了子View的大小,自定义View可以根据需要修改这个值,最终通过setMeasuredDimension(width, height)设置最终大小。

四、View执行onMeasure,onLayout的次数

        根据ViewRootImpl的源码,scheduleTraversales()内部会执行postCallBack触发mTraversalRunnable重新走一遍performTraversals(),第二次执行performTraversals()就会触发performDraw()。所以performTraversals()走了两次,那么肯定会走2次measure方法

        但不一定走2次onMeasure(),measure方法做了2级测量优化:

  • 1.如果flag不为forceLayout或者与上次测量规格(MeasureSpec)相比未改变,那么将不会进行重新测量(执行onMeasure方法),直接使用上次的测量值;

  • 2.如果满足非强制测量的条件,即前后二次测量规格不一致,会先根据目前测量规格生成的key索引缓存数据,索引到就无需进行重新测量;如果targetSDK小于API 20则二级测量优化无效,依旧会重新测量,不会采用缓存测量值。

五、getWidth()和getMeasuredWidth()的区别

        getMeasuredWidth()、getMeasuredHeight()必须在onMeasure之后使用才有效getMeasuredWidth() 的取值最终来源于 setMeasuredDimension() 方法调用时传递的参数

         getWidth()返回的是;mRight - mLeft,mRight、mLeft 变量分别表示View相对父容器的左右边缘位置,getWidth()必须在layout(int l, int t, int r, int b)执行之后才有效

六、如何在onCreate中拿到View的宽高

View.post(runnable)

view.post(new Runnable() {            @Overridepublic void run() {int width = view.getWidth();int measuredWidth = view.getMeasuredWidth();Log.i(TAG, "width: " + width);Log.i(TAG, "measuredWidth: " + measuredWidth);}});

        利用Handler通信机制,发送一个Runnable到MessageQueue中,当View布局处理完成时,自动发送消息,通知UI进程。获取View的高宽属性,代码简洁

ViewTreeObserver.addOnGlobalLayoutListener()

       ViewTreeObserver vto = view.getViewTreeObserver();       vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {@Overridepublic void onGlobalLayout() {view.getViewTreeObserver().removeGlobalOnLayoutListener(this);Log.i(TAG, "width: " + view.getWidth());Log.i(TAG, "height: " + view.getHeight());}});

监听View的onLayout()绘制过程,一旦layout触发变化,立即回调onLayoutChange方法

注意:使用完也要主要调用removeOnGlobalListener()方法移除监听事件。避免后续每一次发生全局View变化均触发该事件,影响性能

七、invalidate和postInvalidate区别

        二者都会触发刷新View,并且当这个View的可见性为VISIBLE的时候,View的onDraw()方法将会被调用

        invalidate()方法在UI线程中调用,重绘当前 UI

        postInvalidate()方法在非 UI 线程中调用,通过Handler通知 UI 线程重绘

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

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

相关文章

【Linux】Linux——Centos7安装

目录 虚拟机安装【空壳子】安装VMware Workstation新建虚拟机硬件兼容性(直接下一步)稍后安装操作系统客户及操作系统选择Linux,版本Centos764位给虚拟机命名,并选择安装位置处理器配置(默认即可,不够用后面可以调)虚拟…

Nexus Repository的搭建

一、前言 Nexus Repository用于管理maven的jar包。java开发程序员每天都在使用,但是自己搭建或者管理的就很少。除非你是系统架构师。因为这一套东西,基本是搭建一次就不需要人来搭建了,日后打开界面维护的机会也很少,我们只需要…

全局消息转换器如何排某些属性

今天使用分页时,发现前端页面,无法正确显示页号,经过debug发现,是传送回前端的Long类型的总记录数被全局的消息转换器转换成了 String 类型,导致出现了bug,本来将 Long 转成String是为了防止前端精度丢失问题&#xff…

2834. 找出美丽数组的最小和

2834. 找出美丽数组的最小和 给你两个正整数:n 和 target 。 如果数组 nums 满足下述条件,则称其为 美丽数组 。 nums.length n.nums 由两两互不相同的正整数组成。在范围 [0, n-1] 内,不存在 两个 不同 下标 i 和 j ,使得 nu…

一元函数微分学——刷题(26

目录 1.题目:2.解题思路和步骤:3.总结:小结: 1.题目: 2.解题思路和步骤: 归纳求解,把指数写成负数就比较容易看出来规律 3.总结: 归纳求解,把指数写成负数就比较容易…

资产管理系统有哪些(一体化资产管理平台推荐)

企业资产管理系统是一种关键的工具,旨在帮助企业有效地管理和追踪其资产。 该系统利用计算机系统和相关软件,通过信息化、智能化的方式,对资产进行全面的可视化管理,从而提高管理效率、降低运营成本,并确保资产的安全…

Hadoop配置日志的聚集——jobhistory不显示任务问题

问题: 一开始job history是正常的,配置了日志的聚集以后不管做什么任务都不显示任务,hdfs是正常运行,而且根据配置步骤都重启过了。 下面先po出日志聚集的操作步骤,再讲问题 1.配置yarn-site.xml cd $HADOOP_HOME/e…

经典排序算法之快速排序|c++代码实现|什么是快速排序|如何代码实现快速排序

引言 排序算法c实现系列第6弹——快速排序 文章末尾还有本菜已实现的其他排序算法文章的链接。不过,排序算法这个系列还没更完,争取本周末搞完!之后还会有堆排序、桶排序等的代码实现,感兴趣的佳人可以点个赞&收藏&#xff…

2024.03.04——2024.03.10 力扣练习总结及专项巩固

1. &#xff08;18. 四数之和&#xff09;已知在一个cpp程序中&#xff0c;使用了"#include<algorithm>"语句&#xff0c;声明引入algorithm头文件。现在假如有一个vector<int>类型的变量nums{-1, 3, 1, -2}&#xff0c;如果仅使用一个语句对其进行排序&…

C++ 信号槽实现sigslot库(不使用QT框架)

SIGSLOT库很简单就一个文件&#xff0c;以下是使用简单的方法&#xff1a; 1、没有参数 signal0<> m_sigSdkInit;信号参数初始化 m_sigSdkInit.connect(this,&类名::SdkLoadingSlot); 信号槽建立连接 m_sigSdkInit.emit();触发方法 2、带一个参数 signal0<int>…

Igraph入门指南 4

二、图的创建 图分有向图和无向图&#xff0c;所以图的创建有各自的实现方式。 1、手工创建图&#xff1a; 1-1 通过文本创建&#xff1a;graph_from_literal 通过每项提供两个顶点名&#xff08;或ID号&#xff09;作为一条边的格式&#xff0c;手动创建图&#xff0c;顶点…

【敬伟ps教程】文字处理工具

文章目录 文字工具使用方式文字图层文字工具选项字符面板段落面板文字工具使用方式 文字工具(快捷键T),包含横排和直排两种类型 创建文本两种类型:点式文本、段落文本 创建文字方式 1、在画面上单击,出现文字光标,可输入文字,然后需要在工具栏中点击“√”或者 Ctrl+…

数学建模-动态规划(美赛运用)

动态规划模型的要素是对问题解决的抽象&#xff0c;其可分为&#xff1a; 阶段。指对问题进行解决的自然划分。例如&#xff1a;在最短线路问题中&#xff0c;每进行走一步的决策就是一个阶段。 状态。指一个阶段开始时的自然状况。例如&#xff1a;在最短线路问题中&#xff…

docker 运行异构镜像

概述 关于docker镜像在不同的cpu架构下运行报错的解决办法&#xff0c;作者踩坑验证&#xff0c;在此分享经验 某次工作遇到需要银行内部部署docker镜像&#xff0c;由于行内已经开始走信创的路线&#xff0c;使用鲲鹏系统&#xff0c;arm架构&#xff0c;记过就遇到了standa…

Linux:安装docker并修改其目录

一、背景 1、通常而言&#xff0c;云服务器的系统盘只有40G&#xff0c;若安装docker后直接运行&#xff0c;则其下载的镜像及容器&#xff0c;均在该系统盘下。 2、当前服务器硬盘背景为&#xff1a;40G系统盘 额外数据盘。 3、需求&#xff1a;将docker运行目录修改至数据盘…

ComfyUI-Flowty-TripoSR

这是一个自定义节点&#xff0c;可让您直接从ComfyUI使用TripoSR。TripoSR 是由 Tripo AI 和 Stability AI 合作开发的最先进的开源模型&#xff0c;用于从单个图像快速前馈 3D 重建。&#xff08;TL;DR 它从图像创建 3d 模型。这篇文章主要介绍了将TripoSR作为ComfyUI节点的配…

华容道问题求解_详细设计(四)之查找算法2_BFS

&#xff08;续上篇&#xff09; 利用BFS查找&#xff0c;会找到最短路径&#xff08;没有权重的图&#xff09;&#xff0c;这个道理比较简单&#xff0c;这是由于寻找路径的方法都是从起点或者接近起点的位置开始的。查找过程如果画出图来&#xff0c;类似于一圈圈的放大&…

【动态规划入门】判断子序列

每日一道算法题之判断子序列 一、题目描述二、思路三、C代码 一、题目描述 题目来源&#xff1a;LeetCode 给定字符串 s 和 t &#xff0c;判断 s 是否为 t 的子序列。 字符串的一个子序列是原始字符串删除一些&#xff08;也可以不删除&#xff09;字符而不改变剩余字符相对位…

HashMap 底层,同时遍历+删除会出现什么问题?怎么解决

HashMap底层使用的是数组链表/红黑树的方式实现的 在并发场景下同时进行遍历删除&#xff0c;会出现ConcurrentModificationException异常&#xff0c;这是因为HashMap的迭代器是快速失败的&#xff08;fail-fast&#xff09;,当检测到结构性的改变&#xff08;比如增加或删除…

java IO实现一次性读取一个文件全部内容后在写入一个文件

java IO实现一次性读取一个文件全部内容后在写入一个文件 import java.io.*;/*** author admin*/ public class FileIo {public static void main(String[] args) {String content getFileString();File outputFile new File("C:\\Users\\admin\\Desktop\\test\\1.txt&…