布局流程
开发者意见->父view意见->自己去测量->父view拿到实际尺寸位置,调用layout方法
-
运行前,开发者在xml文件写入对view的要求
-
父view在自己的onMeasure中根据开发者在xml中写对子view的要求,和自己的可用空间得出对子view的要求
-
子view在自己的onMeasure中,根据自己的特性算出自己期望的尺寸
① 如果是ViewGroup,还会调用每个子view的measure进行测量
-
父view在子view计算出期望尺寸后,得出子view的实际尺寸和位置
-
子view在自己的layout方法中,将父view传进来的自己的实际尺寸和位置保存
① 如果是VIewGroup,还会在onLayout里面调用每个子view的layout把它们的尺寸位置传递给他们
具体开发中的使用
继承已有的view,简单改写尺寸
-
重写onMeasure
-
getMeasuredWidth和getMeasuredSize获取到测量出的尺寸
-
计算出最终需要的尺寸
-
setMeasureDimension(width,height)把结果保存
对自定义view完全进行自定义尺寸计算
-
重写onMeasure
-
计算出自己的尺寸
-
用resolveSize或则resolveSizeAndState修正结果
内部实现:
① 首先用MeasureSpec.getMode(measureSpec)和MeasureSpec.getSize(MeasureSpec)去出对自己的尺寸限制类型和限制尺寸
② 如果measureSpec的mode是EXACTLY,表示父view对子view的尺寸做出了精的限制,直接用measureSpec的size
③ 如果measureSpec的mode是AT_MOST,表示父view对子view的尺寸只限制了上限,要具体看情况
-
size不大于spec限制的size,便是没有超限,选用计算出的size
-
size大于spec限制的size,超限了,选用spec的size
-
mode是UNSPECIFIED,表示父view对子view没有任何尺寸限制,直接选用计算的size
-
-
使用setMeasuredDimension(width,height)保存结果
自定义layout:重写onMeasure和onLayout
-
重写onMeasure
① 遍历每个子view,用measureChildWidthMargins测量子view
-
需要重写generateLayoutParams并返回MarginLayoutParams才能使用measureChildWithMargins方法
-
有些子view需要重新测量 当换行时需要
-
测量完成后,得出子View的实际位置和尺寸,并暂时保存
① measureChildWidthMargins的内部实现
-
通过getChildMeasureSpec方法算出子view的widthMeasureSpec和heightMeasureSpec,然后调用child.measure方法让子view自我测量
-
getChildMeasureSpec方法内部是现实结合开发者设置的LayoutParams中的width和heigth与父view自己的可用空间,得出对子view的限制,并使用MeasureSpec.makeMeasureSpec来求得结果
② 测量出所有子view的位置和尺寸后,计算出自己的尺寸,并用setMeasuredDimension保存
-
-
-
重写onLayout
遍历每个子view,调用他们的layout方法来将位置和尺寸传给他们
杂谈
-
有些父view会对子view进行纠正,如constraintLayout就会强行纠正你自定view的长和宽
-
一般没人重写layout方法
-
onLayout负责递归对子view进行赋值,和layout方法不一样
-
测量的过程也可以换一种方法来理解
① 如果开发者写了具体值(例如 layout_width="24dp"),就不用再考虑父View 的剩余空间了,直接用 LayoutParams.width / height 来作为子 View的限制 size,而限制 mode 为 EXACTLY,因为冲突导致界面不正确,开发者可以通过修改 xml 文件来解决,所以开发者的意见是第一位
② 如果开发者写的是 MATCH_PARENT,即要求填满父控件的可用空间,那么由于自己的可用空间和自己的两个 MeasureSpec 有关,所以需要根据自己的 widthMeasureSpec 或 heightMeasureSpec 中的 mode 来分情况判断:
- 如果自己的 spec 中的 mode 是 EXACTLY 或者 AT_MOST,说明自己的尺⼨寸有上限,那么把 spec 中的 size 减去自己的已用宽度或高度,就是自己可以给子 View 的 size;至于 mode,就用 EXACTLY(注意:就算自己的 mode 是 AT_MOST,传给子 View 的也是EXACTLY);
- 如果自己的 spec 中的 mode 是 UNSPECIFIED,说明自己的尺⼨寸没有上限,那么让子 View 填满自己的可用空间就无从说起,因此选用退让方案:给子 View 限制的 mode 就设置为 UNSPECIFIED,size 写 0 就好;
③ 如果开发者写的是 WRAP_CONTENT,即要求子 View 在不超限制的前提下自我测量,那么同样由于自己的可用空间和自己的两个 MeasureSpec 有关,所以也需要根据自己的 widthMeasureSpec 和 heightMeasureSpec中的 mode 来分情况判断:
- 如果自己的 spec 中的 mode 是 EXACTLY 或者 AT_MOST,说明自己的尺寸有上限,那么把 spec 中的 size 减去自己的已用宽度或高度,就是自己可以给⼦子 View 的尺寸上限;至于 mode,就用AT_MOST;
- 如果自己的 spec 中的 mode 是 UNSPECIFIED,说明自己的尺⼨寸没有上限,那么也就不必限制子 View 的上限,因此给子 View 限制的mode 就设置为 UNSPECIFIED,size 写 0 就好。