之前的文章《View体系(八)深入剖析View的onMeasure方法》 我们深入分析了View的onMeasure方法,我们今天就来看一下ViewGroup的测量流程。
(注:文中源码基于 Android 12)
在View做测量时,会调用View的onMeasure方法,但是我们翻看ViewGroup的源码,并没有发现onMeasure方法,难道ViewGroup不用测量?显然不是,我们换一个类来看,LinearLayout继承自ViewGroup,我们从LinearLayout的源码中找到了熟悉的onMeasure方法,我们看一下源码:
1 2 3 4 5 6 7 8 9 10 11 public class LinearLayout extends ViewGroup { ... @Override protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) { if (mOrientation == VERTICAL) { measureVertical(widthMeasureSpec, heightMeasureSpec); } else { measureHorizontal(widthMeasureSpec, heightMeasureSpec); } } ...
注释1处根据mOrientation判断是横向排列还是纵向排列来进入不同的测量流程,我们以纵向排列为例,看LinearLayout是如何测量其高度的,进入注释2处的measureVertical方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void measureVertical (int widthMeasureSpec, int heightMeasureSpec) { ... mTotalLength = 0 ; final int count = getVirtualChildCount(); ... for (int i = 0 ; i < count; ++i) { final int usedHeight = totalWeight == 0 ? mTotalLength : 0 ; if (heightMode == MeasureSpec.EXACTLY && useExcessSpace) { final int totalLength = mTotalLength; mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin); skippedMeasure = true ; } else { final int usedHeight = totalWeight == 0 ? mTotalLength : 0 ; measureChildBeforeLayout(child, i, widthMeasureSpec, 0 , heightMeasureSpec, usedHeight); final int childHeight = child.getMeasuredHeight(); if (useExcessSpace) { lp.height = 0 ; consumedExcessSpace += childHeight; } final int totalLength = mTotalLength; mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
由于measureVertical方法很长,这里只贴出部分关键代码。注释1处获取子View的数量,注释2处遍历子View并根据测量模式进入不同的if-else分支,这里以wrap_content为例(即AT_MOST模式),则进入else分支,注释3处对子View进行测量,注释4处获取测量到的子View的高度,注释5处对子View的测量结果进行累加作为最终LinearLayout的测量高度。我们进入注释3的measureChildBeforeLayout方法:
1 2 3 4 5 6 void measureChildBeforeLayout (View child, int childIndex, int widthMeasureSpec, int totalWidth, int heightMeasureSpec, int totalHeight) { measureChildWithMargins(child, widthMeasureSpec, totalWidth, heightMeasureSpec, totalHeight); }
再进入measureChildWithMargins方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 protected void measureChildWithMargins (View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
注释1处调用了child.measure方法:
1 2 3 public final void measure (int widthMeasureSpec, int heightMeasureSpec) { ... onMeasure(widthMeasureSpec, heightMeasureSpec);
而measure又调用了View的onMeasure方法,对于View的onMeasure方法,之前的文章《View体系(八)深入剖析View的onMeasure方法》 已经讲过,这里不再赘述。
至此LinearLayout的测量已经很清楚了,即对于纵向的LinearLayout并指定高度为wrap_content来说,是通过遍历测量子View的高度并累加作为自己的高度。至于为什么ViewGroup没有onMeasure方法,是因为它无法统一究竟要以什么方式来测量,所以它要让它的子类来各自实现测量方法,最终它的子类又重写onMeasure完成对自己的测量。
关注我