- 初始化app后DecorView调用performTraversals(),执行完performMeasure后开始执行
performLayout(lp, mWidth, mHeight); - performLayout里面调用了 view的layout方法
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); - 先看viewGroup的处理,里面包含两个函数:
- layout调用了view.layout
super.layout(l, t, r, b) - onLayout
abstract方法,需要子类实现
- layout调用了view.layout
- 所有的方法最终都是调用了view的layout,接下来看view的layout方法
onLayout(changed, l, t, r, b)
调用用了onLayout,而onLayout为空方法。 - 到这里layout执行完了,没有实现功能,说明layout的计算是在具体的view里面实现的
- 接下来分别看一下TextView和FrameLayout的onLayout方法
- TextView
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (mDeferScroll >= 0) { int curs = mDeferScroll; mDeferScroll = -1; bringPointIntoView(Math.min(curs, mText.length())); } }
很简单,直接交给了super即view进行处理,原因很简单:对于单个view(没有子view)来说,只要按照父view传给的ltrb(left、top、right、bottom layout的四个参数)进行处理就好了,并不需要自己本身去计算 - FrameLayout
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) { final int count = getChildCount(); final int parentLeft = getPaddingLeftWithForeground(); final int parentRight = right - left - getPaddingRightWithForeground(); final int parentTop = getPaddingTopWithForeground(); final int parentBottom = bottom - top - getPaddingBottomWithForeground(); for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); final int width = child.getMeasuredWidth(); final int height = child.getMeasuredHeight(); int childLeft; int childTop; int gravity = lp.gravity; if (gravity == -1) { gravity = DEFAULT_CHILD_GRAVITY; } final int layoutDirection = getLayoutDirection(); final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.CENTER_HORIZONTAL: childLeft = parentLeft + (parentRight - parentLeft - width) / 2 + lp.leftMargin - lp.rightMargin; break; case Gravity.RIGHT: if (!forceLeftGravity) { childLeft = parentRight - width - lp.rightMargin; break; } case Gravity.LEFT: default: childLeft = parentLeft + lp.leftMargin; } switch (verticalGravity) { case Gravity.TOP: childTop = parentTop + lp.topMargin; break; case Gravity.CENTER_VERTICAL: childTop = parentTop + (parentBottom - parentTop - height) / 2 + lp.topMargin - lp.bottomMargin; break; case Gravity.BOTTOM: childTop = parentBottom - height - lp.bottomMargin; break; default: childTop = parentTop + lp.topMargin; } child.layout(childLeft, childTop, childLeft + width, childTop + height); } } }
- 逻辑很清楚,遍历child,获得child的measureWidth和measureHeight和layoutparam,根据gravity计算childLeft和childTop,另外两个参数直接通过width和height计算获得。
- 最后调用child.layout进行处理
- 不同的viewgrouop由于自身不同的规则会在onLayout进行处理,这里的fragmeLayout比较简单,没有考虑同一个viewGroup之下的子view之间的互相影响,下面看一下LinearLayout
- LinearLayout
- @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (mOrientation == VERTICAL) {
layoutVertical(l, t, r, b);
} else {
layoutHorizontal(l, t, r, b);
}
} - 首先根据orientation进行区分,没毛病
- 看一下 layoutHorizontal
void layoutHorizontal(int left, int top, int right, int bottom) { final boolean isLayoutRtl = isLayoutRtl(); final int paddingTop = mPaddingTop; int childTop; int childLeft; // Where bottom of child should go final int height = bottom - top; int childBottom = height - mPaddingBottom; // Space available for child int childSpace = height - paddingTop - mPaddingBottom; final int count = getVirtualChildCount(); final int majorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; final boolean baselineAligned = mBaselineAligned; final int[] maxAscent = mMaxAscent; final int[] maxDescent = mMaxDescent; final int layoutDirection = getLayoutDirection(); switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) { case Gravity.RIGHT: // mTotalLength contains the padding already childLeft = mPaddingLeft + right - left - mTotalLength; break; case Gravity.CENTER_HORIZONTAL: // mTotalLength contains the padding already childLeft = mPaddingLeft + (right - left - mTotalLength) / 2; break; case Gravity.LEFT: default: childLeft = mPaddingLeft; break; } int start = 0; int dir = 1; //In case of RTL, start drawing from the last child. if (isLayoutRtl) { start = count - 1; dir = -1; } for (int i = 0; i < count; i++) { final int childIndex = start + dir * i; final View child = getVirtualChildAt(childIndex); if (child == null) { childLeft += measureNullChild(childIndex); } else if (child.getVisibility() != GONE) { final int childWidth = child.getMeasuredWidth(); final int childHeight = child.getMeasuredHeight(); int childBaseline = -1; final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); if (baselineAligned && lp.height != LayoutParams.MATCH_PARENT) { childBaseline = child.getBaseline(); } int gravity = lp.gravity; if (gravity < 0) { gravity = minorGravity; } switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) { case Gravity.TOP: childTop = paddingTop + lp.topMargin; if (childBaseline != -1) { childTop += maxAscent[INDEX_TOP] - childBaseline; } break; case Gravity.CENTER_VERTICAL: // Removed support for baseline alignment when layout_gravity or // gravity == center_vertical. See bug #1038483. // Keep the code around if we need to re-enable this feature // if (childBaseline != -1) { // // Align baselines vertically only if the child is smaller than us // if (childSpace - childHeight > 0) { // childTop = paddingTop + (childSpace / 2) - childBaseline; // } else { // childTop = paddingTop + (childSpace - childHeight) / 2; // } // } else { childTop = paddingTop + ((childSpace - childHeight) / 2) + lp.topMargin - lp.bottomMargin; break; case Gravity.BOTTOM: childTop = childBottom - childHeight - lp.bottomMargin; if (childBaseline != -1) { int descent = child.getMeasuredHeight() - childBaseline; childTop -= (maxDescent[INDEX_BOTTOM] - descent); } break; default: childTop = paddingTop; break; } if (hasDividerBeforeChildAt(childIndex)) { childLeft += mDividerWidth; } childLeft += lp.leftMargin; setChildFrame(child, childLeft + getLocationOffset(child), childTop, childWidth, childHeight); childLeft += childWidth + lp.rightMargin + getNextLocationOffset(child); i += getChildrenSkipCount(child, childIndex); } } }
- 代码比较长,逻辑挺简单,拆开看
- 获取count(子view个数),childLeft(开始显示子view的left位置)
- 遍历child
- 和上面一样获取child的measureHeight和measureWidth,说明layout是在measure的基础上的
- 累加childLeft,水平的linearlayout子view从左到右依次显示
- 最终调用child.layout
- @Override
- TextView
- 综上layout过程分析完毕,总结一下
- 相对于measure,layout过程相对简单,view不需要特殊处理,viewgroup要特殊处理
- viewGroup中必须实现onLayout方法,里面根据父view的layoutGravity计算子view的显示区域和开始的显示位置
- 遍历view,根据子view的layoutHeigth和layoutWidth以及gravity计算子view位置
- 最后说一下getWidth、getHeigth和getMeasureHeight、getMeasureWidth方法
- public final int getWidth() {
return mRight - mLeft;
} - public final int getHeight() {
return mBottom - mTop;
} - public final int getMeasuredWidth() {
return mMeasuredWidth & MEASURED_SIZE_MASK;
} - public final int getMeasuredHeight() {
return mMeasuredHeight & MEASURED_SIZE_MASK;
} - 由上可知getWidth、getHeigth是layout后计算出来的实际值,而getMeasureHeight、getMeasureWidth则是measure计算后获取的理论值
- public final int getWidth() {
performLayout
最新推荐文章于 2023-03-30 16:25:19 发布