View与ViewGroup layout 过程

View与ViewGroup layout 过程

这个过程相比measure要简单一点,我们先从View 的layout()看起,ViewGroup的layout主要还是判断一些条件之后调用View 的 layout()
View.java
   
   
     
     
     
     
  1. public void layout(int l, int t, int r, int b) {
  2. if (DBG_SYSTRACE_LAYOUT) {
  3. Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout : " + getClass().getSimpleName());
  4. }
  5. // !=0 表示在measure 的时候设置这个标志,也就是measure的时候根据measure cache 测量,而不是直接调用onMeasure(),所以这里要重新onMeasure一次
  6. if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
  7. /// M: Monitor onLayout time if longer than 3s print log.
  8. if (DBG_LAYOUT || DBG_MEASURE_LAYOUT) {
  9. Xlog.d(VIEW_LOG_TAG, "view onMeasure start (measure cache), this =" + this
  10. + ", widthMeasureSpec = " + MeasureSpec.toString(mOldWidthMeasureSpec)
  11. + ", heightMeasureSpec = " + MeasureSpec.toString(mOldHeightMeasureSpec));
  12. }
  13. long logTime = System.currentTimeMillis();
  14. onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
  15. long nowTime = System.currentTimeMillis();
  16. if (nowTime - logTime > DBG_TIMEOUT_VALUE) {
  17. Xlog.d(VIEW_LOG_TAG, "[ANR Warning]onMeasure time too long, this =" + this
  18. + "time =" + (nowTime - logTime) + " ms");
  19. }
  20. if (DBG_LAYOUT || DBG_MEASURE_LAYOUT) {
  21. Xlog.d(VIEW_LOG_TAG, "view onMeasure end (measure cache), this =" + this
  22. + ", mMeasuredWidth = " + mMeasuredWidth + ", mMeasuredHeight = "
  23. + mMeasuredHeight + ", time =" + (nowTime - logTime) + " ms");
  24. }
  25. mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;//将标志位恢复
  26. }
  27. int oldL = mLeft;
  28. int oldT = mTop;
  29. int oldB = mBottom;
  30. int oldR = mRight;
  31. //视觉/光学边界布局,一般是false
  32. boolean changed = isLayoutModeOptical(mParent) ?
  33. setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
  34. if (DBG_LAYOUT || DBG_MEASURE_LAYOUT) {
  35. Xlog.d(VIEW_LOG_TAG, "view layout start, this = " + this + ", mLeft = " + mLeft
  36. + ", mTop = " + mTop + ", mRight = " + mRight + ", mBottom = " + mBottom
  37. + ", changed = " + changed);
  38. }
  39. //PFLAG_LAYOUT_REQUIRED这个标志也是在measure 的时候设置的.
  40. if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
  41. /// M: Monitor onLayout time if longer than 3s print log.
  42. long logTime = System.currentTimeMillis();
  43. onLayout(changed, l, t, r, b);//这个方法view和ViewGroup都没有实现,具体是有子类去实现
  44. long nowTime = System.currentTimeMillis();
  45. if (nowTime - logTime > DBG_TIMEOUT_VALUE) {
  46. Xlog.d(VIEW_LOG_TAG, "[ANR Warning]onLayout time too long, this =" + this
  47. + "time =" + (nowTime - logTime) + " ms");
  48. }
  49. if (DBG_LAYOUT || DBG_MEASURE_LAYOUT) {
  50. Xlog.d(VIEW_LOG_TAG, "view layout end, this =" + this + ", mLeft = " + mLeft
  51. + ", mTop = " + mTop + ", mRight = " + mRight + ", mBottom = " + mBottom
  52. + ", time =" + (nowTime - logTime) + " ms");
  53. }
  54. mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;//将标志位恢复
  55. ListenerInfo li = mListenerInfo;
  56. if (li != null && li.mOnLayoutChangeListeners != null) {
  57. ArrayList<OnLayoutChangeListener> listenersCopy =
  58. (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
  59. int numListeners = listenersCopy.size();
  60. for (int i = 0; i < numListeners; ++i) {
  61. listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);//接口回调
  62. }
  63. }
  64. } else {
  65. if (DBG_LAYOUT || DBG_MEASURE_LAYOUT) {
  66. Xlog.d(VIEW_LOG_TAG, "view layout end 2 (use previous layout), this = " + this
  67. + ", mLeft = " + mLeft + ", mTop = " + mTop
  68. + ", mRight = " + mRight + ", mBottom = " + mBottom);
  69. }
  70. }
  71. mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
  72. mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
  73. if (DBG_SYSTRACE_LAYOUT) {
  74. Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  75. }
  76. }

