之前的文章《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
完成对自己的测量。
关注我