概述:
之前分析過一個activity的啓動到顯示流程,具體顯示流程中分析過測量與佈局流程,這裏對具體的繪製渲染流程進行具體的分析。繪製的起始還是從ViewRootImpl開始,在進入到view之後會根據根據是否有邊緣效果稍作區分主要包括以下幾點:
- 繪製背景
- 如果存在邊緣效果的話,保存畫布圖層用於給邊緣效果做準備
- 調用ondraw實現具體的繪製流程
- 繪製子view
- 如果存在邊緣效果的話,繪製邊緣效果,恢復畫布圖層
- 繪製裝飾(滾動條,前景色之類)
大體流程如下圖:
其實從這個大體流程裏也可以知道爲什麼自定義view的時候,如果是繼承自view,那就必須要自己實現ondraw,因爲從這裏流程裏,在view中並沒有具體的繪製操作,只是繪製了前景、背景、邊緣效果,具體的繪製在ondraw中,ondraw需要子類自己去實現。
ViewRootImpl中主要流程代碼如下:
private void performDraw() {
......
try {
//這裏傳入的是true
draw(fullRedrawNeeded);
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
......
//這裏是true
if (mReportNextDraw) {
mReportNextDraw = false;
......
try {
//向窗口管理服務發送界面繪製完成的消息
mWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
}
}
}
draw方法較長,這裏主要關注其進入drawSoftware的流程:
private void draw(boolean fullRedrawNeeded) {
......
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
......
//這裏採用硬件渲染來繪製,會重新走一遍performTraversals流程
mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
} else {
//如果硬件渲染器不爲空,且不可用,則使能它,然後重新繪製之後返回
if (mAttachInfo.mHardwareRenderer != null &&
!mAttachInfo.mHardwareRenderer.isEnabled() &&
mAttachInfo.mHardwareRenderer.isRequested()) {
try {
mAttachInfo.mHardwareRenderer.initializeIfNeeded(
mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
return;
}
mFullRedrawNeeded = true;
scheduleTraversals();
return;
}
//否則調用軟件繪製
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
}
}
......
}
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
//繪製軟件渲染
......
try {
......
dirty.setEmpty();
mIsAnimating = false;
mView.mPrivateFlags |= View.PFLAG_DRAWN;
try {
canvas.translate(-xoff, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
attachInfo.mSetIgnoreDirtyState = false;
//這裏的mView就是Decorview
mView.draw(canvas);
drawAccessibilityFocusedDrawableIfNeeded(canvas);
} finally {
......
}
} finally {
......
return true;
}
Decorview中的draw方法主要調用了父類的方法,Decorview繼承自FrameLayout,draw方法的實現在其祖父類View中:
public void draw(Canvas canvas) {
//第一步,如果沒有陰影效果的話,就繪製背景色
if (!dirtyOpaque) {
drawBackground(canvas);
}
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
//如果沒有水平或者數值邊緣漸變效果,流程就如if中
if (!verticalEdges && !horizontalEdges) {
//第三步,繪製具體內容,這也是所有繼承自view的子view主要衝在的方法
if (!dirtyOpaque) onDraw(canvas);
//第四步,繪製子view,這裏是針對viewgroup類型
dispatchDraw(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
//第六步,繪製前景,如滾動條等
onDrawForeground(canvas);
// ok,完成返回
return;
}
//以下處理的是帶有邊緣效果的情況,這也是完整的繪製流程
boolean drawTop = false;
boolean drawBottom = false;
boolean drawLeft = false;
boolean drawRight = false;
float topFadeStrength = 0.0f;
float bottomFadeStrength = 0.0f;
float leftFadeStrength = 0.0f;
float rightFadeStrength = 0.0f;
int paddingLeft = mPaddingLeft;
final boolean offsetRequired = isPaddingOffsetRequired();
if (offsetRequired) {
paddingLeft += getLeftPaddingOffset();
}
//獲取繪製邊緣效果的left、top、right、bottom四個值
int left = mScrollX + paddingLeft;
int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
int top = mScrollY + getFadeTop(offsetRequired);
int bottom = top + getFadeHeight(offsetRequired);
if (offsetRequired) {
right += getRightPaddingOffset();
bottom += getBottomPaddingOffset();
}
final ScrollabilityCache scrollabilityCache = mScrollCache;
final float fadeHeight = scrollabilityCache.fadingEdgeLength;
int length = (int) fadeHeight;
//如果頂部和底部淡入淡出重疊,則修剪淡入淡出長度重疊的淡化產生奇特的僞像
if (verticalEdges && (top + length > bottom - length)) {
length = (bottom - top) / 2;
}
//同樣方式處理水平方向
if (horizontalEdges && (left + length > right - length)) {
length = (right - left) / 2;
}
//確定邊緣漸變繪製位置
if (verticalEdges) {
topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
drawTop = topFadeStrength * fadeHeight > 1.0f;
bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
}
if (horizontalEdges) {
leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
drawLeft = leftFadeStrength * fadeHeight > 1.0f;
rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
drawRight = rightFadeStrength * fadeHeight > 1.0f;
}
saveCount = canvas.getSaveCount();
int solidColor = getSolidColor();
if (solidColor == 0) {
final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
//根據以上獲取的繪製位置來保存相對位置的圖層
if (drawTop) {
canvas.saveLayer(left, top, right, top + length, null, flags);
}
if (drawBottom) {
canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
}
if (drawLeft) {
canvas.saveLayer(left, top, left + length, bottom, null, flags);
}
if (drawRight) {
canvas.saveLayer(right - length, top, right, bottom, null, flags);
}
} else {
scrollabilityCache.setFadeColor(solidColor);
}
// 步驟三,進行實際內容的繪製
if (!dirtyOpaque) onDraw(canvas);
// 步驟四,繪製子view
dispatchDraw(canvas);
//步驟五,繪製邊緣效果,並恢復圖層
final Paint p = scrollabilityCache.paint;
final Matrix matrix = scrollabilityCache.matrix;
final Shader fade = scrollabilityCache.shader;
if (drawTop) {
matrix.setScale(1, fadeHeight * topFadeStrength);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, top, right, top + length, p);
}
if (drawBottom) {
matrix.setScale(1, fadeHeight * bottomFadeStrength);
matrix.postRotate(180);
matrix.postTranslate(left, bottom);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, bottom - length, right, bottom, p);
}
if (drawLeft) {
matrix.setScale(1, fadeHeight * leftFadeStrength);
matrix.postRotate(-90);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, top, left + length, bottom, p);
}
if (drawRight) {
matrix.setScale(1, fadeHeight * rightFadeStrength);
matrix.postRotate(90);
matrix.postTranslate(right, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(right - length, top, right, bottom, p);
}
canvas.restoreToCount(saveCount);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// 步驟六,繪製前景色
onDrawForeground(canvas);
}
到這裏主題流程分析完成,接下來繼續對小流程進行代碼分析:
繪製背景色:drawBackground
private void drawBackground(Canvas canvas) {
//獲取背景drawable,若沒有就返回
final Drawable background = mBackground;
if (background == null) {
return;
}
//設置背景範圍
setBackgroundBounds();
// 如果硬件加速和硬件渲染都使能,則使用顯示列表來繪製渲染節點
if (canvas.isHardwareAccelerated() && mAttachInfo != null
&& mAttachInfo.mHardwareRenderer != null) {
mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode);
final RenderNode renderNode = mBackgroundRenderNode;
if (renderNode != null && renderNode.isValid()) {
setBackgroundRenderNodeProperties(renderNode);
((DisplayListCanvas) canvas).drawRenderNode(renderNode);
return;
}
}
//繪製前面獲取到的backgroud背景
final int scrollX = mScrollX;
final int scrollY = mScrollY;
if ((scrollX | scrollY) == 0) {
background.draw(canvas);
} else {
canvas.translate(scrollX, scrollY);
background.draw(canvas);
canvas.translate(-scrollX, -scrollY);
}
}
繪製具體內容:onDraw
//view中的ondraw是個空實現,需要子view來實現,比如textview、imageview中都進行了重載,完成具體內容的繪製
protected void onDraw(Canvas canvas) {
}
繪製子view:dispatchDraw
該方法實現在viewgroup中,用於繪製子view
@Override
protected void dispatchDraw(Canvas canvas) {
......
//遍歷每個子view爲其設置動畫
if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
final boolean buildCache = !isHardwareAccelerated();
for (int i = 0; i < childrenCount; i++) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
final LayoutParams params = child.getLayoutParams();
attachLayoutAnimationParameters(child, params, i, childrenCount);
bindLayoutAnimation(child);
}
}
......
}
......
for (int i = 0; i < childrenCount; i++) {
//優先繪製短暫的子view
while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
final View transientChild = mTransientViews.get(transientIndex);
if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
transientChild.getAnimation() != null) {
more |= drawChild(canvas, transientChild, drawingTime);
}
transientIndex++;
if (transientIndex >= transientCount) {
transientIndex = -1;
}
}
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
//然後繪製普通的可見view
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
more |= drawChild(canvas, child, drawingTime);
}
}
while (transientIndex >= 0) {
// 普通視圖之後可能還有其他臨時視圖
final View transientChild = mTransientViews.get(transientIndex);
if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
transientChild.getAnimation() != null) {
more |= drawChild(canvas, transientChild, drawingTime);
}
transientIndex++;
if (transientIndex >= transientCount) {
break;
}
}
if (preorderedList != null) preorderedList.clear();
// 繪製正在變爲不可見的有動畫的view
if (mDisappearingChildren != null) {
final ArrayList<View> disappearingChildren = mDisappearingChildren;
final int disappearingCount = disappearingChildren.size() - 1;
// 遍歷繪製正在變爲不可見的view
for (int i = disappearingCount; i >= 0; i--) {
final View child = disappearingChildren.get(i);
more |= drawChild(canvas, child, drawingTime);
}
}
......
}
drawChild又回到了view中的draw方法,對子view進行繪製操作
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
view中的這個draw方法中,主要處理基於圖層類型和硬件加速的渲染行爲,這裏只關注下關鍵代碼:
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
......
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchDraw(canvas);
} else {
draw(canvas);
}
......
}
至此,繪製流程的簡單分析就結束了,在view的繪製流程中並沒有對界面內容進行繪製,這也就是爲什麼我們自定義view,繼承自view的話,一定要重載ondraw,在ondraw裏做繪製內容的操作。