源码阅读有感--View的测量and布局

熟悉view的测量与布局是自定义view的基础。

网上关于这部分的源码解析已经有很多了,本篇只当作一篇观后感。

测量--measure

测量就是量出view长宽,height和width。
子view的长宽依赖于父view的决策。 这一点是明显的,我们屏幕的尺寸是固定的,是往里面填view的,所以最上层最外面的父view的大小肯定是固定的,没记错的话是由window的layoutParam设置。

父view的决策在代码里就是两个spec,heightSpec和widthSpec。这两个参数会在view的递归测量的过程中一层层传递,由父view传递给子view的onMeasure函数。

那传递给子View的onMeasure函数干什么呢?

当然是决策,这两个参数就相当于父View告诉子View,父view是这样的,能给出的规格也是这样的(待遇),在子view的onMeasure中,获取子view的layoutParam,相当于子View向父view申请的待遇。在代码getChildMeasureSpec里面的呈现就是一块switch代码块,判断出各种情况,最后综合出一个结果(这部分求子view的spec的代码也可以自己自定义,getChildMeasureSpec是一个现成的方法)。

java 复制代码
  case MeasureSpec.EXACTLY:  
       //1.1、子View的width或height是个精确值 (an exactly size) 
       if (childDimension >= 0) {           
           resultSize = childDimension;         //size为精确值 
           resultMode = MeasureSpec.EXACTLY;    //mode为 EXACTLY 。 
      }  
       //1.2、子View的width或height为 MATCH_PARENT/FILL_PARENT  
       else if (childDimension == LayoutParams.MATCH_PARENT) { 
           // Child wants to be our size. So be it. 
           resultSize = size;                   //size为父视图大小 
           resultMode = MeasureSpec.EXACTLY;    //mode为 EXACTLY 。 
      }  
       //1.3、子View的width或height为 WRAP_CONTENT 
       else if (childDimension == LayoutParams.WRAP_CONTENT) { 
           // Child wants to determine its own size. It can't be 
           // bigger than us. 
           resultSize = size;                   //size为父视图大小 
           resultMode = MeasureSpec.AT_MOST;    //mode为AT_MOST 。 
      }
      ---------------------------------------------------
      
   case MeasureSpec.AT_MOST: 
       //2.1、子View的width或height是个精确值 (an exactly size) 
       if (childDimension >= 0) { 
           // Child wants a specific size... so be it 
           resultSize = childDimension;        //size为精确值 
           resultMode = MeasureSpec.EXACTLY;   //mode为 EXACTLY 。 
      } 
       //2.2、子View的width或height为 MATCH_PARENT/FILL_PARENT 
       else if (childDimension == LayoutParams.MATCH_PARENT) { 
           // Child wants to be our size, but our size is not fixed. 
           // Constrain child to not be bigger than us. 
           resultSize = size;                  //size为父视图大小 
           resultMode = MeasureSpec.AT_MOST;   //mode为AT_MOST 
      } 
       //2.3、子View的width或height为 WRAP_CONTENT 
       else if (childDimension == LayoutParams.WRAP_CONTENT) { 
           // Child wants to determine its own size. It can't be 
           // bigger than us. 
           resultSize = size;                  //size为父视图大小 
           resultMode = MeasureSpec.AT_MOST;   //mode为AT_MOST 
      }

例如case为EXACTLY的情况:这是一种比较好理解的情况,父view的mode是EXACTLY意味着,父view的某个维度(dimension)是准确的一个px,例如size为500px。也就是说父view就只能给子view最多500px的待遇了,具体多少还要看子view的申请了。这个申请就是childDimension,包括了layoutParam的三种情况,准确值、match、warp。在权衡之下,最终给子view创造新的spec规格。

根据不同的自定义view,measure有不同的计算形式和计算过程,但都包含根据子view的layoutParam和父view的spec得到属于子view的spec和子view的尺寸大小并保存起来,在measureHeight和measureWidth中。 整个流程的伪代码如下:

