Android布局绘制流程:
从根节点 noodview 递归调用每一级子view 的 Mesure 进行测量
然后
从根节点 noodview 递归调用每一级子view 的 layout 方法,把测量过程得出的子view的位置和size进行传递给子view ,然后子view保存参数
测量是动态化,是需要根view和子view
首先nood view 根节点,在自己的measure 中,根据xml布局中对子view 的 要求规范 和 自己可用空间,得出子view 具体尺寸要求
next:
noodview(父View) 根节点在自己的onMeasure 中 根据xml定义的对子view 的要求,和自己可用空间,得出对子view 的具体尺寸要求
子view在自己的onMeasure 根据noodView 的要求以及特性计算出自己的期望尺寸
如果是viewGroup,还会调用每个子View的measure 进行测量 计算(父View一般会和子View之间宽度相同 或者相加叠加)
如果自定义子view 宽高 和 父View有冲突,则父View会修正子View大小,例如ConstraintLayout 和 LinearLayout 表现出很大差异
父View在子View计算出期望尺寸时后,得出子View的实际尺寸和位置
子View在self的layout中,将父view传来的实际尺寸和位置进行保存
- public void layout(int l, int t, int r, int b) {
- if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
- onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
- mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
- }
-
- int oldL = mLeft;
- int oldT = mTop;
- int oldB = mBottom;
- int oldR = mRight;
-
- boolean changed = isLayoutModeOptical(mParent) ?
- setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
-
- if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
- onLayout(changed, l, t, r, b);
-
- if (shouldDrawRoundScrollbar()) {
- if(mRoundScrollbarRenderer == null) {
- mRoundScrollbarRenderer = new RoundScrollbarRenderer(this);
- }
- } else {
- mRoundScrollbarRenderer = null;
- }
-
- mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
-
- ListenerInfo li = mListenerInfo;
- if (li != null && li.mOnLayoutChangeListeners != null) {
- ArrayList<OnLayoutChangeListener> listenersCopy =
- (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
- int numListeners = listenersCopy.size();
- for (int i = 0; i < numListeners; ++i) {
- listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
- }
- }
- }
-
- final boolean wasLayoutValid = isLayoutValid();
-
- mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
- mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
-
- if (!wasLayoutValid && isFocused()) {
- mPrivateFlags &= ~PFLAG_WANTS_FOCUS;
- if (canTakeFocus()) {
- // We have a robust focus, so parents should no longer be wanting focus.
- clearParentsWantFocus();
- } else if (getViewRootImpl() == null || !getViewRootImpl().isInLayout()) {
- // This is a weird case. Most-likely the user, rather than ViewRootImpl, called
- // layout. In this case, there's no guarantee that parent layouts will be evaluated
- // and thus the safest action is to clear focus here.
- clearFocusInternal(null, /* propagate */ true, /* refocus */ false);
- clearParentsWantFocus();
- } else if (!hasParentWantsFocus()) {
- // original requestFocus was likely on this view directly, so just clear focus
- clearFocusInternal(null, /* propagate */ true, /* refocus */ false);
- }
- // otherwise, we let parents handle re-assigning focus during their layout passes.
- } else if ((mPrivateFlags & PFLAG_WANTS_FOCUS) != 0) {
- mPrivateFlags &= ~PFLAG_WANTS_FOCUS;
- View focused = findFocus();
- if (focused != null) {
- // Try to restore focus as close as possible to our starting focus.
- if (!restoreDefaultFocus() && !hasParentWantsFocus()) {
- // Give up and clear focus once we've reached the top-most parent which wants
- // focus.
- focused.clearFocusInternal(null, /* propagate */ true, /* refocus */ false);
- }
- }
- }
-
- if ((mPrivateFlags3 & PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT) != 0) {
- mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
- notifyEnterOrExitForAutoFillIfNeeded(true);
- }
-
- notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
- }
- protected boolean setFrame(int left, int top, int right, int bottom) {
- boolean changed = false;
-
- if (DBG) {
- Log.d(VIEW_LOG_TAG, this + " View.setFrame(" + left + "," + top + ","
- + right + "," + bottom + ")");
- }
-
- if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
- changed = true;
-
- // Remember our drawn bit
- int drawn = mPrivateFlags & PFLAG_DRAWN;
-
- int oldWidth = mRight - mLeft;
- int oldHeight = mBottom - mTop;
- int newWidth = right - left;
- int newHeight = bottom - top;
- boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
-
- // Invalidate our old position
- invalidate(sizeChanged);
-
- mLeft = left;
- mTop = top;
- mRight = right;
- mBottom = bottom;
- mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
-
- mPrivateFlags |= PFLAG_HAS_BOUNDS;
-
-
- if (sizeChanged) {
- sizeChange(newWidth, newHeight, oldWidth, oldHeight);
- }
-
- if ((mViewFlags & VISIBILITY_MASK) == VISIBLE || mGhostView != null) {
- // If we are visible, force the DRAWN bit to on so that
- // this invalidate will go through (at least to our parent).
- // This is because someone may have invalidated this view
- // before this call to setFrame came in, thereby clearing
- // the DRAWN bit.
- mPrivateFlags |= PFLAG_DRAWN;
- invalidate(sizeChanged);
- // parent display list may need to be recreated based on a change in the bounds
- // of any child
- invalidateParentCaches();
- }
-
- // Reset drawn bit to original value (invalidate turns it off)
- mPrivateFlags |= drawn;
-
- mBackgroundSizeChanged = true;
- mDefaultFocusHighlightSizeChanged = true;
- if (mForegroundInfo != null) {
- mForegroundInfo.mBoundsChanged = true;
- }
-
- notifySubtreeAccessibilityStateChangedIfNeeded();
- }
- return changed;
- }
在setFrame时 , left,top,right,bottom 在layout的时候就已经保存下来了,重写layout 可以改变 这些参数
如果是ViewGroup,在onLayout中调用每个子View 的layout,并把子view的位置传给子view