首先一个view的绘制是由viewrootimpl中的performTraversals的方法开始执行measure,layout,draw三个方法。
具体的viewrootimpl是什么。看一段源码的注释,了解一下。
二个关键词:最高层级的view,view与windowmanager的纽带。
具体的方法调用流程如图所示:
1.onMeasure
1.1measurespec
在我们覆写了onMeasure方法后,我们会发现方法里有两个参数int widthMeasureSpec, int heightMeasureSpec
我们可以把它理解为规格,测量的规格。
MeasureSpec是代表一个32位的int值,高2位代表spec的mode,低30位代表size。
因此我们可以提前得知,MeasureSpec的mode最多有4种,那到底有几种呢?
1.2specmode 的三种模式
SpecMode一共有三种
1.Unspecified
表示父容器不对view有任何限制,要多大就多大,为所欲为
2.Exactly
顾名思义,父容器已经知道你的大小了,例如match parent,或者具体layoutparams中的数值。
3.atmost
父容器制定了一个specsize,view的大小不能超过这个值。例如,wrap-content。
1.3measurespec 与layoutparam的对应关系
可以这样理解,view的measurespec由父容器的measurespec和自身的layoutparam决定。有人会问,那decorview又没有父容器?decorview的父容器,可以理解成window,decorview的measurespec是由windowsize和自身layoutparam决定。
1.4 onMeasure
onMeasure源码很简单,就是调用了一下setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));复制代码
getSuggestedMinimumWidth的逻辑是,如果view没有设置背景,那么返回android:minWidth这个属性所指定的值,如果view设置了背景,那么返回android:minWidth和背景两者之间最大的。
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}复制代码
再来看下此方法,ATMOST 和EXACTLY,的size就直接等于父容器的measuresize。
因此如果直接继承view的自定义控件,不处理wrapcontent,那么效果和matchparent是一样的,都直接等于父容器的剩余大小。像Textview,imageview都特殊处理的wrapcontent。
1.5如何在activity中得到view的测量宽高
1.onWindowFocusChanged
2.view.post
3.ViewTreeObserver
4.view.measure()
这里要根据具体情况来判断。
1.如果view的layoutparam是matchparent,那么直接放弃,此时还不知道父容器的大小。
2.如果是具体数值,那么就easy多了,measure(数字,就是写在xml里的具体数值,MeasureSpec.Exactly)
3.如果是wrap-content ,可以view.measure(1<<30 - 1, MeasureSpec.ATMOST);
2.onLayout
onlayout方法的用途是viewgroup来设置子view的位置。view的onlayout方法会调用setframe来设置4个顶点的位置,这4个顶点的位置来自于viewgroup对子view的测量。
3.onDraw
ondraw其实没什么特别的内容,只要按照对应的顺序进行绘制。