自定义View的布局工作内容
测量阶段:从上到下递归地调用每个 View 或者 ViewGroup 的 measure() 方法,使他们的onMeasure工作,测量他们的尺寸并计算它们的位置。
布局阶段:从上到下递归地调用每个 View 或者 ViewGroup 的 layout() 方法,使他们的onLayout工作,把测得的它们的尺寸和位置赋值给它们。
view与ViewGroup的测量方式不一样
View的测量
修改尺寸
View 在 onMeasure() 中会修改自己的尺寸然后保存;
super.onMeasure系统帮助计算一次宽高
自定义正方形图片,在onMeasure里面计算并且修改自己的尺寸并且调用setMeasuredDimension保存新的宽高
总结成三步
1 重写 onMeasure() 方法,并在里面调用 super.onMeasure(),触发原有的自我测量;
2 在 super.onMeasure() 的下面用 getMeasuredWidth() 和 getMeasuredHeight() 来获取到之前的测量结果,并使用自己的算法,根据测量结果计算出新的结果;
3 调用 setMeasuredDimension() 来保存新的结果。
完全自己测量尺寸
注意
这里就不用调用super.onMeasure();
在父View计算子View的尺寸时结果要满足父 View 给出的的尺寸限制
限制的分类:
1 UNSPECIFIED:不限制
2 AT_MOST:限制上限
3 EXACTLY:限制固定值
这个限制来自于onMeasure方法的两个参数
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int measureWidth=200;
int measureHeight=300;
//resolveSize 系统根据子View的需求和子View的限定来计算
// resolveSize(measureWidth,widthMeasureSpec);
measureWidth=reSize(measureWidth,widthMeasureSpec);
measureHeight=reSize(measureHeight,heightMeasureSpec);
Log.e("yzh","width--"+measureWidth+"---heigt--"+measureHeight);
setMeasuredDimension(measureWidth,measureHeight);
}
reSize是我们根据View的需求和View的限定来自己计算的一个方法
public int reSize(int size,int measureSpec){
int specMode =MeasureSpec.getMode(measureSpec);
int specSize=MeasureSpec.getSize(measureSpec);
switch (specMode){
case MeasureSpec.UNSPECIFIED:
Log.e("yzh","MeasureSpec.UNSPECIFIED");
return size;
case MeasureSpec.AT_MOST:
Log.e("yzh","MeasureSpec.AT_MOST"+specSize);
if(size<=specSize){
return size;
}else{
return specSize;
}
case MeasureSpec.EXACTLY:
Log.e("yzh","MeasureSpec.EXACTLY"+specSize);
return specSize;
default:
Log.e("yzh","default");
return size;
}
}
做了几组测试(xml中是一个linearlayout 包含了我们的view)
// LinearLayout--match_parent,match_parent view_wrap_content,wrap_contentwrap 限制上限 view要自行计算宽高
// 03-20 10:34:54.711 3409-3409/com.example.issuser.rxtest E/yzh: MeasureSpec.AT_MOST1080
//03-20 10:34:54.711 3409-3409/com.example.issuser.rxtest E/yzh: MeasureSpec.AT_MOST1776
//03-20 10:34:54.711 3409-3409/com.example.issuser.rxtest E/yzh: width--200---heigt--300
/ linearlayout 200 match view wrap 限制上限 view要自行计算宽高
// 03-20 10:38:23.477 4108-4108/com.example.issuser.rxtest E/yzh: MeasureSpec.AT_MOST600
//03-20 10:38:23.477 4108-4108/com.example.issuser.rxtest E/yzh: MeasureSpec.AT_MOST1776
//03-20 10:38:23.477 4108-4108/com.example.issuser.rxtest E/yzh: width--200---heigt--300
//LinearLayout -match view_50 xml有准确的诉求 以这个诉求优先 不用重新计算
//03-20 10:32:37.489 1230-1230/com.example.issuser.rxtest E/yzh: MeasureSpec.EXACTLY150
//03-20 10:32:37.489 1230-1230/com.example.issuser.rxtest E/yzh: MeasureSpec.EXACTLY150
//03-20 10:32:37.489 1230-1230/com.example.issuser.rxtest E/yzh: width--150---heigt--150
//LinearLayout wrap view 50 xml有准确的诉求 以这个诉求优先 不用重新计算
// 03-20 11:12:15.638 10778-10778/com.example.issuser.rxtest E/yzh: MeasureSpec.EXACTLY150
//03-20 11:12:15.638 10778-10778/com.example.issuser.rxtest E/yzh: MeasureSpec.EXACTLY150
//03-20 11:12:15.638 10778-10778/com.example.issuser.rxtest E/yzh: width--150---heigt--150
// Linearlayout 200 match view——match
///yzh: MeasureSpec.EXACTLY600
//03-20 10:40:30.828 4693-4693/com.example.issuser.rxtest E/yzh: MeasureSpec.EXACTLY1776
//03-20 10:40:30.828 4693-4693/com.example.issuser.rxtest E/yzh: width--600---heigt--1776
// LinearLayout 200 wrap view match 限制上限 view要自行计算宽高
// 03-20 10:41:01.413 4988-4988/? E/yzh: MeasureSpec.EXACTLY600
//03-20 10:41:01.413 4988-4988/? E/yzh: MeasureSpec.AT_MOST1776
//03-20 10:41:01.413 4988-4988/? E/yzh: width--600---heigt--300
总结
EXACTLY:表示设置了精确的值,一般当childView设置其宽、高为精确值、match_parent时,ViewGroup会将其设置为EXACTLY;
AT_MOST:表示子布局被限制在一个最大值内,一般当childView设置其宽、高为wrap_content时,ViewGroup会将其设置为AT_MOST;
ViewGroup的测量
ViewGroup 在 onMeasure() 中会调用所有子 View 的 measure() 让它们进行自我测量(onMeasure),并根据子 View 计算出的期望尺寸来计算出它们的实际尺寸和位置(会以子 View 给出的期望尺寸来优先作为实际尺寸)然后保存。同时,它也会根据子 View 的尺寸和位置来计算出自己的尺寸然后保存;
最后计算viewGroup的宽高
/**
* 如果是wrap_content设置为我们计算的值
* 否则:直接设置为父容器计算的值
*/
setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? sizeWidth
: width, (heightMode == MeasureSpec.EXACTLY) ? sizeHeight
: height);