view的绘制原理

知识储备

1.ViewGroup.LayoutParams类()

  ViewGroup 的子类(RelativeLayout、LinearLayout)有其对应的ViewGroup.LayoutParams 子类

  如:RelativeLayout的 ViewGroup.LayoutParams子类 = RelativeLayoutParams 该类的作用为指定视图(View)的高度(Width)、宽度(Height)等参数 具体使用:(即我们平时布局文件中设定的参数值)

2. MeasureSpecs 类(父视图对子视图的测量要求)

   测量规格(MeasureSpec) = 测量模式(mode) + 测量大小(size)

其中,测量模式(Mode)的类型有3种:UNSPECIFIED、EXACTLY 和 AT_MOST

UNSPECIFIED:父容器对view没有任何限制,要多大给多大,这一般用于系统内部,表示一种测量的状态。

EXACTLY:父容器已经测出View的精确大小,这时候view的最终大小就是SpecSize所指定的值了。它对应于LayoutParams中的match_parent和具体的数值这两种模式。

AT_MOST:父容器指定了一个可用大小,即SpecSize,view的大小不能大于这个值,具体什么值要看不同view的具体实现。它对应于LayoutParams中的wrap_content。

MeasureSpec类的具体使用

1. 获取测量模式(Mode)

int specMode = MeasureSpec.getMode(measureSpec)

2. 获取测量大小(Size)

int specSize = MeasureSpec.getSize(measureSpec)

3. 通过Mode 和 Size 生成新的SpecMode

int measureSpec=MeasureSpec.makeMeasureSpec(size, mode);

看下源码:(以下为删减版,重点)


 
 
  1. public static class MeasureSpec {
  2. private static final int MODE_SHIFT = 30;
  3. private static final int MODE_MASK = 0x3 << MODE_SHIFT;
  4. /** @hide */
  5. @IntDef({UNSPECIFIED, EXACTLY, AT_MOST})
  6. @Retention(RetentionPolicy.SOURCE)
  7. public @interface MeasureSpecMode {}
  8. public static final int UNSPECIFIED = 0 << MODE_SHIFT;
  9. public static final int EXACTLY = 1 << MODE_SHIFT;
  10. public static final int AT_MOST = 2 << MODE_SHIFT;
  11. public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,
  12. @MeasureSpecMode int mode) {
  13. if (sUseBrokenMakeMeasureSpec) {
  14. return size + mode;
  15. } else {
  16. return (size & ~MODE_MASK) | (mode & MODE_MASK);
  17. }
  18. }
  19. @MeasureSpecMode
  20. public static int getMode(int measureSpec) {
  21. return (measureSpec & MODE_MASK);
  22. }
  23. public static int getSize(int measureSpec) {
  24. return (measureSpec & ~MODE_MASK);
  25. }
  26. }

这个已经写的很清楚了,即一个32位的二进制数通过与计算得到他的头2位和剩余30位。

下面来看下怎么得到MeasureSpec的值,子View的MeasureSpec值根据子View的布局参数(LayoutParams)和父容器的MeasureSpec值计算得来的,具体计算逻辑封装在getChildMeasureSpec()里。如下图:

