Android的View绘制流程

看见好多文章都写View系统的绘制流程会从ViewRoot的performTraversals()方法中开始,从刚开始迈步的时候就摔了一大跤,为什么呢?因为我在View的源码中就找不到ViewRoot这个类,只有一个ViewRootHandler类,我以为我找错了,我觉得ViewRoot应该是单独的类,于是尝试着声明了一个ViewRoot类型的变量,企图直接找到他,然而根本没有这东西,我猜会不会在ViewGroup呢?毕竟都带个View嘛,然而还是没有找到...我就滚回去看了一下ViewRootHandler,发现里面有一行requestLayout()!顾名思义,请求布局,那我就切(按着ctrl,用鼠标左键点他)进去

requestLayout的实现

checkThread是确认发起请求的布局是否处在主线程,这里有一个scheduleTraversals(),和performTraversals()有点像,都是遍历,那就切进去看看吧,似乎没有我们要找的东西,不过你看这里有一个getLooper(),根据学到的异步消息机制,我们可以知道Looper是Queue的管家,将队列中存在的信息取出,分发回handleMessage()中,我认为这个scheduleTraversals()的意义应该是在主线程中完成了一次View绘制,那我可不可以认为正在传递的消息就是View的绘制流程呢?我刚刚上下乱翻的时候见到了不少的xxImpl,Impl就是执行的缩写,我姑且试一下吧,搜搜Impl

scheduleTraversals()的实现


还真让我搜到了一个ViewRootImpl(),我估计这个应该就是View的绘制流程,然后我找到了可以证明这是View的绘制流程的圣物,绘制View的开端——performTraversals()!于是可以正式分析View的绘制流程了


众所周知View的绘制流程分为3个最主要的阶段,onMeasure()、onLayout()和onDraw(),下面我就一个一个找出来配合其他博客做一些分析


onMeasure()


6.0版本onMeasure()似乎改名了,改叫measureHierarchy()



MeasureSpec的值由specSize和specMode共同组成的,其中specSize记录的是大小,specMode记录的是规格

EXACTLY: 对子View提出了一个确切的建议尺寸(SpecSize)

AT_MOST: 子View的大小不得超过SpecSize

UNSPECIFIED: 对子View的尺寸不作限制,通常用于系统内部

measureHierarchy()确定了绘制的矩形的规格和大小,由getRootMeasureSpec()获取到了最外层的视图(根视图),再把获取到的大小和规格赋给childWidthMeasureSpec和childHeightMeasureSpec,我们切进去看看里面写了什么


getRootMeasureSpec

我们可以观察到lp.width和lp.height在创建出Viewgroup实例的时候被赋值了,默认为MATCH_PARENT,MeasureSpec调用了makeMeasureSpec组装一个MeasureSpec,当rootDimension等于MATCH_PARENT的时候,MeasureSpec的specMode就等于EXACTLY,当rootDimension等于WRAP_CONTENT的时候,MeasureSpec的specMode就等于AT_MOST,都不是的时候就由开发人员来决定大小。MATCH_PARENT和WRAP_CONTENT时的specSize都是等于windowSize的,也就意味着根视图总是会充满全屏的


把通过makeMeasureSpec规定好的高和宽传给performMeasure(),切进去看一下他的源码

mView就是大小和规格已经确定的View,调用它的measure()开始测量,切进去看看

调用measure()来算出一个View应该为多大。参数为父View对其宽高的约束信息。

什么情况下才会开始测量呢?可以看出是在需要测量或者强制测量时开始测量,如果这时发现缓存中存在已经测量好的View就不在测量,如果没有View的缓存就调用onMeasure()开始真正的实际测量,最后将测量好的结果作为最终值返回,那么实际测量又是怎样测量的呢?切进去看看


可以看到实际测量工作由getDefaultSize()完成,用它来获取View的大小,但是在深入下去路就断了,我查了百度后才知道实际执行测量工作的是FrameLayout的onMeasure(),实际测量是这样的,先通过measureChildWithMargins()测量子View,在测量自身,还要把padding的部分也算进去,这样就能测量出一个能够正常显示自身及其所有子View的结果,再调用resolveSizeAndState()把测量好的结果缓存起来,最后就通过getDefaultSize()获取默认尺寸,完成实际测量

需要注意的是,在setMeasuredDimension()方法调用之后,我们才能使用getMeasuredWidth()和getMeasuredHeight()来获取View测量出的宽高,以此之前调用这两个方法得到的值都会是0。

View大小的控制是由父View、布局文件(MATCH_PARENT和WRAP_CONTENT)、以及View本身共同完成的,父View会提供给子View参考的大小,而开发人员可以在XML文件中指定View的大小,然后View本身会对最终的大小进行拍板


onLayout()


经过了一轮测量,View的大小已经测量好了,需要对View进行布局,就是确定View的位置,接着就会调用View里的layout(),我们切进去看一下吧


layout()接收4个参数代表着上(top)下(below)左(left)右(right),这个坐标是相对于当前的父布局而言的。chang判断View的大小是否变化,是否要对当前View进行重绘。同时还会在这里把传递过来的四个参数分别赋值给mLeft、mTop、mRight和mBottom这几个变量,接下来是重头戏onLayout(),切进去看看

怎么是个空方法???经过百度查证,这个确实是个空方法!因为onLayout()是为了确定视图在布局中所在的位置,而这个操作应该是由布局来完成的,即父View决定子View的显示位置。所以我们来看下ViewGroup中的onLayout()方法是怎么写的吧


ViewGroup中的onLayout()方法竟然是一个抽象方法,这就意味着所有ViewGroup的子类都必须重写这个方法。没错,像LinearLayout、RelativeLayout等布局,都是重写了这个方法,然后在内部按照各自的规则对子视图进行布局的,这些布局源码都比较复杂,有兴趣你可以去看看


onDraw()


measure和layout的过程都结束后,接下来就进入到draw的过程了,从名称上来看你可以得知这才是真正的绘制View的时候,老方法搜一下draw,一下子就出来了


总共有7个步骤,但是注释里写着跳过2和5步骤,搜了一下说是不常用....不过这是我见到的最言简意赅的一次了.....

步骤1:如果需要,绘制背景

步骤3:绘制内容

步骤4:绘制子View

步骤6:绘制滚动条等

步骤7:绘制默认焦点高亮

当你切进去看这些源码内容时, 你会发现View是不会帮我们绘制内容部分的,因此需要每个View根据想要展示的内容来自行绘制,绘制的方式主要还是通过Canvas类


总结


简单总结一下,View的绘制流程简单来说就是在屏幕上绘制一块矩形的区域,首先执行onMeasure测量View的大小,如果有缓存过的View就用缓存里的,没有就用measure重新测量,父View会给子View提供一个参考,程序员可以通过xml文件来主动设置View的大小,View的大小最终由View自身决定。执行完成后开始执行onLayout,确定好View的位置,而onLayout是个抽象方法必须要重写。最后执行onDraw来进行绘制,就是通过Canvas类来实现各种各样的绘制,但是View不会绘制内容部分,每个View需要根据自己想要的内容自行绘制。写到这里就算完篇了,有不少东西看的不是很懂,只能根据网上的东西猜到大概的意思。如果你觉得看完本篇还不满意的话,我建议你下个源码去读一读吧,说不准你能有不小的收获

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容