irpas技术客

View体系(十一)View的draw流程_droidYu

大大的周 493

之前的文章《View体系(六)View工作流程入口》提到View的工作流程包括了measure、layout和draw的过程,今天我们就来看一下View的draw流程是怎样的。

(注:文中源码基于 Android 12)

View的draw流程很简单,源码里的注释官方也写的很清楚,我们看View的draw方法:

public void draw(Canvas canvas) { ... // Step 1, draw the background, if needed drawBackground(canvas); ... // skip step 2 & 5 if possible (common case) ... // Step 2, save the canvas' layers ... // Step 3, draw the content onDraw(canvas); ... // Step 4, draw the children dispatchDraw(canvas); ... // Step 5, draw the fade effect and restore layers ... // Step 6, draw decorations (foreground, scrollbars) onDrawForeground(canvas); ... // Step 7, draw the default focus highlight drawDefaultFocusHighlight(canvas); 总览

官方注释已经清楚的写了每一步的工作:

如果需要,则绘制背景(drawBackground)保存当前canvas层绘制View的内容(onDraw)绘制子View(dispatchDraw)如果需要,则绘制View的褪色边缘,类似于阴影效果绘制装饰,比如滚动条(onDrawForeground)绘制默认焦点高亮效果(drawDefaultFocusHighlight)

注释中说明了第2步和第5步可以跳过,这里就不展开讲解,在此重点分析其他步骤。

步骤1:绘制背景

进入View的drawBackground方法:

private void drawBackground(Canvas canvas) { final Drawable background = mBackground; ... setBackgroundBounds(); //1 ... background.draw(canvas); //2

注释1处设置背景范围,注释2处通过Drawable的draw方法来绘制背景,关于Drawable将在后面的文章详细讲解。看注释1的setBackgroundBounds是如何设置背景范围的:

void setBackgroundBounds() { if (mBackgroundSizeChanged && mBackground != null) { mBackground.setBounds(0, 0, mRight - mLeft, mBottom - mTop); //1 mBackgroundSizeChanged = false; rebuildOutline(); } }

看到注释1处通过View的mRight、mLeft、mBottom、mTop等参数调用mBackground.setBounds方法来进行绘制范围的设置。

步骤2:保存当前canvas层 步骤3:绘制View的内容

步骤3调用了View的onDraw方法,这个方法是一个空实现,因为不同的View有不同的内容,所以需要我们自己去实现,即在自定义View时重写该方法来实现我们自己的绘制。

protected void onDraw(Canvas canvas) { } 步骤4:绘制子View

步骤4调用了dispatchDraw方法,这个方法也是个空实现:

protected void dispatchDraw(Canvas canvas) { }

ViewGroup重写了这个方法,我们看ViewGroup的dispatchDraw方法:

protected void dispatchDraw(Canvas canvas) { ... for (int i = 0; i < childrenCount; i++) { ... more |= drawChild(canvas, transientChild, drawingTime);

源码很长,这里只贴出关键代码,在dispatchDraw方法中遍历子View并调用drawChild方法,我们继续看drawChild方法:

protected boolean drawChild(Canvas canvas, View child, long drawingTime) { return child.draw(canvas, this, drawingTime); }

drawChild方法实际就是调用了子View的draw方法对子View进行绘制

步骤5:绘制View的阴影效果 步骤6:绘制装饰

步骤6调用了onDrawForeground方法:

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:绘制默认焦点高亮效果 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); } }

学习更多知识,请关注我的个人博客:droidYu


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #注文中源码基于 #Android