这里面相对较于复杂,我们一步步看下源码,


 
 
  1. public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
  2. int specMode = MeasureSpec.getMode(spec);
  3. int specSize = MeasureSpec.getSize(spec);
  4. int size = Math.max( 0, specSize - padding);
  5. int resultSize = 0;
  6. int resultMode = 0;
  7. switch (specMode) {
  8. // Parent has imposed an exact size on us
  9. case MeasureSpec.EXACTLY:
  10. if (childDimension >= 0) {
  11. resultSize = childDimension;
  12. resultMode = MeasureSpec.EXACTLY;
  13. } else if (childDimension == LayoutParams.MATCH_PARENT) {
  14. // Child wants to be our size. So be it.
  15. resultSize = size;
  16. resultMode = MeasureSpec.EXACTLY;
  17. } else if (childDimension == LayoutParams.WRAP_CONTENT) {
  18. // Child wants to determine its own size. It can't be
  19. // bigger than us.
  20. resultSize = size;
  21. resultMode = MeasureSpec.AT_MOST;
  22. }
  23. break;
  24. // Parent has imposed a maximum size on us
  25. case MeasureSpec.AT_MOST:
  26. if (childDimension >= 0) {
  27. // Child wants a specific size... so be it
  28. resultSize = childDimension;
  29. resultMode = MeasureSpec.EXACTLY;
  30. } else if (childDimension == LayoutParams.MATCH_PARENT) {
  31. // Child wants to be our size, but our size is not fixed.
  32. // Constrain child to not be bigger than us.
  33. resultSize = size;
  34. resultMode = MeasureSpec.AT_MOST;
  35. } else if (childDimension == LayoutParams.WRAP_CONTENT) {
  36. // Child wants to determine its own size. It can't be
  37. // bigger than us.
  38. resultSize = size;
  39. resultMode = MeasureSpec.AT_MOST;
  40. }
  41. break;
  42. // Parent asked to see how big we want to be
  43. case MeasureSpec.UNSPECIFIED:
  44. if (childDimension >= 0) {
  45. // Child wants a specific size... let him have it
  46. resultSize = childDimension;
  47. resultMode = MeasureSpec.EXACTLY;
  48. } else if (childDimension == LayoutParams.MATCH_PARENT) {
  49. // Child wants to be our size... find out how big it should
  50. // be
  51. resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
  52. resultMode = MeasureSpec.UNSPECIFIED;
  53. } else if (childDimension == LayoutParams.WRAP_CONTENT) {
  54. // Child wants to determine its own size.... find out how
  55. // big it should be
  56. resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
  57. resultMode = MeasureSpec.UNSPECIFIED;
  58. }
  59. break;
  60. }
  61. //noinspection ResourceType
  62. return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
  63. }

梳理一下这部分的整体逻辑,就是根据分类不同的测量模式和view本身的大小来组成新的MeasureSpec值。总结如下

测量过程(measure)

自定义view我们都有写过,基本绘制流程都是测量(measure)、布局(layout)、绘制(draw),我们先来看测量view的原理。

源码(有删减,看重点)


 
 
  1. public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
  2. boolean optical = isLayoutModeOptical( this);
  3. if (optical != isLayoutModeOptical(mParent)) {
  4. Insets insets = getOpticalInsets();
  5. int oWidth = insets.left + insets.right;
  6. int oHeight = insets.top + insets.bottom;
  7. widthMeasureSpec = MeasureSpec.adjust(widthMeasureSpec, optical ? -oWidth : oWidth);
  8. heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
  9. }
  10. if (forceLayout || needsLayout) {
  11. // first clears the measured dimension flag
  12. mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
  13. resolveRtlPropertiesIfNeeded();
  14. int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
  15. if (cacheIndex < 0 || sIgnoreMeasureCache) {
  16. // measure ourselves, this should set the measured dimension flag back
  17. onMeasure(widthMeasureSpec, heightMeasureSpec);
  18. mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
  19. } else {
  20. long value = mMeasureCache.valueAt(cacheIndex);
  21. // Casting a long to int drops the high 32 bits, no mask needed
  22. setMeasuredDimensionRaw(( int) ( value >> 32), ( int) value);
  23. mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
  24. }
  25. // flag not set, setMeasuredDimension() was not invoked, we raise
  26. // an exception to warn the developer
  27. if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
  28. throw new IllegalStateException( "View with id " + getId() + ": "
  29. + getClass().getName() + "#onMeasure() did not set the"
  30. + " measured dimension by calling"
  31. + " setMeasuredDimension()");
  32. }
  33. mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
  34. }
  35. mOldWidthMeasureSpec = widthMeasureSpec;
  36. mOldHeightMeasureSpec = heightMeasureSpec;
  37. mMeasureCache.put(key, (( long) mMeasuredWidth) << 32 |
  38. ( long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
  39. }

这是个final方法,子类不会重写,关注下重点方法,也就是onMeasure(),继续


 
 
  1. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  2. setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
  3. getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
  4. }

