源码阅读有感--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...

相关推荐
hepherd3 分钟前
音视频学习 - ffmpeg 编译与调试
程序员
小鬼难缠z20 分钟前
阿里云部署Hugo与备案实践
程序员·全栈
爱喝奶茶的企鹅29 分钟前
Ethan独立开发产品日报 | 2025-04-16
人工智能·程序员·开源
程序员麻城东6 小时前
如何看待一门新兴技术
程序员·开源
若梦plus7 小时前
异步编程思想
前端·javascript·程序员
无限大67 小时前
数据结构与算法入门 Day 0:程序世界的基石与密码
后端·算法·程序员
爱喝奶茶的企鹅19 小时前
Ethan独立开发产品日报 | 2025-04-15
人工智能·程序员·产品
PBitW1 天前
真的不知道谁对谁错!感觉都有问题,但是就是解决不了!
程序员
舒一笑1 天前
腾讯云cos大文件上传服务端实现一篇搞定
后端·程序员·腾讯
ssshooter1 天前
GPT-4.1 官方提示词指南,速来!
人工智能·chatgpt·程序员