View的绘制过程

简介: View的绘制过程从Activity.setContentView开始经过如下方法:Activity.setContentView—>PhoneWindow.

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方法。

目录
相关文章
|
API 调度 Android开发
自定义View 绘制顺序
自定义View 绘制顺序
自定义View 绘制顺序
|
前端开发 API
View的测量、布局和绘制过程中的关键方法
我们这里说的View的测量、布局和绘制,实质上是针对ViewGroup的,简单起见就不区分View和ViewGroup。View的测量、布局和绘制是包含在ViewGroup流程中的。
|
前端开发 容器
View的测量、布局和绘制过程中父View(当前View)和子View的先后顺序
View的测量、布局和绘制过程中,到底是先测量(布局、绘制)父View,还是先测量子View,这篇文章会从源码角度给出答案。
|
Linux Android开发 开发者
Android窗口管理分析(1):View如何绘制到屏幕上的主观理解
Android窗口管理分析(1):View如何绘制到屏幕上的主观理解
222 0
Android窗口管理分析(1):View如何绘制到屏幕上的主观理解
|
前端开发 API Android开发
3.3 自定义控件基础 之 View的绘制
当测量好了一个View之后,我们就可以简单地重写onDraw()方法,并在Canvas对象上来绘制所需要的图形。首先我们来了解一下利用系统2D绘图API所必须要使用到的Canvas对象。
659 0
|
缓存 前端开发
View的测量、布局和绘制过程
写在前面的话         按照之前写的节奏来的话,这篇改对View的整个测量、布局和绘制过程进行分析了。在之前的Activity显示到Window的过程中了解到performTraversals()这个方法会执行performMeasure()去测量View的大小,performLayout()去将子View放到合适的位置上,performDraw()将View真正绘制出来。
849 0
|
前端开发 Android开发 Python
这可能是第二好的自定义 View 教程之绘制
面试系列 不继续了吗? 知道我的人都知道,之前我写了这个 面试系列宣言,如今好像一直都没有连载,而是隔三差五地来一篇,其实也是因为笔者也能力有限,构思一篇文章需要足够的时间去印证其准确性,而之前的部分就因为印证不够造成了勘误。
1098 0