这里面的方法很好懂,setMeasuredDimension很明显就是设置测量的大小,getDefaultSize也就是得到大小,然后我们具体来看下这些方法即对应参数。


 
 
  1. protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
  2. boolean optical = isLayoutModeOptical( this);
  3. if (optical != isLayoutModeOptical(mParent)) {
  4. Insets insets = getOpticalInsets();
  5. int opticalWidth = insets.left + insets.right;
  6. int opticalHeight = insets.top + insets.bottom;
  7. measuredWidth += optical ? opticalWidth : -opticalWidth;
  8. measuredHeight += optical ? opticalHeight : -opticalHeight;
  9. }
  10. setMeasuredDimensionRaw(measuredWidth, measuredHeight);
  11. }

方法里参数即测量的宽和高,转换成绝对尺寸进行设置,那我们就看下参数,即先看getDefaultSize方法,


 
 
  1. public static int getDefaultSize(int size, int measureSpec) {
  2. int result = size;
  3. int specMode = MeasureSpec.getMode(measureSpec);
  4. int specSize = MeasureSpec.getSize(measureSpec);
  5. switch (specMode) {
  6. case MeasureSpec.UNSPECIFIED:
  7. result = size;
  8. break;
  9. case MeasureSpec.AT_MOST:
  10. case MeasureSpec.EXACTLY:
  11. result = specSize;
  12. break;
  13. }
  14. return result;
  15. }

这个也很好理解,下面我们就需要知道getSuggestedMinimumWidth()这个了,继续


 
 
  1. protected int getSuggestedMinimumWidth() {
  2. return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
  3. }

这里没有背景返回的是mMinwidth,如果有背景返回的是二者的较大者。mMinwidth即我们设置的属性值android:minWidth,那我们再多看一下,这个getMinimumWidth()。


 
 
  1. public int getMinimumWidth() {
  2. return mMinWidth;
  3. }

即得到的对应背景view的属性值,这里要记住,其实返回的都是mMinwidth,当有背景的时候用的是背景的android:minWidth属性值。

至此,我们view的测量就结束了,记住重点方法getDefaultSize() = 计算View的宽/高值、setMeasuredDimension() = 存储测量后的View宽 / 高。

接下来我们看下测量viewgroup的原理(先遍历view,对每天进行测量,最后合在一起即是viewgroup的测量结果),


 
 
  1. protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
  2. final int size = mChildrenCount;
  3. final View[] children = mChildren;
  4. for ( int i = 0; i < size; ++i) {
  5. final View child = children[i];
  6. if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
  7. measureChild(child, widthMeasureSpec, heightMeasureSpec);
  8. }
  9. }
  10. }

继续往下,measureChild


 
 
  1. protected void measureChild(View child, int parentWidthMeasureSpec,
  2. int parentHeightMeasureSpec) {
  3. final LayoutParams lp = child.getLayoutParams();
  4. final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
  5. mPaddingLeft + mPaddingRight, lp.width);
  6. final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
  7. mPaddingTop + mPaddingBottom, lp.height);
  8. child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
  9. }

这里面最终调的就是view的measure方法,也就是view的measure方法了。

这地方只是viewgroup的测量,具体到LinearLayout或者FrameLayout内部实现也有所差异,这个下篇抽个具体的viewgroup来分析他的绘制过程即布局、绘制过程。

布局过程(layout)

先看单个view的layout(重点代码)


 
 
  1. @SuppressWarnings({ "unchecked"})
  2. public void layout(int l, int t, int r, int b) {
  3. if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
  4. onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
  5. mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
  6. }
  7. int oldL = mLeft;
  8. int oldT = mTop;
  9. int oldB = mBottom;
  10. int oldR = mRight;
  11. boolean changed = isLayoutModeOptical(mParent) ?
  12. setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
  13. if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
  14. onLayout(changed, l, t, r, b);
  15. ……
  16. }
  17. }