从上面的代码可以看到,layout 流程先会根据之前在measure流程中是否设置了 PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT 变量来判断是否需要重新measure一遍,然后调用 setFrame 来确定自己的4个点的位置,4个点分别是left,top.right,bottom.有了这4个点,后续才能准确的绘制.
接着根据在measure里面设置的 PFLAG_LAYOUT_REQUIRED 变量来执行 onLayout 流程,其实这个方法View和ViewGroup都没有去实现,真正的实现是在各个子类里面的,后面会分析.再这之后,如果这个视图设置过 OnLayoutChangeListener 接口,还会回调这些接口的 onLayoutChange() 方法.

这里暂时不分析 setFrame()流程,后续在draw()里面再看.

上面已经提到 View和ViewGroup没有自己去实现onLayout(),是需要子类是去实现.这里还是以LinearLayout为例分析一下实现过程.
   
   
     
     
     
     
  1. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  2. if (mOrientation == VERTICAL) {
  3. measureVertical(widthMeasureSpec, heightMeasureSpec);
  4. } else {
  5. measureHorizontal(widthMeasureSpec, heightMeasureSpec);
  6. }
  7. }
这里我们只分析垂直布局
   
   
     
     
     
     
  1. void layoutVertical(int left, int top, int right, int bottom) {
  2. final int paddingLeft = mPaddingLeft;
  3. int childTop;
  4. int childLeft;
  5. // Where right end of child should go
  6. final int width = right - left;
  7. int childRight = width - mPaddingRight;
  8. // Space available for child
  9. int childSpace = width - paddingLeft - mPaddingRight;
  10. final int count = getVirtualChildCount();
  11. final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
  12. final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
  13. switch (majorGravity) {
  14. case Gravity.BOTTOM:
  15. // mTotalLength contains the padding already
  16. childTop = mPaddingTop + bottom - top - mTotalLength;
  17. break;
  18. // mTotalLength contains the padding already
  19. case Gravity.CENTER_VERTICAL:
  20. childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
  21. break;
  22. case Gravity.TOP:
  23. default:
  24. childTop = mPaddingTop;
  25. break;
  26. }
  27. for (int i = 0; i < count; i++) {
  28. final View child = getVirtualChildAt(i);
  29. if (child == null) {
  30. childTop += measureNullChild(i);
  31. } else if (child.getVisibility() != GONE) {
  32. final int childWidth = child.getMeasuredWidth();
  33. final int childHeight = child.getMeasuredHeight();
  34. final LinearLayout.LayoutParams lp =
  35. (LinearLayout.LayoutParams) child.getLayoutParams();
  36. int gravity = lp.gravity;
  37. if (gravity < 0) {
  38. gravity = minorGravity;
  39. }
  40. final int layoutDirection = getLayoutDirection();
  41. final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
  42. switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
  43. case Gravity.CENTER_HORIZONTAL:
  44. childLeft = paddingLeft + ((childSpace - childWidth) / 2)
  45. + lp.leftMargin - lp.rightMargin;
  46. break;
  47. case Gravity.RIGHT:
  48. childLeft = childRight - childWidth - lp.rightMargin;
  49. break;
  50. case Gravity.LEFT:
  51. default:
  52. childLeft = paddingLeft + lp.leftMargin;
  53. break;
  54. }
  55. if (hasDividerBeforeChildAt(i)) {
  56. childTop += mDividerHeight;
  57. }
  58. childTop += lp.topMargin;
  59. setChildFrame(child, childLeft, childTop + getLocationOffset(child),
  60. childWidth, childHeight);
  61. childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
  62. i += getChildrenSkipCount(child, i);
  63. }
  64. }
  65. }

    
    
      
      
      
      
  1. private void setChildFrame(View child, int left, int top, int width, int height) {
  2. child.layout(left, top, left + width, top + height);
  3. }

从上面的代码可以看到,是先根据 Gravity 属性来确定第一个child 的top 位置,然后循环来获取childTop和childLeft,同时获取宽高来调用 setChildFrame ().而 setChildFrame ()只是调用child 的layout流程.就这样递归调用各个"child"的layout流程.

你可能感兴趣的:(Android,源码分析)