View and ViewGroup layout process

Keywords: Java Attribute

View and ViewGroup layout process

The process here is a little simpler than measure. Let's start with the layout() of View Group. The layout() of View Group is mainly called after judging some conditions.
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 means setting this flag when measuring, that is, measuring according to measure cache, instead of calling onMeasure() directly, so we need to restart onMeasure once.
  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;//Recovery of markers
  26. }
  27. int oldL = mLeft;
  28. int oldT = mTop;
  29. int oldB = mBottom;
  30. int oldR = mRight;
  31. //Visual/optical boundary layout, generally 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 is also set at measure time.
  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);//This method view and ViewGroup are not implemented, but there are subclasses to implement it.
  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;//Recovery of markers
  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);//Interface callback
  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. }

As can be seen from the above code, the layout process first decides whether it needs to measure again based on whether the PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT variable has been set in the measurement process before, and then calls the setFrame to determine the location of its four points, which are left, top. right and bottom. With these four points, the subsequent drawing can be accurate.
Then the onLayout process is executed according to the PFLAG_LAYOUT_REQUIRED variable set in measure. In fact, neither View nor ViewGroup implements the onLayout process. The real implementation is in each subclass, which will be analyzed later. After that, if the view has set the OnLayoutChangeListener interface, the onLayoutChange() method of these interfaces will be called back.

For the time being, the setFrame() process is not analyzed here, and we will see it later in draw().

As mentioned above, View and ViewGroup do not implement onLayout() by themselves, but need subclasses to implement it. Here, we will take LinearLayout as an example to analyze the implementation process.
  1. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  2. if (mOrientation == VERTICAL) {
  3. measureVertical(widthMeasureSpec, heightMeasureSpec);
  4. } else {
  5. measureHorizontal(widthMeasureSpec, heightMeasureSpec);
  6. }
  7. }
Here we only analyze the vertical layout.
  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. }

As you can see from the above code, the first child is determined by the Gravity attribute first. The top position is then looped to get child Top and child Left, while the width is obtained to call setChildFrame(). The setChildFrame() simply calls the child. The layout process of each child is called recursively.

Posted by shavas on Thu, 28 Mar 2019 21:12:29 -0700