看下两个set方法,都是将l、t、r、b传入进行布局,重写onlayout即是因为发生了改变,需要重新布局。看下具体代码


 
 
  1. protected boolean setFrame(int left, int top, int right, int bottom) {
  2. boolean changed = false;
  3. if (DBG) {
  4. Log.d( "View", this + " View.setFrame(" + left + "," + top + ","
  5. + right + "," + bottom + ")");
  6. }
  7. if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
  8. changed = true;
  9. // Remember our drawn bit
  10. int drawn = mPrivateFlags & PFLAG_DRAWN;
  11. int oldWidth = mRight - mLeft;
  12. int oldHeight = mBottom - mTop;
  13. int newWidth = right - left;
  14. int newHeight = bottom - top;
  15. boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
  16. // Invalidate our old position
  17. invalidate(sizeChanged);
  18. mLeft = left;
  19. mTop = top;
  20. mRight = right;
  21. mBottom = bottom;
  22. mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
  23. mPrivateFlags |= PFLAG_HAS_BOUNDS;
  24. if (sizeChanged) {
  25. sizeChange(newWidth, newHeight, oldWidth, oldHeight);
  26. }
  27. if ((mViewFlags & VISIBILITY_MASK) == VISIBLE || mGhostView != null) {
  28. // If we are visible, force the DRAWN bit to on so that
  29. // this invalidate will go through (at least to our parent).
  30. // This is because someone may have invalidated this view
  31. // before this call to setFrame came in, thereby clearing
  32. // the DRAWN bit.
  33. mPrivateFlags |= PFLAG_DRAWN;
  34. invalidate(sizeChanged);
  35. // parent display list may need to be recreated based on a change in the bounds
  36. // of any child
  37. invalidateParentCaches();
  38. }
  39. // Reset drawn bit to original value (invalidate turns it off)
  40. mPrivateFlags |= drawn;
  41. mBackgroundSizeChanged = true;
  42. mDefaultFocusHighlightSizeChanged = true;
  43. if (mForegroundInfo != null) {
  44. mForegroundInfo.mBoundsChanged = true;
  45. }
  46. notifySubtreeAccessibilityStateChangedIfNeeded();
  47. }
  48. return changed;
  49. }

看是否变化,进行重绘,实现新的布局。


 
 
  1. private boolean setOpticalFrame( int left, int top, int right, int bottom) {
  2. Insets parentInsets = mParent instanceof View ?
  3. ((View) mParent).getOpticalInsets() : Insets.NONE;
  4. Insets childInsets = getOpticalInsets();
  5. return setFrame(
  6. left + parentInsets. left - childInsets. left,
  7. top + parentInsets.top - childInsets.top,
  8. right + parentInsets. left + childInsets. right,
  9. bottom + parentInsets.top + childInsets.bottom);
  10. }

这个内部调用的方法和上述一致,原理相同。

再看下onlayout


 
 
  1. protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
  2. }

很容易理解,他没有子view。只能通过自身的layout来布局。

在看viewgroup的layout过程


 
 
  1. @Override
  2. public final void layout(int l, int t, int r, int b) {
  3. if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
  4. if (mTransition != null) {
  5. mTransition.layoutChange( this);
  6. }
  7. super.layout(l, t, r, b);
  8. } else {
  9. // record the fact that we noop'd it; request layout when transition finishes
  10. mLayoutCalledWhileSuppressed = true;
  11. }
  12. }

很明显,就使用父类的布局方法,即单个view的布局方法。这里也不能具体,因为不同的viewgroup布局肯定不同,都留到下一篇具体分析。

