Android view 绘制简述

实际开发过程中会遇到很多自定义的view 或者viewgroup ,要怎么去实现view的自定义,我们需要处理哪些事情,要了解这些 我必须知道 view的工作原理,view是怎么绘制的

首先onMeasure 

1.现看下这个方法onMeasure(int widthMeasureSpec, int heightMeasureSpec) ,这个方法是用来测量控件大小的,也是可以用来控制控件大小 。有两个参数widthMeasureSpec,heightMeasureSpec。这两个参数表示宽度和高度的测量规格,在源码里面可以看到在view实现的是


接着看这个方法 getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec)


可以很清楚的看到 通过一个widthMeasureSpec 参数可以获取两个值 一个是specSize 一个是specMode


从这里可以看出来widthMeasureSpec是一个组合参数, MeasureSpec中的值是一个整型(32位)将size和mode打包成一个Int型,其中高两位是mode,后面30位存的是size,是为了减少对象的分配开支。MeasureSpec 类似于下图,只不过这边用的是十进制的数,而MeasureSpec 是二进制存储的。

现在我们来看下MeasureSpec的模式 ,一共有三种模式

UPSPECIFIED : 父容器对于子容器没有任何限制,子容器想要多大就多大

EXACTLY: 父容器已经为子容器设置了尺寸,子容器应当服从这些边界,不论子容器想要多大的空间。

AT_MOST:子容器可以是声明大小内的任意大小

这个是官方的文档说明,看完也不是很清楚 ,先看看这两个参数是从哪里来的,我们都知道view是不会独立存在的 ,先简单了解下android 视图架构

所以onMeasure(int widthMeasureSpec, int heightMeasureSpec) 这个方法的参数肯定是父类里传过来的 ,那么这个参数的值是有什么决定的呢,看上图最终肯定是DecorView 处理的 ,涉及DecorView 就要看下ViewRoot的源码了(这个不做详细解释 ,DecorView 和 ViewRoot关系不一般),我截了别人帖子里的图大概说明下 

链接: 传送门

现在我们来看下widthMeasureSpec 是怎么赋值的  看图 




这里是初始化MeasureSpec的地方 在看怎么赋值的 


看这这个方法 就知道了 我们在写布局文件的是 view的 layout_width ,layout_height 的属性对应的MeasureSpec的模式了 ,系统就给了两种 ,还有一个是给开发者自己用的 

现在总结下:

1、如果子View 的layout_xxxx是MATCH_PARENT,父View的大小是确切,子View的大小又MATCH_PARENT(充满整个父View),那么子View的大小肯定是确切的,而且大小值就是父View的size。所以子View的size=父View的size,mode=EXACTLY

2、如果子View 的layout_xxxx是WRAP_CONTENT,也就是子View的大小是根据自己的content 来决定的,但是子View的毕竟是子View,大小不能超过父View的大小,但是子View的是WRAP_CONTENT,我们还不知道具体子View的大小是多少,要等到child.measure(childWidthMeasureSpec, childHeightMeasureSpec) 调用的时候才去真正测量子View 自己content的大小(比如TextView wrap_content 的时候你要测量TextView content 的大小,也就是字符占用的大小,这个测量就是在child.measure(childWidthMeasureSpec, childHeightMeasureSpec)的时候,才能测出字符的大小,MeasureSpec 的意思就是假设你字符100px,但是MeasureSpec 要求最大的只能50px,这时候就要截掉了)。通过上述描述,子View MeasureSpec mode的应该是AT_MOST,而size 暂定父View的 size,表示的意思就是子View的大小没有不确切的值,子View的大小最大为父View的大小,不能超过父View的大小(这就是AT_MOST 的意思),然后这个MeasureSpec 做为子View measure方法 的参数,做为子View的大小的约束或者说是要求,有了这个MeasureSpec子View再实现自己的测量。

3、如果如果子View 的layout_xxxx是确定的值(200dp),那么就更简单了,不管你父View的mode和size是什么,我都写死了就是200dp,那么控件最后展示就是就是200dp,不管我的父View有多大,也不管我自己的content 有多大,反正我就是这么大,所以这种情况MeasureSpec 的mode = EXACTLY 大小size=你在layout_xxxx 填的那个值。

