之前的文章《View体系(六)View工作流程入口》 提到View
的工作流程包括了measure
、layout
和draw
的过程,今天我们就来看一下View
的draw
流程是怎样的。
(注:文中源码基于 Android 12
)
View
的draw
流程很简单,源码里的注释官方也写的很清楚,我们看View
的draw
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public void draw (Canvas canvas) { ... drawBackground(canvas); ... ... ... onDraw(canvas); ... dispatchDraw(canvas); ... ... onDrawForeground(canvas); ... drawDefaultFocusHighlight(canvas);
总览 官方注释已经清楚的写了每一步的工作:
如果需要,则绘制背景(drawBackground
)
保存当前canvas层
绘制View的内容(onDraw
)
绘制子View(dispatchDraw
)
如果需要,则绘制View的褪色边缘,类似于阴影效果
绘制装饰,比如滚动条(onDrawForeground
)
绘制默认焦点高亮效果(drawDefaultFocusHighlight
)
注释中说明了第2步和第5步可以跳过,这里就不展开讲解,在此重点分析其他步骤。
步骤1:绘制背景 进入View
的drawBackground
方法:
1 2 3 4 5 6 private void drawBackground (Canvas canvas) { final Drawable background = mBackground; ... setBackgroundBounds(); ... background.draw(canvas);
注释1处设置背景范围,注释2处通过Drawable
的draw
方法来绘制背景,关于Drawable
将在后面的文章详细讲解。看注释1的setBackgroundBounds
是如何设置背景范围的:
1 2 3 4 5 6 7 void setBackgroundBounds () { if (mBackgroundSizeChanged && mBackground != null ) { mBackground.setBounds(0 , 0 , mRight - mLeft, mBottom - mTop); mBackgroundSizeChanged = false ; rebuildOutline(); } }
看到注释1处通过View
的mRight、mLeft、mBottom、mTop
等参数调用mBackground.setBounds
方法来进行绘制范围的设置。
步骤2:保存当前canvas层 步骤3:绘制View的内容 步骤3调用了View
的onDraw
方法,这个方法是一个空实现,因为不同的View
有不同的内容,所以需要我们自己去实现,即在自定义View
时重写该方法来实现我们自己的绘制。
1 2 protected void onDraw (Canvas canvas) {}
步骤4:绘制子View 步骤4调用了dispatchDraw
方法,这个方法也是个空实现:
1 2 3 protected void dispatchDraw (Canvas canvas) {}
ViewGroup
重写了这个方法,我们看ViewGroup
的dispatchDraw
方法:
1 2 3 4 5 protected void dispatchDraw (Canvas canvas) { ... for (int i = 0 ; i < childrenCount; i++) { ... more |= drawChild(canvas, transientChild, drawingTime);
源码很长,这里只贴出关键代码,在dispatchDraw
方法中遍历子View
并调用drawChild
方法,我们继续看drawChild
方法:
1 2 3 protected boolean drawChild (Canvas canvas, View child, long drawingTime) { return child.draw(canvas, this , drawingTime); }
drawChild
方法实际就是调用了子View
的draw
方法对子View
进行绘制
步骤5:绘制View的阴影效果 步骤6:绘制装饰 步骤6调用了onDrawForeground
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public void onDrawForeground (Canvas canvas) { onDrawScrollIndicators(canvas); onDrawScrollBars(canvas); final Drawable foreground = mForegroundInfo != null ? mForegroundInfo.mDrawable : null ; if (foreground != null ) { if (mForegroundInfo.mBoundsChanged) { mForegroundInfo.mBoundsChanged = false ; final Rect selfBounds = mForegroundInfo.mSelfBounds; final Rect overlayBounds = mForegroundInfo.mOverlayBounds; if (mForegroundInfo.mInsidePadding) { selfBounds.set(0 , 0 , getWidth(), getHeight()); } else { selfBounds.set(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(), getHeight() - getPaddingBottom()); } final int ld = getLayoutDirection(); Gravity.apply(mForegroundInfo.mGravity, foreground.getIntrinsicWidth(), foreground.getIntrinsicHeight(), selfBounds, overlayBounds, ld); foreground.setBounds(overlayBounds); } foreground.draw(canvas); } }
这里主要是对ScrollBar
及其它装饰进行绘制。
步骤7:绘制默认焦点高亮效果 1 2 3 4 5 6 7 8 9 10 11 12 13 private void drawDefaultFocusHighlight (Canvas canvas) { if (mDefaultFocusHighlight != null && isFocused()) { if (mDefaultFocusHighlightSizeChanged) { mDefaultFocusHighlightSizeChanged = false ; final int l = mScrollX; final int r = l + mRight - mLeft; final int t = mScrollY; final int b = t + mBottom - mTop; mDefaultFocusHighlight.setBounds(l, t, r, b); } mDefaultFocusHighlight.draw(canvas); } }
关注我