一、View的测量过程
1、MeasureSpace :封装了父View传递到子View的布局要求。是由size(大小) 和modle(模式组成),有三种模式:
- UNSPECIFIED:
父视图不对子视图施加任何限制,子视图可以得到任意想要的大小;
- EXACTLY:
父视图希望子视图的大小是specSize中指定的大小;
- AT_MOST:
子视图的大小最多是specSize中的大小。
2、measure(int widthMeasureSpec, int heightMeasureSpec):当父View对子view进行测量时,会调用的方法。两个入参分别代表对子View的限制。
-
1)先判断是否需要进行测量: View.measure()方法时View并不是立即就去测量,而是先判断一下是否有必要进行测量操作,如果不是强制测量或者MeasureSpec与上次的MeasureSpec相同的时候,那么View就不需要重新测量了.
-
2)从缓存中读取是否测量过: 如果不满足上面条件,View就考虑去做测量工作了.但在测量之前,View还想偷懒,如果能在缓存中找到上次的测量结果,那直接从缓存中获取就可以了。它会以MeasureSpec计算出的key值作为键,去成员变量mMeasureCache中查找是否缓存过对应key的测量结果,
-
3)onMeasure()方法进行测量: 如果不能从mMeasureCache中读到缓存过的测量结果,只能乖乖地调用onMeasure()方法去完成实际的测量工作,并且将尺寸限制条件widthMeasureSpec和heightMeasureSpec传递给onMeasure()方法。
-
4)保存测量结果: 最终View都会得到测量的结果,并且将结果保存到mMeasuredWidth和mMeasuredHeight这两个成员变量中,同时缓存到成员变量mMeasureCache中,以便下次执行measure()方法时能够从其中读取缓存值
3、onMeasure()
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
4、setMeasuredDimension()
- 1)通过setMeasuredDimensionRaw() ,把测量完的宽高值赋值给mMeasuredWidth、mMeasuredHeight
二、
1、ViewGroup怎么知道他的子View是多大呢?View提供了以下三组方法:
- getMeasuredWidth()和getMeasuredHeight()
- getMeasuredWidthAndState()和getMeasuredHeightAndState()
- getMeasuredState()
2、mMeasuredWidth是一个Int类型的值,其是由4个字节组成的。
- 其高位的第一个字节为第一部分,用于标记测量完的尺寸是不是达到了View想要的宽度,我们称该信息为测量的state信息。
- 其低位的三个字节为第二部分,用于存储测量到的宽度。
- 有点类似于measureSpec
3、resolveSizeAndState()
- 1)这个方法的代码结构跟前文提到的getDefaultSize()方法很相似。
三、ViewGroup的measure过程
1、对于ViewGroup来说,除了完成自己的measure过程,还会遍历去调用所有子元素的measure()方法,各个子元素再递归去执行这个过程
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
final int size = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < size; ++i) {
final View child = children[i];
//遍历每个子元素,如果该子元素不是GONE的话,就去测量该子元素
if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
}
2、对每个子view 再次进行测量
protected void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
//获取child自身的LayoutParams属性
final LayoutParams lp = child.getLayoutParams();
//根据父布局的MeasureSpec,父布局的padding和child的LayoutParams这三个参数,通过getChildMeasureSpec()方法计算出子元素的MeasureSpec
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom, lp.height);
//调用measure()方法测量child,前文已经解释过这个方法,
//调用该方法之后会将view的宽高值保存在mMeasuredWidth和mMeasuredHeight这两个属性当中,这样child的尺寸就已经测量出来了
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
measureChild()的思想就是取出子元素的LayoutParams,然后再通过getChildMeasureSpec()方法来创建子元素的MeasureSpec,接着将MeasureSpec传给View的measure()方法来完成对子元素的测量。
3、getChildMeasureSpec(int spec, int padding, int childDimension)
- getChildMeasureSpec()这个方法清楚展示了普通View的MeasureSpec的创建规则,每个View的MeasureSpec状态量由其直接父View的MeasureSpec和View自身的属性LayoutParams(LayoutParams有宽高尺寸值等信息)共同决定。
4、MeasureSpec状态后,将其与尺寸值通过makeMeasureSpec(int size,int mode)方法结合在一起,就是最终传给View的onMeasure(int, int)方法的MeasureSpec值了。
5、ViewRootImpl是连接WindowManager和DecorView的纽带,控件的测量、布局、绘制以及输入事件的分发处理都由ViewRootImpl触发。
performTraversals()它调用了一个performTraversals()方法使得View树开始三大工作流程
private void performTraversals() {
...
if (!mStopped || mReportNextDraw) {
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
}
}
if (didLayout) {
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
...
}
if (!cancelDraw && !newSurface) {
performDraw();
}
...
}
6、performTraversals() 通过调用performMeasure()、performLayout()、performDraw()这三个方法,这三个方法分别完成DecorView的measure、layout、和draw这三大流程,其中performMeasure()中会调用measure()方法,在measure()方法中又会调用onMeasure()方法,在onMeasure()方法中会对所有子元素进行measure过程,这个时候measure流程就从父容器传递到子元素中了,这样就完成了一次measure过程。
7、树是遍历顺序,这意味着父View将被先绘制,子View视图后被绘制。
8、在measure过程中,通过 widthMeasureSpec() heightMeasureSpec 以及View自身的LayoutParams共同决定子视图的测量规格;
-
1)MeasureSpec :父View对子View的测量模式、测量大小的要求。
-
2)子View 视图通过LayoutParams,这个类并告诉父View 视图他们应该怎样被测量和放置。
-
3)它的尺寸可以有三种表示方法:
1、具体数值 2、FILL_PARENT 3、WRAP_CONTENT
9、对于不同的ViewGroup的子类,有着各自不同的LayoutParams。
10、getMeasuredWidth()、getMeasuredHeight()返回的是measure过程得到的mMeasuredWidth和mMeasuredHeight的值,而getWidth()和getHeight()返回的是mRight - mLeft和mBottom - mTop的值。
public final int getMeasuredWidth() {
return mMeasuredWidth & MEASURED_SIZE_MASK;
}
public final int getWidth() {
return mRight - mLeft;
}
11、draw方法步骤:绘制背景---------绘制自己--------绘制chrildren----绘制装饰。