博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
View的绘制过程
阅读量:6277 次
发布时间:2019-06-22

本文共 2450 字,大约阅读时间需要 8 分钟。

View的绘制过程从Activity.setContentView开始经过如下方法:

Activity.setContentView—>PhoneWindow.setContentView—>ViewRootImpl.requestLayout—>ViewRootImpl.scheduleTraversals—>ViewRootImpl.TraversalRunnable—>ViewRootImpl. doTraversal—>ViewRootImpl. performTraversals—>ViewRootImpl.performMeasure—>ViewRootImpl. performLayout—>ViewRootImpl. performDraw

img_15943f98c909d749e456599c77f7c8c2.png
View绘制过程

Measure

测量View的大小,从ViewRootImpl.measureHierarchy开始,计算各个控件显示需要多大的尺寸。

img_ce850dd72b9124b0d00c1dee40793bc5.png
ViewRootImpl.measureHierarchy

其中getRootMeasureSpec用来获取根布局的MeasureSpec,MeasureSpec封装了从父容器传递给子容器的布局要求,而不是父容器对子容器的布局要求,更精确的说法是,MeasureSpec是由父View的MeasureSpec和子View的LayoutParams通过计算得出的一个针对子View的测量要求,这个测量要求就是MeasureSpec。类似于下图,由一个32位的整型将mode和size包装起来。

img_b4a08a4fe27bf028ecdb36a2432e5e9c.png
MeasureSpec

MeasureSpec一共有三种模式,UNSPECIFIED(mode=0),父容器对于子容器大小没有任何限制,子容器想要多大就多大;EXACTLY(mode=-1):父容器已经为子容器设置了尺寸,子容器无论想要多大空间,都应该服从父容器指定的边界;AT_MOST(mode=-2):子容器可以是父容器指定边界内的任意大小。

img_263bdb15c79f97fd16ebb6fe95a3697a.png
ViewRootImpl.getRootMeasureSpec

根布局直接根据自己width和height属性计算MeasureSpec,MATCH_PARENT说明父布局的大小就是窗口大小,mode就是EXACTLY;WRAP_CONTENT说明根布局的最大尺寸就是窗口的尺寸,mode为AT_MOST。

在计算出Root的MeasureSpec后就通过performMeasure来调用DectorView的measure方法,该方法计算出View的大小,参数是父View对它的宽高约束,实际的测量工作是在onMeasure方法中进行的,对于DectorView就是调用FrameLayout的onMeasure方法。

img_6ddc6fabf66530cf6ec9497b6199d468.png
FrameLayout.onMeasre

FrameLayout是ViewGroup的子类,有个View[]类型的成员变量mChildren,在上图的源码中,首先调用measureChildWithMargin方法对所有子View进行一遍策略,计算所有子View的最大宽度和高度。寄过一系列计算后得到maxHeight和maxWidth。measureChildWithMargin是ViewGroup的方法,如下所示,通过调用View.measure方法。

img_6bbc9099c6a9267f330a74ffa25747b4.png
measureChildWithMargin

在measure child的时候,需要传入对子view的MeasureSpec限制,通过getChildMeasureSpec可以计算ViewGroup对子View的Spec限制,计算方式如下,根据父View的MeasureSpec和子View的LayoutParams生成子View的MeasureSpec的过程,子View的LayoutParam表示子View期待的大小。

img_ce85561f4f4a56bfdd520596b66e8bb1.png
img_4688a377f179c8d65073867f2252d86a.png
getChildMeasureSpec

以上代码分为以下几种情况:

1、父View的MeasureSpec为EXACTLY

如果childDimension>0,说明给子View指定了具体的大小,那么子View的mode为EXACTLY,size为childDimension;如果childDimension=MATH_PARENT,那么子View就是父View的Size,所以子View的Mode为EXACTLY,Size就是父View的Size;如果childDimension=WRAP_CONTENT,那么子View的最大Size为父View的Size,所以子View的Mode为AT_MOST,Size就是父View的Size

2、父View的MeasureSpec为AT_MOST

表示父View限制了子View最大为Size,如果childDimension>0,说明给子View指定了具体的大小,那么子View的Mode为EXACTLY,Size为childDimension;如果childDimension=MATH_PARENT,那么子View想和父View一样大,Mode就是AT_MOST,Size就是父View的Size;如果childDimension=WRAP_CONTENT,子View想要自己决定尺寸,但不能比父View大,Mode就是AT_MOST,Size就是父View的Size。

child.measure过程如下:

img_cfe4669effc74af2f210c0168642b669.png
View.measure

在调用了measureChildWithMargin方法后,获取到子View的MeasureSpec,接下来就调用子View的measure,并将MeasureSpec传进入, 最终调用onMeasure方法,并通过setMeasureDimension进行设置。

在Measure结束后就运行ViewRootImpl.performLayout,最后运行onLayout;之后运行performDraw,运行View的onDraw方法。

转载地址:http://argpa.baihongyu.com/

你可能感兴趣的文章
TFS二次开发系列:四、TFS二次开发WorkItem添加和修改、保存
查看>>
一次向svn中增加所有新增文件 svn add all new files【转】
查看>>
云计算-从基础到应用架构系列-云计算的概念
查看>>
How to convert smartform output into pdf?
查看>>
SQL Left Join, Right Join, Inner Join, and Natural Join 各种Join小结
查看>>
粒子滤波概述
查看>>
FlyCapture2 Qt5 MinGW Configuration
查看>>
Ruby on Rails入门——macOS 下搭建Ruby Rails Web开发环境
查看>>
Kafka 设计与原理详解
查看>>
[外挂7] 井字棋外挂 博弈算法
查看>>
国家代码查询
查看>>
弯道超车,换一个思路,避免addEventListener为同一个元素重复赋予事件
查看>>
布隆过滤器
查看>>
C#中使用Redis不同数据结构的内存占有量的疑问和对比测试
查看>>
配置druid内置的log实现
查看>>
[LeetCode] Meeting Rooms II 会议室之二
查看>>
泛型与非泛型的区别
查看>>
ASP.NET MVC:WebPageBase.cs
查看>>
Xen虚拟机的创建和启动
查看>>
Design Pattern: Factory Method 模式
查看>>