performLayout

  1. 初始化app后DecorView调用performTraversals(),执行完performMeasure后开始执行
    performLayout(lp, mWidthmHeight);
  2. performLayout里面调用了 view的layout方法
    host.layout(00, host.getMeasuredWidth(), host.getMeasuredHeight());
  3. 先看viewGroup的处理,里面包含两个函数:
    1. layout调用了view.layout
      super.layout(l, t, r, b)
    2. onLayout
      abstract方法,需要子类实现
  4. 所有的方法最终都是调用了view的layout,接下来看view的layout方法
    onLayout(changed, l, t, r, b)
    调用用了onLayout,而onLayout为空方法。
  5. 到这里layout执行完了,没有实现功能,说明layout的计算是在具体的view里面实现的
  6. 接下来分别看一下TextView和FrameLayout的onLayout方法
    1. 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的四个参数)进行处理就好了,并不需要自己本身去计算
    2. FrameLayout
      1. 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);
                }
            }
        }


      2. 逻辑很清楚,遍历child,获得child的measureWidth和measureHeight和layoutparam,根据gravity计算childLeft和childTop,另外两个参数直接通过width和height计算获得。
      3. 最后调用child.layout进行处理
      4. 不同的viewgrouop由于自身不同的规则会在onLayout进行处理,这里的fragmeLayout比较简单,没有考虑同一个viewGroup之下的子view之间的互相影响,下面看一下LinearLayout
    3. LinearLayout
      1. @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);
            }
        }
      2. 首先根据orientation进行区分,没毛病
      3. 看一下 layoutHorizontal
      4. 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);
                }
            }
        }


      5. 代码比较长,逻辑挺简单,拆开看
        1. 获取count(子view个数),childLeft(开始显示子view的left位置)
        2. 遍历child
          1. 和上面一样获取child的measureHeight和measureWidth,说明layout是在measure的基础上的
          2. 累加childLeft,水平的linearlayout子view从左到右依次显示
          3. 最终调用child.layout
  7. 综上layout过程分析完毕,总结一下
    1. 相对于measure,layout过程相对简单,view不需要特殊处理,viewgroup要特殊处理
    2. viewGroup中必须实现onLayout方法,里面根据父view的layoutGravity计算子view的显示区域和开始的显示位置
    3. 遍历view,根据子view的layoutHeigth和layoutWidth以及gravity计算子view位置
    4. 最后说一下getWidth、getHeigth和getMeasureHeight、getMeasureWidth方法
      1. public final int getWidth() {
            return mRight mLeft;
        }
      2. public final int getHeight() {
            return mBottom mTop;
        }
      3. public final int getMeasuredWidth() {
            return mMeasuredWidth MEASURED_SIZE_MASK;
        }
      4. public final int getMeasuredHeight() {
            return mMeasuredHeight MEASURED_SIZE_MASK;
        }
      5. 由上可知getWidth、getHeigth是layout后计算出来的实际值,而getMeasureHeight、getMeasureWidth则是measure计算后获取的理论值
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值