绘制过程(draw)


 
 
  1. @ CallSuper
  2. public void draw( Canvas canvas) {
  3. final int privateFlags = mPrivateFlags;
  4. final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
  5. (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
  6. mPrivateFlags = (privateFlags & ~ PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
  7. /*
  8. * Draw traversal performs several drawing steps which must be executed
  9. * in the appropriate order:
  10. *
  11. * 1. Draw the background
  12. * 2. If necessary, save the canvas' layers to prepare for fading
  13. * 3. Draw view's content
  14. * 4. Draw children
  15. * 5. If necessary, draw the fading edges and restore layers
  16. * 6. Draw decorations (scrollbars for instance)
  17. */
  18. // Step 1, draw the background, if needed
  19. int saveCount;
  20. if (!dirtyOpaque) {
  21. drawBackground(canvas);
  22. }
  23. // skip step 2 & 5 if possible (common case)
  24. final int viewFlags = mViewFlags;
  25. boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
  26. boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
  27. if (!verticalEdges && !horizontalEdges) {
  28. // Step 3, draw the content
  29. if (!dirtyOpaque) onDraw(canvas);
  30. // Step 4, draw the children
  31. dispatchDraw(canvas);
  32. drawAutofilledHighlight(canvas);
  33. // Overlay is part of the content and draws beneath Foreground
  34. if (mOverlay != null && !mOverlay.isEmpty()) {
  35. mOverlay.getOverlayView().dispatchDraw(canvas);
  36. }
  37. // Step 6, draw decorations (foreground, scrollbars)
  38. onDrawForeground(canvas);
  39. // Step 7, draw the default focus highlight
  40. drawDefaultFocusHighlight(canvas);
  41. if (debugDraw()) {
  42. debugDrawFocus(canvas);
  43. }
  44. // we're done...
  45. return;
  46. }
  47. /*
  48. * Here we do the full fledged routine...
  49. * (this is an uncommon case where speed matters less,
  50. * this is why we repeat some of the tests that have been
  51. * done above)
  52. */
  53. boolean drawTop = false;
  54. boolean drawBottom = false;
  55. boolean drawLeft = false;
  56. boolean drawRight = false;
  57. float topFadeStrength = 0.0f;
  58. float bottomFadeStrength = 0.0f;
  59. float leftFadeStrength = 0.0f;
  60. float rightFadeStrength = 0.0f;
  61. // Step 2, save the canvas' layers
  62. int paddingLeft = mPaddingLeft;
  63. final boolean offsetRequired = isPaddingOffsetRequired();
  64. if (offsetRequired) {
  65. paddingLeft += getLeftPaddingOffset();
  66. }
  67. int left = mScrollX + paddingLeft;
  68. int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
  69. int top = mScrollY + getFadeTop(offsetRequired);
  70. int bottom = top + getFadeHeight(offsetRequired);
  71. if (offsetRequired) {
  72. right += getRightPaddingOffset();
  73. bottom += getBottomPaddingOffset();
  74. }
  75. final ScrollabilityCache scrollabilityCache = mScrollCache;
  76. final float fadeHeight = scrollabilityCache.fadingEdgeLength;
  77. int length = (int) fadeHeight;
  78. // clip the fade length if top and bottom fades overlap
  79. // overlapping fades produce odd-looking artifacts
  80. if (verticalEdges && (top + length > bottom - length)) {
  81. length = (bottom - top) / 2;
  82. }
  83. // also clip horizontal fades if necessary
  84. if (horizontalEdges && ( left + length > right - length)) {
  85. length = ( right - left) / 2;
  86. }
  87. if (verticalEdges) {
  88. topFadeStrength = Math. max( 0.0f, Math. min( 1.0f, getTopFadingEdgeStrength()));
  89. drawTop = topFadeStrength * fadeHeight > 1.0f;
  90. bottomFadeStrength = Math. max( 0.0f, Math. min( 1.0f, getBottomFadingEdgeStrength()));
  91. drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
  92. }
  93. if (horizontalEdges) {
  94. leftFadeStrength = Math. max( 0.0f, Math. min( 1.0f, getLeftFadingEdgeStrength()));
  95. drawLeft = leftFadeStrength * fadeHeight > 1.0f;
  96. rightFadeStrength = Math. max( 0.0f, Math. min( 1.0f, getRightFadingEdgeStrength()));
  97. drawRight = rightFadeStrength * fadeHeight > 1.0f;
  98. }
  99. saveCount = canvas.getSaveCount();
  100. int solidColor = getSolidColor();
  101. if (solidColor == 0) {
  102. final int flags = Canvas. HAS_ALPHA_LAYER_SAVE_FLAG;
  103. if (drawTop) {
  104. canvas.saveLayer( left, top, right, top + length, null, flags);
  105. }
  106. if (drawBottom) {
  107. canvas.saveLayer( left, bottom - length, right, bottom, null, flags);
  108. }
  109. if (drawLeft) {
  110. canvas.saveLayer( left, top, left + length, bottom, null, flags);
  111. }
  112. if (drawRight) {
  113. canvas.saveLayer( right - length, top, right, bottom, null, flags);
  114. }
  115. } else {
  116. scrollabilityCache.setFadeColor(solidColor);
  117. }
  118. // Step 3, draw the content
  119. if (!dirtyOpaque) onDraw(canvas);
  120. // Step 4, draw the children
  121. dispatchDraw(canvas);
  122. // Step 5, draw the fade effect and restore layers
  123. final Paint p = scrollabilityCache.paint;
  124. final Matrix matrix = scrollabilityCache.matrix;
  125. final Shader fade = scrollabilityCache.shader;
  126. if (drawTop) {
  127. matrix.setScale( 1, fadeHeight * topFadeStrength);
  128. matrix.postTranslate( left, top);
  129. fade.setLocalMatrix(matrix);
  130. p.setShader(fade);
  131. canvas.drawRect( left, top, right, top + length, p);
  132. }
  133. if (drawBottom) {
  134. matrix.setScale( 1, fadeHeight * bottomFadeStrength);
  135. matrix.postRotate( 180);
  136. matrix.postTranslate( left, bottom);
  137. fade.setLocalMatrix(matrix);
  138. p.setShader(fade);
  139. canvas.drawRect( left, bottom - length, right, bottom, p);
  140. }
  141. if (drawLeft) {
  142. matrix.setScale( 1, fadeHeight * leftFadeStrength);
  143. matrix.postRotate(- 90);
  144. matrix.postTranslate( left, top);
  145. fade.setLocalMatrix(matrix);
  146. p.setShader(fade);
  147. canvas.drawRect( left, top, left + length, bottom, p);
  148. }
  149. if (drawRight) {
  150. matrix.setScale( 1, fadeHeight * rightFadeStrength);
  151. matrix.postRotate( 90);
  152. matrix.postTranslate( right, top);
  153. fade.setLocalMatrix(matrix);
  154. p.setShader(fade);
  155. canvas.drawRect( right - length, top, right, bottom, p);
  156. }
  157. canvas.restoreToCount(saveCount);
  158. drawAutofilledHighlight(canvas);
  159. // Overlay is part of the content and draws beneath Foreground
  160. if (mOverlay != null && !mOverlay.isEmpty()) {
  161. mOverlay.getOverlayView().dispatchDraw(canvas);
  162. }
  163. // Step 6, draw decorations (foreground, scrollbars)
  164. onDrawForeground(canvas);
  165. if (debugDraw()) {
  166. debugDrawFocus(canvas);
  167. }
  168. }

这是段又臭又长的代码,不过他的注释和步骤都很清晰,分别进行绘制自身view、自身view的背景、自身view的内容、子view如果有必要的话、绘制装饰

再反过来想一下viewgroup的绘制,应该也就多了绘制子view,我们看下这个代码

……代码太长,不贴了,就是进行遍历,最后进行单个view的绘制。

以上就是整个view的measure、layout、draw过程分析,个人感觉很是粗糙(只是带着源码走了一圈)

写在最后

欢迎入群指导我 QQ群:589780530

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值