(内容来自于android开发艺术探索)
一、 ViewRoot和ViewRootImpl类
一个View的onMeasure,onDraw,onLayout过程,都是通过ViewRoot来完成的,它对应的类就是ViewRootImpl。当一个Activity创建完成后,会将DecorView(顶级View)添加到Window层中,同时会创建ViewRootImpl类和DecorView进行关联,通过performTraversals方法完成测量,布局,绘制。
DecorView:顶级view是装载setContentView的一个容器。
二、理解MeasureSpec 测量的尺寸规格
它包含两个部分 尺寸和规格(specSize和specModel)
1.specModel有三种类型
精确类型:Exactly:View的最终大小就是specSize的值,对应设置参数时,设置的具体值和match_parent。
最大类型:AT_MOST:父容器指定一个最大的值,设置值的时候不能超过这个值,对应参数warp_content。
任意类型:UNSPECIFIED
用户想设置多大就多打,不多做限制。一般不常用。
决定View大小的两个值
一个View的大小不光由MeasureSpec决定,还由LayoutParams决定。
ViewGroup的大小 由自身设定的MeasureSpec和Params(padding,margin,width/height)决定。
对于布局中的View来说,MeasureSpec由父容器传递过来,所以子View的测量规格由父容器的MeasureSpec和Params决定。
//spec:父容器的规则,padding:padding+margin,
//childDimension:子View的参数 childView.width。。。
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
int size = Math.max(0, specSize - padding);//父容器的剩余空间
//子View的最后宽高
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
// Parent has imposed an exact size on us
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent has imposed a maximum size on us
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// Child wants a specific size... so be it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} 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;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent asked to see how big we want to be
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// Child wants a specific size... let him have it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size... find out how big it should
// be
resultSize = 0;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size.... find out how
// big it should be
resultSize = 0;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
三、onMeasure的典型逻辑
onMeasure{
int specModel=MeasureSpec.getModel(measureSpec);
specSize=MeasureSpec.getModel(measureSpec);
switch(specModel){
case:MeasureSpec.AT_MOST:
//
break;
case:MeausreSpec.EXACTLY:
//
break;
}
}
1.直接继承View需要重现onMeasure方法,并且设置
warp_content时候的大小,否则在布局中使用时相当于match_parent
2.继承ViewGroup时getMeasureWidth/heigth
是测量的宽高,不够准确,如果想要得到准确的宽高,要在onLayout中获得。
如何在activity启动时同时获得View的宽高
1.在onWindowFocusChanged:这个方法代表View已经初始化完毕了
public void onWindowFocusChanged(boolean hasFocus){
if(hasFocus){
int width=view.getMeasuredWidth();
。。。。。。
}
}
2.view.post(runnable):还没有看懂- -。。。。
protect void onStarted(){
super.onStart();
view.post(new Runnable){
public void run(){
int width=view.getMeasureWidth();
int height=view.getMeasureHeigth();
}
}
}