4、如果子View 的layout_xxxx是MATCH_PARENT,父View的大小是不确定(只知道最大只能多大),子View的大小MATCH_PARENT(充满整个父View),那么子View你即使充满父容器,你的大小也是不确定的,父View自己都确定不了自己的大小,你MATCH_PARENT你的大小肯定也不能确定的,所以子View的mode=AT_MOST,size=父View的size,也就是你在布局虽然写的是MATCH_PARENT,但是由于你的父容器自己的大小不确定,导致子View的大小也不确定,只知道最大就是父View的大小。

5、如果子View 的layout_xxxx是WRAP_CONTENT,父View的大小是不确定(只知道最大只能多大),子View又是WRAP_CONTENT,那么在子View的Content没算出大小之前,子View的大小最大就是父View的大小,所以子View MeasureSpec mode的就是AT_MOST,而size 暂定父View的 size。

(以上总结,不是我总结的,传送门)


layout方法 


performTraversals 方法执行完mView.measure 计算出mMeasuredXXX后就开始执行layout 函数来确定View具体放在哪个位置,我们计算出来的View目前只知道view矩阵的大小,具体这个矩阵放在哪里,这就是layout 的工作了。layout的主要作用 :根据子视图的大小以及布局参数将View树放到合适的位置上。

既然是通过mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight()); 那我们来看下layout 函数做了什么,mView肯定是个ViewGroup,不会是View,我们直接看下ViewGroup 的layout函数


1、setFrame(l, t, r, b) 可以理解为给mLeft 、mTop、mRight、mBottom赋值,然后基本就能确定View自己在父视图的位置了,这几个值构成的矩形区域就是该View显示的位置,这里的具体位置都是相对与父视图的位置。

2、回调onLayout,对于View来说,onLayout只是一个空实现,一般情况下我们也不需要重载该函数,:

对于ViewGroup 来说,唯一的差别就是ViewGroup中多了关键字abstract的修饰,要求其子类必须重载onLayout函数。

我们在重载onLayout函数的时候要求对ViewGroup 里面的子view 进行布局代码随意 


代码很简单 就是根据自己的需求进行布局就好了 

MeasuredWidth和MeasuredHeight这两个参数为layout过程提供了一个很重要的依据(如果不知道View的大小,你怎么固定四个点的位置呢),但是这两个参数也不是必须的,layout过程中的4个参数l, t, r, b完全可以由我们任意指定,而View的最终的布局位置和大小(mRight - mLeft=实际宽或者mBottom-mTop=实际高)完全由这4个参数决定,measure过程得到的mMeasuredWidth和mMeasuredHeight提供了视图大小测量的值,但我们完全可以不使用这两个值,所以measure过程并不是必须的。如果我们不使用这两个值,那么getMeasuredWidth() 和getWidth() 就很有可能不是同一个值,它们的计算是不一样的


onDraw()

performTraversals 方法的下一步就是mView.draw(canvas); 因为View的draw 方法一般不去重写,官网文档也建议不要去重写draw 方法,所以下一步执行就是View.java的draw 方法,这里不在贴源码了 说下大概的过程:


1.背景绘制 

 2.skip  

3.对View的内容进行绘制 

onDraw(canvas) 方法是view用来draw 自己的,具体如何绘制,颜色线条什么样式就需要子View自己去实现,View.java 的onDraw(canvas) 是空实现,ViewGroup 也没有实现,每个View的内容是各不相同的,所以需要由子类去实现具体逻辑。

4 .对当前View的所有子View进行绘制 

就是遍历子View然后drawChild(),drawChild()方法实际调用的是子View.draw()方法,ViewGroup类已经为我们实现绘制子View的默认过程,这个实现基本能满足大部分需求,所以ViewGroup类的子类(LinearLayout,FrameLayout)也基本没有去重写dispatchDraw方法,我们在实现自定义控件,除非比较特别,不然一般也不需要去重写它, drawChild()的核心过程就是为子视图分配合适的cavas剪切区,剪切区的大小正是由layout过程决定的,而剪切区的位置取决于滚动值以及子视图当前的动画。设置完剪切区后就会调用子视图的draw()函数进行具体的绘制了

5.skip  

6. 对View的滚动条进行绘制

字面意思 


这里面少一个ViewGroup  onMeasure 的分析 

简单说下,ViewGroup遍历自己的子View,只要不是GONE的都会参与测量,基本思想就是父View把自己的MeasureSpec  传给子View结合子View自己的LayoutParams 算出子View 的MeasureSpec,然后继续往下传,传递叶子节点,叶子节点没有子View,根据传下来的这个MeasureSpec测量自己就好了。

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

推荐阅读更多精彩内容