java 复制代码
parent.onMeasure(widthSpec, heightSpec) {
    1、根据widthSpec, heightSpec和parent.layoutParam判断获取并返回parent.spec(即getChildMeasureSpec(xxxx))
    2、for循环遍历parent所有的child
           child.measure(parent.widthSpec, parent.heightSpec)
    3、计算parent的尺寸,这一步根据自定义View的需求自行编写怎么计算尺寸, 并保存setMeasuredDimension
}
-------------
measure() {
    xxxxx
    onMeasure()
    xxxxx
}

举个例子:三层嵌套的view布局,父view和子view都是viewGroup,孙view不是viewGroup只是个view,父view的layout_width为500px且specMode为EXACTLY,子view的layout_width为warp_content,孙view的layout_width依旧为warp_content,那么这种情况measure后每个view测量后的width会变成多少?

父view由于宽度给的是准确值(size==500)且specMode为EXACTLY,子view调用getChildMeasureSpec计算子view的spec,会将子view的spec.size赋值为500px,spec.mode为AT_MOST。孙view因为已经是最底层的view了,是可以分为两种情况的,一种是view.java,没有重写过onMeasure,其他的就是重写过onMeasure,例如textView.java

1、如果孙view是view.java,那么孙view的width就为500px,mode自然不会设定了,spec的出现是为了约束子view的,都到最底层了,自然就不需要spec。

2、如果孙view是textView.java,那么孙view的width就会在重写的onMeasure中设定,textView是取text字符的总宽度为孙view的width

上述的过程只是递归到最底层的孙view时,分情况讨论而已,此时还需要从最底层递归上去,完成每一层view的最后一步,计算每一层的尺寸,这一步根据自定义View的需求自行编写怎么计算尺寸,反正孙view的大小都明确了,只要符合本身的spec要求就行了最终setMeasuredDimension保存。假设,孙view的宽度确定为100px,已知子view的spec.size赋值为500px,spec.mode为AT_MOST,意思是子view最大可以500px,那么设置400px行不行?肯定可以。

布局--layout

layout的意思就是把view放在准确的位置,layout函数有四个参数,top、bottom、left、right。

layout函数的伪代码和measure相近,如下

java 复制代码
layout() {
    xxxx.setFrame()
    onLayout()
}
------------------
onLayout() {
int childCount = getChildCount() ;
for(int i=0 ;i<childCount ;i++){
     View child = getChildAt(i) ;
     //整个layout()过程就是个递归过程
     child.layout(l, t, r, b) 
}

layout的流程就不过多赘述了,整体来说比measure要简单清晰些。

1、layout能改view的大小吗?

能,在layout的setFrame中会给view的成员变量top、bottom、left、right赋值。这四个参数代表着矩形view的左上角和右下角坐标,当然也就控制着大小。

2、只重写onlayout并在其中修改view的大小会出现问题吗?

可能会,因为onlayout中修改的值只会通知到当前view,对于他的父view是不知道这个改变的,可能会引起一些布局上的bug

深度好文: juejin.cn/post/696243...

相关推荐
我是陈泽20 小时前
一行 Python 代码能实现什么丧心病狂的功能?圣诞树源代码
开发语言·python·程序员·编程·python教程·python学习·python教学
肖哥弹架构2 天前
Spring 全家桶使用教程
java·后端·程序员
IT杨秀才5 天前
自己动手写了一个协程池
后端·程序员·go
程序员麻辣烫7 天前
像AI一样思考
程序员
一颗苹果OMG8 天前
关于进游戏公司实习的第一周
前端·程序员
万少8 天前
你会了吗 HarmonyOS Next 项目级别的注释规范
前端·程序员·harmonyos
楽码9 天前
彻底理解时间?在编程中使用原子钟
后端·算法·程序员
江南一点雨10 天前
又一家培训机构即将倒闭!打工人讨薪无果,想报名的小伙伴擦亮眼睛~
java·程序员
用户861782773651810 天前
ELK 搭建 & 日志集成
java·后端·程序员
河北小田10 天前
局部变量成员变量、引用类型、this、static
java·后端·程序员