前言
之前已經(jīng)和大家聊了onLayout的流程,本文將會(huì)繼續(xù)聊一聊onDraw中做了什么?本文將集中關(guān)注軟件渲染,關(guān)于Canvas的api源碼解析暫時(shí)不會(huì)在本文聊,會(huì)專門開一個(gè)Skia源碼解析進(jìn)行分析。
如果遇到問題可以來http://www.lxweimin.com/p/a4fb6a02ad53中討論
正文
performTravel的方法走完onMeasure和onLayout流程后會(huì)走到下面這段代碼段。
文件:/frameworks/base/core/java/android/view/ViewRootImpl.java
if (mFirst) {
if (sAlwaysAssignFocus || !isInTouchMode()) {
if (mView != null) {
if (!mView.hasFocus()) {
mView.restoreDefaultFocus();
} else {
...
}
}
} else {
View focused = mView.findFocus();
if (focused instanceof ViewGroup
&& ((ViewGroup) focused).getDescendantFocusability()
== ViewGroup.FOCUS_AFTER_DESCENDANTS) {
focused.restoreDefaultFocus();
}
}
}
final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible;
final boolean hasWindowFocus = mAttachInfo.mHasWindowFocus && isViewVisible;
final boolean regainedFocus = hasWindowFocus && mLostWindowFocus;
if (regainedFocus) {
mLostWindowFocus = false;
} else if (!hasWindowFocus && mHadWindowFocus) {
mLostWindowFocus = true;
}
if (changedVisibility || regainedFocus) {
boolean isToast = (mWindowAttributes == null) ? false
: (mWindowAttributes.type == WindowManager.LayoutParams.TYPE_TOAST);
...
}
mFirst = false;
mWillDrawSoon = false;
mNewSurfaceNeeded = false;
mActivityRelaunched = false;
mViewVisibility = viewVisibility;
mHadWindowFocus = hasWindowFocus;
if (hasWindowFocus && !isInLocalFocusMode()) {
final boolean imTarget = WindowManager.LayoutParams
.mayUseInputMethod(mWindowAttributes.flags);
if (imTarget != mLastWasImTarget) {
mLastWasImTarget = imTarget;
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null && imTarget) {
imm.onPreWindowFocus(mView, hasWindowFocus);
imm.onPostWindowFocus(mView, mView.findFocus(),
mWindowAttributes.softInputMode,
!mHasHadWindowFocus, mWindowAttributes.flags);
}
}
}
在進(jìn)入onDraw的流程之前,會(huì)先處理焦點(diǎn)。這個(gè)過程中可以分為2大步驟:
- 1.如果是第一次渲染,則說明之前的寬高都是都為0.在requestFocus方法中會(huì)有這個(gè)判斷把整個(gè)焦點(diǎn)集中攔截下來:
private boolean canTakeFocus() {
return ((mViewFlags & VISIBILITY_MASK) == VISIBLE)
&& ((mViewFlags & FOCUSABLE) == FOCUSABLE)
&& ((mViewFlags & ENABLED_MASK) == ENABLED)
&& (sCanFocusZeroSized || !isLayoutValid() || hasSize());
}
而在每一次onMeasure之前,都會(huì)嘗試集中一次焦點(diǎn)的遍歷。其中requestFocusNoSearch方法中,如果沒有測(cè)量過就會(huì)直接返回false。因?yàn)槊恳淮胃鼡Q焦點(diǎn)或者集中焦點(diǎn)都可能伴隨著如背景drawable,statelistDrawable等切換。沒有測(cè)量過就沒有必要做這無(wú)用功(詳情請(qǐng)看View的繪制流程(三) onMeasure
)。
因此此時(shí)為了彌補(bǔ)之前拒絕焦點(diǎn)的行為,會(huì)重新進(jìn)行一次restoreDefaultFocus的行為進(jìn)行requestFocus處理。
- 2.如果存在窗體焦點(diǎn),同時(shí)不是打開了FLAG_LOCAL_FOCUS_MODE標(biāo)志(這是一種特殊情況,一般打上這個(gè)標(biāo)志位只有在startingWindow的快照中才會(huì)有,startingWindow具體是什么可以看看WMS在Activity啟動(dòng)中的職責(zé) 添加窗體(三))。
則會(huì)調(diào)用InputMethodManager的onPostWindowFocus方法啟動(dòng)帶了android.view.InputMethod這個(gè)action的軟鍵盤服務(wù)。詳細(xì)的這里暫時(shí)不展開討論。
onDraw流程
if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
reportNextDraw();
}
boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
if (!cancelDraw && !newSurface) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
performDraw();
} else {
if (isViewVisible) {
scheduleTraversals();
} else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).endChangingAnimations();
}
mPendingTransitions.clear();
}
}
mIsInTraversal = false;
- 1.判斷到如果是第一次調(diào)用draw方法,則會(huì)調(diào)用reportNextDraw。
private void reportNextDraw() {
if (mReportNextDraw == false) {
drawPending();
}
mReportNextDraw = true;
}
void drawPending() {
mDrawsNeededToReport++;
}
能看到實(shí)際上就是設(shè)置mReportNextDraw為true。我們回顧一下前兩個(gè)流程mReportNextDraw參與了標(biāo)志位的判斷。在執(zhí)行onMeasure和onLayout有兩個(gè)大前提,一個(gè)是mStop為false,一個(gè)是mReportNextDraw為true。只要滿足其一就會(huì)執(zhí)行。
這么做的目的只有一個(gè),保證調(diào)用一次onDraw方法。為什么會(huì)這樣呢?performDraw是整個(gè)Draw流程的入口。然而在這個(gè)入口,必須要保證cancelDraw為false以及newSurface為false。
注意,如果是第一次渲染因?yàn)闀?huì)添加進(jìn)新的Surface,此時(shí)newSurface為true(可以看View的繪制流程(二) 繪制的準(zhǔn)備)。所以會(huì)走到下面的分之,如果串口可見則調(diào)用scheduleTraversals執(zhí)行下一次Loop的繪制流程。否則判斷是否有需要執(zhí)行的LayoutTransitions layout動(dòng)畫就執(zhí)行了。
因此第一次是不會(huì)走到onDraw,是從第二次Looper之后View的繪制流程才會(huì)執(zhí)行onDraw。
我們繼續(xù)關(guān)注performDraw的邏輯。
ViewRootImpl performDraw
private void performDraw() {
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
return;
} else if (mView == null) {
return;
}
final boolean fullRedrawNeeded = mFullRedrawNeeded || mReportNextDraw;
mFullRedrawNeeded = false;
mIsDrawing = true;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
boolean usingAsyncReport = false;
if (mReportNextDraw && mAttachInfo.mThreadedRenderer != null
&& mAttachInfo.mThreadedRenderer.isEnabled()) {
usingAsyncReport = true;
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
pendingDrawFinished();
});
}
try {
boolean canUseAsync = draw(fullRedrawNeeded);
if (usingAsyncReport && !canUseAsync) {
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
usingAsyncReport = false;
}
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
if (mAttachInfo.mPendingAnimatingRenderNodes != null) {
final int count = mAttachInfo.mPendingAnimatingRenderNodes.size();
for (int i = 0; i < count; i++) {
mAttachInfo.mPendingAnimatingRenderNodes.get(i).endAllAnimators();
}
mAttachInfo.mPendingAnimatingRenderNodes.clear();
}
if (mReportNextDraw) {
mReportNextDraw = false;
if (mWindowDrawCountDown != null) {
try {
mWindowDrawCountDown.await();
} catch (InterruptedException e) {
Log.e(mTag, "Window redraw count down interrupted!");
}
mWindowDrawCountDown = null;
}
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.setStopped(mStopped);
}
if (mSurfaceHolder != null && mSurface.isValid()) {
SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::postDrawFinished);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
} else if (!usingAsyncReport) {
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.fence();
}
pendingDrawFinished();
}
}
}
我們把整個(gè)流程抽象出來實(shí)際上就是可以分為如下幾個(gè)步驟:
對(duì)于軟件渲染:
- 1.調(diào)用draw方法,遍歷View的層級(jí)。
- 2.如果Surface是生效的,則在SurfaceHolder.Callback的surfaceRedrawNeededAsync回調(diào)中調(diào)用pendingDrawFinished。
- 3.如果是強(qiáng)制同步渲染,則會(huì)直接調(diào)用pendingDrawFinished。
對(duì)于硬件渲染:
- 1.調(diào)用draw方法,遍歷View的層級(jí)。
- 2.通過監(jiān)聽mThreadedRenderer的setFrameCompleteCallback回調(diào)執(zhí)行pendingDrawFinished方法。
我們先關(guān)注軟件渲染的流程。也就是draw和pendingDrawFinished。
ViewRootImpl draw
private boolean draw(boolean fullRedrawNeeded) {
Surface surface = mSurface;
if (!surface.isValid()) {
return false;
}
if (!sFirstDrawComplete) {
synchronized (sFirstDrawHandlers) {
sFirstDrawComplete = true;
final int count = sFirstDrawHandlers.size();
for (int i = 0; i< count; i++) {
mHandler.post(sFirstDrawHandlers.get(i));
}
}
}
scrollToRectOrFocus(null, false);
if (mAttachInfo.mViewScrollChanged) {
mAttachInfo.mViewScrollChanged = false;
mAttachInfo.mTreeObserver.dispatchOnScrollChanged();
}
boolean animating = mScroller != null && mScroller.computeScrollOffset();
final int curScrollY;
if (animating) {
curScrollY = mScroller.getCurrY();
} else {
curScrollY = mScrollY;
}
if (mCurScrollY != curScrollY) {
mCurScrollY = curScrollY;
fullRedrawNeeded = true;
if (mView instanceof RootViewSurfaceTaker) {
((RootViewSurfaceTaker) mView).onRootViewScrollYChanged(mCurScrollY);
}
}
final float appScale = mAttachInfo.mApplicationScale;
final boolean scalingRequired = mAttachInfo.mScalingRequired;
final Rect dirty = mDirty;
if (mSurfaceHolder != null) {
dirty.setEmpty();
if (animating && mScroller != null) {
mScroller.abortAnimation();
}
return false;
}
if (fullRedrawNeeded) {
mAttachInfo.mIgnoreDirtyState = true;
dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
}
mAttachInfo.mTreeObserver.dispatchOnDraw();
int xOffset = -mCanvasOffsetX;
int yOffset = -mCanvasOffsetY + curScrollY;
final WindowManager.LayoutParams params = mWindowAttributes;
final Rect surfaceInsets = params != null ? params.surfaceInsets : null;
if (surfaceInsets != null) {
xOffset -= surfaceInsets.left;
yOffset -= surfaceInsets.top;
dirty.offset(surfaceInsets.left, surfaceInsets.right);
}
...
mAttachInfo.mDrawingTime =
mChoreographer.getFrameTimeNanos() / TimeUtils.NANOS_PER_MS;
boolean useAsyncReport = false;
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
boolean invalidateRoot = accessibilityFocusDirty || mInvalidateRootRequested;
mInvalidateRootRequested = false;
mIsAnimating = false;
if (mHardwareYOffset != yOffset || mHardwareXOffset != xOffset) {
mHardwareYOffset = yOffset;
mHardwareXOffset = xOffset;
invalidateRoot = true;
}
if (invalidateRoot) {
mAttachInfo.mThreadedRenderer.invalidateRoot();
}
dirty.setEmpty();
final boolean updated = updateContentDrawBounds();
if (mReportNextDraw) {
mAttachInfo.mThreadedRenderer.setStopped(false);
}
if (updated) {
requestDrawWindow();
}
useAsyncReport = true;
final FrameDrawingCallback callback = mNextRtFrameCallback;
mNextRtFrameCallback = null;
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this, callback);
} else {
if (mAttachInfo.mThreadedRenderer != null &&
!mAttachInfo.mThreadedRenderer.isEnabled() &&
mAttachInfo.mThreadedRenderer.isRequested() &&
mSurface.isValid()) {
try {
mAttachInfo.mThreadedRenderer.initializeIfNeeded(
mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
return false;
}
mFullRedrawNeeded = true;
scheduleTraversals();
return false;
}
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
return false;
}
}
}
if (animating) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
return useAsyncReport;
}
大致上完成了如下流程:
1.如果surface無(wú)效則直接返回
- sFirstDrawHandlers這個(gè)存儲(chǔ)著runnable靜態(tài)對(duì)象。實(shí)際上是在ActivityThread啟動(dòng)后調(diào)用attach方法通過addFirstDrawHandler添加進(jìn)來的目的只是為了啟動(dòng)jit模式。
3.scrollToRectOrFocus 處理滑動(dòng)區(qū)域或者焦點(diǎn)區(qū)域。如果發(fā)生了滑動(dòng)則回調(diào)TreeObserver.dispatchOnScrollChanged。接下來則通過全局的mScroller通過computeScrollOffset判斷是否需要滑動(dòng)動(dòng)畫。如果需要執(zhí)行動(dòng)畫,則調(diào)用DeocView的onRootViewScrollYChanged,進(jìn)行Y軸上的動(dòng)畫執(zhí)行。
4.通過ViewTreeObserver的dispatchOnDraw開始分發(fā)draw開始繪制的監(jiān)聽者。
5.判斷是否存在surface面上偏移量,有就矯正一次臟區(qū),把偏移量添加上去。
接下來則會(huì)進(jìn)入到硬件渲染和軟件渲染的分支。但是進(jìn)一步進(jìn)行調(diào)用draw的流程有幾個(gè)前提條件:臟區(qū)不為空,需要執(zhí)行動(dòng)畫,輔助服務(wù)發(fā)生了焦點(diǎn)變化
- 6.如果ThreadedRenderer不為空且可用。ThreadedRenderer通過onPreDraw回調(diào)到ViewRootImpl,更新mHardwareYOffset,mHardwareXOffset。如果這兩個(gè)參數(shù)發(fā)生了變化,則說明整個(gè)發(fā)生了硬件繪制的區(qū)域變化,需要從頭遍歷一次所有的區(qū)域設(shè)置為無(wú)效區(qū)域,mThreadedRenderer.invalidateRoot。
最后調(diào)用ThreadedRenderer.draw方法執(zhí)行硬件渲染繪制。并且設(shè)置通過registerRtFrameCallback設(shè)置進(jìn)來的callback設(shè)置到ThreadedRenderer中。
- 7.如果此時(shí)ThreadedRenderer不可用但是不為空,說明此時(shí)需要對(duì)ThreadedRenderer進(jìn)行初始化,調(diào)用scheduleTraversals在下一輪的繪制流程中才進(jìn)行硬件渲染。
8.如果以上情況都不滿足,說明是軟件渲染,則調(diào)用drawSoftware進(jìn)行軟件渲染。
9.如果不許要draw方法遍歷全局的View樹,則判斷是否需要執(zhí)行滑動(dòng)動(dòng)畫,需要?jiǎng)t調(diào)用scheduleTraversals進(jìn)入下一輪的繪制。
本文先拋開硬件渲染,來看看軟件渲染drawSoftware中做了什么。還有scrollToRectOrFocus滑動(dòng)中做了什么?
ViewRootImpl scrollToRectOrFocus
boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
final Rect ci = mAttachInfo.mContentInsets;
final Rect vi = mAttachInfo.mVisibleInsets;
int scrollY = 0;
boolean handled = false;
if (vi.left > ci.left || vi.top > ci.top
|| vi.right > ci.right || vi.bottom > ci.bottom) {
final View focus = mView.findFocus();
if (focus == null) {
return false;
}
View lastScrolledFocus = (mLastScrolledFocus != null) ? mLastScrolledFocus.get() : null;
if (focus != lastScrolledFocus) {
rectangle = null;
}
if (focus == lastScrolledFocus && !mScrollMayChange && rectangle == null) {
} else {
mLastScrolledFocus = new WeakReference<View>(focus);
mScrollMayChange = false;
if (focus.getGlobalVisibleRect(mVisRect, null)) {
if (rectangle == null) {
focus.getFocusedRect(mTempRect);
if (mView instanceof ViewGroup) {
((ViewGroup) mView).offsetDescendantRectToMyCoords(
focus, mTempRect);
}
} else {
mTempRect.set(rectangle);
}
if (mTempRect.intersect(mVisRect)) {
if (mTempRect.height() >
(mView.getHeight()-vi.top-vi.bottom)) {
}
else if (mTempRect.top < vi.top) {
scrollY = mTempRect.top - vi.top;
} else if (mTempRect.bottom > (mView.getHeight()-vi.bottom)) {
scrollY = mTempRect.bottom - (mView.getHeight()-vi.bottom);
} else {
scrollY = 0;
}
handled = true;
}
}
}
}
if (scrollY != mScrollY) {
if (!immediate) {
if (mScroller == null) {
mScroller = new Scroller(mView.getContext());
}
mScroller.startScroll(0, mScrollY, 0, scrollY-mScrollY);
} else if (mScroller != null) {
mScroller.abortAnimation();
}
mScrollY = scrollY;
}
return handled;
}
能看到在這個(gè)過程中實(shí)際上就是處理兩個(gè)區(qū)域mVisibleInsets可見區(qū)域以及mContentInsets內(nèi)容區(qū)域。
實(shí)際上這個(gè)過程就是從根部節(jié)點(diǎn)開始尋找焦點(diǎn),然后整個(gè)畫面定格在焦點(diǎn)處。因?yàn)閙VisibleInsets一般是屏幕中出去過掃描區(qū)的大小,但是內(nèi)容區(qū)域就不一定了,可能內(nèi)容會(huì)超出屏幕大小,因此會(huì)通過mScroller滑動(dòng)定位。
計(jì)算原理如下,分為2個(gè)情況:
- 1.可視區(qū)域的頂部比起獲得了焦點(diǎn)的view的頂部要低,說明這個(gè)view在屏幕外了,需要向下滑動(dòng):
scrollY = mTempRect.top - vi.top;
- 2.如果焦點(diǎn)view的底部比起可視區(qū)域要比可視區(qū)域的低,說明需要向上滑動(dòng),注意滑動(dòng)之后需要展示view,因此滑動(dòng)的距離要減去view的高度:
scrollY = mTempRect.bottom - (mView.getHeight()-vi.bottom);
稍微變一下如下:
scrollY = mTempRect.bottom +vi.bottom - mView.getHeight();
ViewRootImpl drawSoftware
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
final Canvas canvas;
int dirtyXOffset = xoff;
int dirtyYOffset = yoff;
if (surfaceInsets != null) {
dirtyXOffset += surfaceInsets.left;
dirtyYOffset += surfaceInsets.top;
}
try {
dirty.offset(-dirtyXOffset, -dirtyYOffset);
final int left = dirty.left;
final int top = dirty.top;
final int right = dirty.right;
final int bottom = dirty.bottom;
canvas = mSurface.lockCanvas(dirty);
if (left != dirty.left || top != dirty.top || right != dirty.right
|| bottom != dirty.bottom) {
attachInfo.mIgnoreDirtyState = true;
}
canvas.setDensity(mDensity);
} catch (Surface.OutOfResourcesException e) {
handleOutOfResourcesException(e);
return false;
} catch (IllegalArgumentException e) {
mLayoutRequested = true; // ask wm for a new surface next time.
return false;
} finally {
dirty.offset(dirtyXOffset, dirtyYOffset); // Reset to the original value.
}
try {
if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
}
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.draw(canvas);
drawAccessibilityFocusedDrawableIfNeeded(canvas);
} finally {
if (!attachInfo.mSetIgnoreDirtyState) {
attachInfo.mIgnoreDirtyState = false;
}
}
} finally {
try {
surface.unlockCanvasAndPost(canvas);
} catch (IllegalArgumentException e) {
Log.e(mTag, "Could not unlock surface", e);
mLayoutRequested = true; // ask wm for a new surface next time.
//noinspection ReturnInsideFinallyBlock
return false;
}
}
return true;
}
這里面的邏輯和上面硬件渲染邏輯有點(diǎn)相似:
- 1.同樣還是根據(jù)全局的surface的偏移量對(duì)整個(gè)dirty區(qū)域進(jìn)行偏移
- 2.通過Surface.lockCanvas方法映射一個(gè)Canvas對(duì)象,之后所有的繪制行為都在這個(gè)Canvas對(duì)象上。關(guān)于這個(gè)方法,詳細(xì)的原理可以看看我寫的SurfaceView和TextureView 源碼淺析(上)。
- 3.獲得Canvas后,由于上面是對(duì)整個(gè)surface的偏移,因此Canvas作為surface映射出來的繪制對(duì)象也需要進(jìn)行一次偏移。
- 4.調(diào)用DecorView的draw方法,開始對(duì)整個(gè)View樹遍歷。
- 5.遍歷完整個(gè)View樹后,說明所有的信息已經(jīng)會(huì)知道Canvas了,就可以通過surface.unlockCanvasAndPost,把記錄在SkCanvas中的像素?cái)?shù)據(jù)發(fā)送到SF中渲染到屏幕中。關(guān)于第五點(diǎn)詳細(xì)的可以閱讀SurfaceView和TextureView 源碼淺析(上)。后續(xù)的步驟可以閱讀我寫的SF系列文章。
我們繼續(xù)來要來看看draw方法做了什么。
DecorView draw
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (mMenuBackground != null) {
mMenuBackground.draw(canvas);
}
}
能看到整個(gè)DecorView實(shí)際上就是調(diào)用了父類的draw方法后,專門給menu欄的drawable繪制到Canvas中。
View draw
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
int saveCount;
if (!dirtyOpaque) {
drawBackground(canvas);
}
// skip step 2 & 5 if possible (common case)
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
if (!dirtyOpaque) onDraw(canvas);
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
onDrawForeground(canvas);
drawDefaultFocusHighlight(canvas);
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();
}
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) {
if (drawTop) {
canvas.saveUnclippedLayer(left, top, right, top + length);
}
if (drawBottom) {
canvas.saveUnclippedLayer(left, bottom - length, right, bottom);
}
if (drawLeft) {
canvas.saveUnclippedLayer(left, top, left + length, bottom);
}
if (drawRight) {
canvas.saveUnclippedLayer(right - length, top, right, bottom);
}
} else {
scrollabilityCache.setFadeColor(solidColor);
}
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
// Step 5, draw the fade effect and restore layers
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);
drawAutofilledHighlight(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
}
大致上draw方法分為如下幾個(gè)步驟:
- 1.首先在draw方法中先校驗(yàn)mPrivateFlags中打開的標(biāo)志位。還記得上一篇文章聊過的PFLAG_DIRTY_MASK的掩碼實(shí)際上控制的是dirty以及透明兩個(gè)標(biāo)志位。
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
首先校驗(yàn)PFLAG_DIRTY_OPAQUE也就是透明標(biāo)志位是否開啟了,開啟了dirtyOpaque則為true。同時(shí)打開PFLAG_DRAWN標(biāo)志位,說明該View已經(jīng)調(diào)用過了draw方法了。
2.如果dirtyOpaque為false說明不是透明則調(diào)用drawBackground,進(jìn)行背景的繪制。
-
3.校驗(yàn)是否有橫豎方向的邊緣陰影需要繪制,如果不需要?jiǎng)t以此執(zhí)行如下三個(gè)流程:
- 1.執(zhí)行該View重寫的onDraw流程,進(jìn)行繪制
- 2.dispatchDraw 把繪制行為分發(fā)到子View中
- 3.判斷是否有overlay,有則繪制每一個(gè)View的浮層的dispatchDraw
- 4.onDrawForeground 繪制View的前景drawable
- 5.drawDefaultFocusHighlight 繪制默認(rèn)的焦點(diǎn)高亮。
-
4.如果需要繪制上下左右四個(gè)方向的滑輪,則執(zhí)行如下幾個(gè)步驟:
- 1.計(jì)算滑輪的上下左右四個(gè)方向,根據(jù)是橫向還是豎向計(jì)算其長(zhǎng)度
- 2.把這一塊內(nèi)容作為Canvas的非裁剪區(qū)域繪制到外面區(qū)域
- 3.執(zhí)行3.1以及3.2的步驟
- 4.根據(jù)繪制的上下左右四個(gè)方向,對(duì)滑輪進(jìn)行旋轉(zhuǎn)
- 5.執(zhí)行3.3-3.5的步驟
我們關(guān)注核心行為3.1onDraw以及3.2dispatchDraw 以及drawBackground中完成了什么事情.
View drawBackground
private void drawBackground(Canvas canvas) {
final Drawable background = mBackground;
if (background == null) {
return;
}
setBackgroundBounds();
// Attempt to use a display list if requested.
if (canvas.isHardwareAccelerated() && mAttachInfo != null
&& mAttachInfo.mThreadedRenderer != null) {
mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode);
final RenderNode renderNode = mBackgroundRenderNode;
if (renderNode != null && renderNode.isValid()) {
setBackgroundRenderNodeProperties(renderNode);
((DisplayListCanvas) canvas).drawRenderNode(renderNode);
return;
}
}
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);
}
}
能看到這里面繪制的邏輯分為硬件渲染和軟件渲染:
硬件渲染首先會(huì)通過getDrawableRenderNode方法獲取一個(gè)drawable渲染的renderNode,接著調(diào)用canvas的drawRenderNode。從之前我分析的TextureView一文中可以了解到硬件渲染,Canvas實(shí)質(zhì)上就是DisplayListCanvas。
軟件渲染則是調(diào)用draable的draw方法,把像素繪制到canvas智商。
getDrawableRenderNode
private RenderNode getDrawableRenderNode(Drawable drawable, RenderNode renderNode) {
if (renderNode == null) {
renderNode = RenderNode.create(drawable.getClass().getName(), this);
}
final Rect bounds = drawable.getBounds();
final int width = bounds.width();
final int height = bounds.height();
final DisplayListCanvas canvas = renderNode.start(width, height);
canvas.translate(-bounds.left, -bounds.top);
try {
drawable.draw(canvas);
} finally {
renderNode.end(canvas);
}
renderNode.setLeftTopRightBottom(bounds.left, bounds.top, bounds.right, bounds.bottom);
renderNode.setProjectBackwards(drawable.isProjected());
renderNode.setProjectionReceiver(true);
renderNode.setClipToBounds(false);
return renderNode;
}
能看到在繪制一個(gè)硬件渲染的drawable對(duì)象時(shí)候,會(huì)先生成一個(gè)RenderNode,調(diào)用start之后獲取Drawable對(duì)象對(duì)應(yīng)DisplayListCanvas,在調(diào)用drawable的draw方法,把信息繪制到DisplayListCanvas,最后返回renderNode。
再把這個(gè)drawable對(duì)應(yīng)的renderNode添加到當(dāng)前View的Canvas中。
ViewGroup dispatchDraw
View默認(rèn)是留下一個(gè)onDraw的空方法。我們看看dispatchDraw中做了什么。
protected void dispatchDraw(Canvas canvas) {
boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
final int childrenCount = mChildrenCount;
final View[] children = mChildren;
int flags = mGroupFlags;
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);
}
}
final LayoutAnimationController controller = mLayoutAnimationController;
if (controller.willOverlap()) {
mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
}
controller.start();
mGroupFlags &= ~FLAG_RUN_ANIMATION;
mGroupFlags &= ~FLAG_ANIMATION_DONE;
if (mAnimationListener != null) {
mAnimationListener.onAnimationStart(controller.getAnimation());
}
}
int clipSaveCount = 0;
final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
if (clipToPadding) {
clipSaveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
mScrollX + mRight - mLeft - mPaddingRight,
mScrollY + mBottom - mTop - mPaddingBottom);
}
// We will draw our child's animation, let's reset the flag
mPrivateFlags &= ~PFLAG_DRAW_ANIMATION;
mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
boolean more = false;
final long drawingTime = getDrawingTime();
if (usingRenderNodeProperties) canvas.insertReorderBarrier();
final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
int transientIndex = transientCount != 0 ? 0 : -1;
final ArrayList<View> preorderedList = usingRenderNodeProperties
? null : buildOrderedChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
for (int i = 0; i < childrenCount; i++) {
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);
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();
// Draw any disappearing views that have animations
if (mDisappearingChildren != null) {
final ArrayList<View> disappearingChildren = mDisappearingChildren;
final int disappearingCount = disappearingChildren.size() - 1;
// Go backwards -- we may delete as animations finish
for (int i = disappearingCount; i >= 0; i--) {
final View child = disappearingChildren.get(i);
more |= drawChild(canvas, child, drawingTime);
}
}
if (usingRenderNodeProperties) canvas.insertInorderBarrier();
if (clipToPadding) {
canvas.restoreToCount(clipSaveCount);
}
flags = mGroupFlags;
if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
invalidate(true);
}
if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
mLayoutAnimationController.isDone() && !more) {
mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
final Runnable end = new Runnable() {
@Override
public void run() {
notifyAnimationListener();
}
};
post(end);
}
}
這個(gè)過程中做了如下幾件事情:
- 1.判斷是否打開了FLAG_RUN_ANIMATION標(biāo)志位,且允許Layout動(dòng)畫。首先遍歷該viewGroup中所有子View中所有的可見的子View,并且bindLayoutAnimation設(shè)置好每一個(gè)子View對(duì)應(yīng)的Layout動(dòng)畫。
private void bindLayoutAnimation(View child) {
Animation a = mLayoutAnimationController.getAnimationForView(child);
child.setAnimation(a);
}
- LayoutAnimationController 控制Layout動(dòng)畫的控制者調(diào)用start啟動(dòng)動(dòng)畫,并且回調(diào)監(jiān)聽。
3.判斷是否被padding裁剪內(nèi)容區(qū)域,默認(rèn)是開啟的。這個(gè)情況下,則會(huì)滑動(dòng)的區(qū)域,padding區(qū)域,以及viewgroup的區(qū)域,進(jìn)行裁剪,而不是統(tǒng)統(tǒng)都畫到Canvas中。
canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
mScrollX + mRight - mLeft - mPaddingRight,
mScrollY + mBottom - mTop - mPaddingBottom);
4.判斷是否打開了硬件加速。如果沒有,buildOrderedChildList對(duì)當(dāng)前該ViewGroup下所有子View進(jìn)行z軸上的插入排序,從而得知誰(shuí)將繪制在更加更加上方。這個(gè)過程中z軸上的數(shù)值越小,越先調(diào)用drawChild方法繪制到canvas中,也就是層級(jí)越低,被其他子View覆蓋在其上。在處理每一個(gè)孩子對(duì)應(yīng)的drawChild方法之前,會(huì)先處理通過addTransientView添加進(jìn)來的臨時(shí)View。這種方式十分少見,你可以看成臨時(shí)動(dòng)畫一樣的效果,不參加view的onmeasure,onLayout,但是會(huì)繪制出來。需要手動(dòng)的remove掉。
5.繪制通過addDisappearingView添加的消失臨時(shí)View。
6.如果FLAG_NOTIFY_ANIMATION_LISTENER,F(xiàn)LAG_ANIMATION_DONE標(biāo)志位都關(guān)閉了,同時(shí)Layout動(dòng)畫也完成了,所有的drawChild都返回了false,則在下一個(gè)Looper中開始時(shí)機(jī)調(diào)用notifyAnimationListener,通知監(jiān)聽者本次動(dòng)畫已經(jīng)完成。
整個(gè)核心都是drawChild方法,我們來看看drawChild做了什么?
drawChild
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
核心調(diào)用了view的draw方法。但是注意了,這個(gè)draw和上面那個(gè)draw方法不太一樣。
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated();
boolean drawingWithRenderNode = mAttachInfo != null
&& mAttachInfo.mHardwareAccelerated
&& hardwareAcceleratedCanvas;
boolean more = false;
final boolean childHasIdentityMatrix = hasIdentityMatrix();
final int parentFlags = parent.mGroupFlags;
if ((parentFlags & ViewGroup.FLAG_CLEAR_TRANSFORMATION) != 0) {
parent.getChildTransformation().clear();
parent.mGroupFlags &= ~ViewGroup.FLAG_CLEAR_TRANSFORMATION;
}
Transformation transformToApply = null;
boolean concatMatrix = false;
final boolean scalingRequired = mAttachInfo != null && mAttachInfo.mScalingRequired;
final Animation a = getAnimation();
if (a != null) {
more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
concatMatrix = a.willChangeTransformationMatrix();
if (concatMatrix) {
mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
}
transformToApply = parent.getChildTransformation();
} else {
if ((mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_TRANSFORM) != 0) {
// No longer animating: clear out old animation matrix
mRenderNode.setAnimationMatrix(null);
mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
}
if (!drawingWithRenderNode
&& (parentFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
final Transformation t = parent.getChildTransformation();
final boolean hasTransform = parent.getChildStaticTransformation(this, t);
if (hasTransform) {
final int transformType = t.getTransformationType();
transformToApply = transformType != Transformation.TYPE_IDENTITY ? t : null;
concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
}
}
}
concatMatrix |= !childHasIdentityMatrix;
// Sets the flag as early as possible to allow draw() implementations
// to call invalidate() successfully when doing animations
mPrivateFlags |= PFLAG_DRAWN;
if (!concatMatrix &&
(parentFlags & (ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS |
ViewGroup.FLAG_CLIP_CHILDREN)) == ViewGroup.FLAG_CLIP_CHILDREN &&
canvas.quickReject(mLeft, mTop, mRight, mBottom, Canvas.EdgeType.BW) &&
(mPrivateFlags & PFLAG_DRAW_ANIMATION) == 0) {
mPrivateFlags2 |= PFLAG2_VIEW_QUICK_REJECTED;
return more;
}
mPrivateFlags2 &= ~PFLAG2_VIEW_QUICK_REJECTED;
if (hardwareAcceleratedCanvas) {
// Clear INVALIDATED flag to allow invalidation to occur during rendering, but
// retain the flag's value temporarily in the mRecreateDisplayList flag
mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED) != 0;
mPrivateFlags &= ~PFLAG_INVALIDATED;
}
RenderNode renderNode = null;
Bitmap cache = null;
int layerType = getLayerType(); // TODO: signify cache state with just 'cache' local
if (layerType == LAYER_TYPE_SOFTWARE || !drawingWithRenderNode) {
if (layerType != LAYER_TYPE_NONE) {
// If not drawing with RenderNode, treat HW layers as SW
layerType = LAYER_TYPE_SOFTWARE;
buildDrawingCache(true);
}
cache = getDrawingCache(true);
}
if (drawingWithRenderNode) {
renderNode = updateDisplayListIfDirty();
if (!renderNode.isValid()) {
renderNode = null;
drawingWithRenderNode = false;
}
}
int sx = 0;
int sy = 0;
if (!drawingWithRenderNode) {
computeScroll();
sx = mScrollX;
sy = mScrollY;
}
final boolean drawingWithDrawingCache = cache != null && !drawingWithRenderNode;
final boolean offsetForScroll = cache == null && !drawingWithRenderNode;
int restoreTo = -1;
if (!drawingWithRenderNode || transformToApply != null) {
restoreTo = canvas.save();
}
if (offsetForScroll) {
canvas.translate(mLeft - sx, mTop - sy);
} else {
if (!drawingWithRenderNode) {
canvas.translate(mLeft, mTop);
}
if (scalingRequired) {
if (drawingWithRenderNode) {
// TODO: Might not need this if we put everything inside the DL
restoreTo = canvas.save();
}
// mAttachInfo cannot be null, otherwise scalingRequired == false
final float scale = 1.0f / mAttachInfo.mApplicationScale;
canvas.scale(scale, scale);
}
}
float alpha = drawingWithRenderNode ? 1 : (getAlpha() * getTransitionAlpha());
if (transformToApply != null
|| alpha < 1
|| !hasIdentityMatrix()
|| (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) != 0) {
if (transformToApply != null || !childHasIdentityMatrix) {
int transX = 0;
int transY = 0;
if (offsetForScroll) {
transX = -sx;
transY = -sy;
}
if (transformToApply != null) {
if (concatMatrix) {
if (drawingWithRenderNode) {
renderNode.setAnimationMatrix(transformToApply.getMatrix());
} else {
canvas.translate(-transX, -transY);
canvas.concat(transformToApply.getMatrix());
canvas.translate(transX, transY);
}
parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
}
float transformAlpha = transformToApply.getAlpha();
if (transformAlpha < 1) {
alpha *= transformAlpha;
parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
}
}
if (!childHasIdentityMatrix && !drawingWithRenderNode) {
canvas.translate(-transX, -transY);
canvas.concat(getMatrix());
canvas.translate(transX, transY);
}
}
if (alpha < 1 || (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) != 0) {
if (alpha < 1) {
mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_ALPHA;
} else {
mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_ALPHA;
}
parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
if (!drawingWithDrawingCache) {
final int multipliedAlpha = (int) (255 * alpha);
if (!onSetAlpha(multipliedAlpha)) {
if (drawingWithRenderNode) {
renderNode.setAlpha(alpha * getAlpha() * getTransitionAlpha());
} else if (layerType == LAYER_TYPE_NONE) {
canvas.saveLayerAlpha(sx, sy, sx + getWidth(), sy + getHeight(),
multipliedAlpha);
}
} else {
mPrivateFlags |= PFLAG_ALPHA_SET;
}
}
}
} else if ((mPrivateFlags & PFLAG_ALPHA_SET) == PFLAG_ALPHA_SET) {
onSetAlpha(255);
mPrivateFlags &= ~PFLAG_ALPHA_SET;
}
if (!drawingWithRenderNode) {
if ((parentFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0 && cache == null) {
if (offsetForScroll) {
canvas.clipRect(sx, sy, sx + getWidth(), sy + getHeight());
} else {
if (!scalingRequired || cache == null) {
canvas.clipRect(0, 0, getWidth(), getHeight());
} else {
canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight());
}
}
}
if (mClipBounds != null) {
// clip bounds ignore scroll
canvas.clipRect(mClipBounds);
}
}
if (!drawingWithDrawingCache) {
if (drawingWithRenderNode) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
((DisplayListCanvas) canvas).drawRenderNode(renderNode);
} else {
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchDraw(canvas);
} else {
draw(canvas);
}
}
} else if (cache != null) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
if (layerType == LAYER_TYPE_NONE || mLayerPaint == null) {
Paint cachePaint = parent.mCachePaint;
if (cachePaint == null) {
cachePaint = new Paint();
cachePaint.setDither(false);
parent.mCachePaint = cachePaint;
}
cachePaint.setAlpha((int) (alpha * 255));
canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
} else {
int layerPaintAlpha = mLayerPaint.getAlpha();
if (alpha < 1) {
mLayerPaint.setAlpha((int) (alpha * layerPaintAlpha));
}
canvas.drawBitmap(cache, 0.0f, 0.0f, mLayerPaint);
if (alpha < 1) {
mLayerPaint.setAlpha(layerPaintAlpha);
}
}
}
if (restoreTo >= 0) {
canvas.restoreToCount(restoreTo);
}
if (a != null && !more) {
if (!hardwareAcceleratedCanvas && !a.getFillAfter()) {
onSetAlpha(255);
}
parent.finishAnimatingView(this, a);
}
if (more && hardwareAcceleratedCanvas) {
if (a.hasAlpha() && (mPrivateFlags & PFLAG_ALPHA_SET) == PFLAG_ALPHA_SET) {
invalidate(true);
}
}
mRecreateDisplayList = false;
return more;
}
上面這個(gè)方法做了一個(gè)很重要的事情,那就是繪制每一個(gè)View的緩存。在這個(gè)過程中有兩個(gè)比較重要的標(biāo)志位:
boolean drawingWithRenderNode = mAttachInfo != null
&& mAttachInfo.mHardwareAccelerated
&& hardwareAcceleratedCanvas;
drawingWithRenderNode判斷是否需要通過硬件繪制RenderNode。
final boolean drawingWithDrawingCache = cache != null && !drawingWithRenderNode;
drawingWithDrawingCache是否繪制view中的緩存由2點(diǎn)決定,一個(gè)是關(guān)閉硬件渲染,另一個(gè)是緩存的bitmap不為空。
做到的事情如下:
1.首先判斷View中是否通過setAnimation設(shè)置了一個(gè)變換矩陣Transformation動(dòng)畫到View中。如果有,則調(diào)用applyLegacyAnimation方法確定整個(gè)變化動(dòng)畫刷新的范圍在這個(gè)View范圍內(nèi);transformToApply設(shè)置為父容器的Transformation。如果沒有設(shè)置Animation,則判斷是否關(guān)閉了硬件渲染且打開了FLAG_SUPPORT_STATIC_TRANSFORMATIONS。也就是說當(dāng)前的View的父容器是否存在一個(gè)靜態(tài)的變換矩陣,存在則更新到transformToApply中。
2.如果mLayerType為L(zhǎng)AYER_TYPE_SOFTWARE或者關(guān)閉了硬件渲染,說明是一個(gè)軟件渲染,則調(diào)用buildDrawingCache構(gòu)建一個(gè)繪制的緩存,通過getDrawingCache獲取這個(gè)緩存bitmap到cache中。
3.如果drawingWithRenderNode為true,說明在使用硬件渲染。則調(diào)用updateDisplayListIfDirty更新DisplayList中的臟區(qū)。
4.drawingWithRenderNode為false,則調(diào)用computeScroll計(jì)算滑動(dòng)區(qū)域,賦值給sx和sy。
5.如果存在一個(gè)變換矩陣或者關(guān)閉硬件渲染,則調(diào)用canvas的save方法保存當(dāng)前的狀態(tài)。如果offsetForScroll為true(說明此時(shí)是軟件渲染同時(shí)沒有緩存),則調(diào)用canvas的translate方法參數(shù)第四步驟中計(jì)算出來平移距離。
橫向平移:mLeft - sx
縱向平移: mTop - sy
這么做可以把繪制的原點(diǎn)移動(dòng)到平移 平移后的位置,之后所有的繪制都是基于這個(gè)點(diǎn)進(jìn)行的
6.offsetForScroll為false,說明此時(shí)可能需要繪制緩存或者是硬件渲染,只做了兩件事情:平移當(dāng)前的畫布的繪制原點(diǎn),如果需要?jiǎng)t對(duì)整個(gè)畫布進(jìn)行伸縮。
7.如果transformToApply不為空,前提下。發(fā)現(xiàn)打開了硬件渲染,則調(diào)用RenderNode.setAnimationMatrix方法設(shè)置動(dòng)畫的矩陣。如果關(guān)閉,則先回退經(jīng)過平移的Canvas原點(diǎn),先對(duì)動(dòng)畫矩陣進(jìn)行合并后在進(jìn)行滑動(dòng)的移動(dòng)。
8.如果alpha小于1,且判斷到drawingWithDrawingCache關(guān)閉的。則說明可以在當(dāng)前的繪制結(jié)果中進(jìn)行透明度處理。判斷onSetAlpha為false,如果是硬件渲染則調(diào)用renderNode.setAlpha,如果是軟件渲染則調(diào)用canvas.saveLayerAlpha。如果繪制緩存是打開。onSetAlpha如果為true說明整個(gè)透明是由子View決定的,因此先打開PFLAG_ALPHA_SET標(biāo)志位,等待后續(xù)的處理。
9.如果父容器打開了FLAG_CLIP_CHILDREN標(biāo)志位且當(dāng)前的View沒有緩存。說明當(dāng)前的View繪制的結(jié)果需要被父容器裁剪了:
如果進(jìn)行了滑動(dòng),則裁剪區(qū)域如下:
canvas.clipRect(sx, sy, sx + getWidth(), sy + getHeight());
如果沒有緩存,則直接裁剪當(dāng)前的View
canvas.clipRect(0, 0, getWidth(), getHeight());
有緩存:
canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight());
如果需要根據(jù)邊緣進(jìn)行裁剪:
canvas.clipRect(mClipBounds);
10.drawingWithDrawingCache如果是關(guān)閉,且drawingWithRenderNode是打開的,則調(diào)用DisplayListCanvas.drawRenderNode(renderNode) 方法。參數(shù)中的renderNode是updateDisplayListIfDirty方法生成一個(gè)新的DisplayListCanvas。通過drawRenderNode把結(jié)果繪制到父容器的DisplayListCanvas。
11.drawingWithDrawingCache關(guān)閉,drawingWithRenderNode也是關(guān)閉的。說明此時(shí)是直接進(jìn)行繪制。如果打開了PFLAG_SKIP_DRAW標(biāo)志位說明需要直接掉過當(dāng)前的View,直接調(diào)用dispatchDraw分發(fā)View的繪制命令。如果沒有打開,則調(diào)用draw方法。就會(huì)繼續(xù)調(diào)用onDraw后并且dispatchDraw分發(fā)View的繪制方法。
12.drawingWithDrawingCache是打開的,同時(shí)cache緩存不為空,則把cache中的結(jié)果繪制到Canvas中。
- 13.當(dāng)所有子View都繪制結(jié)束之后,則調(diào)用canvas.restoreToCount方法一層層的恢復(fù)繪制狀態(tài)。主要還是恢復(fù)繪制原點(diǎn)。調(diào)用父容器的finishAnimatingView清空所有的Animation以及disappearingAnimation,回調(diào)onAnimationEnd。
在這幾點(diǎn)中,除去Canvas操作(關(guān)于Canvas操作,我會(huì)專門開一個(gè)Skia源碼解析專題進(jìn)行分析)有幾個(gè)比較重要的方法:
- 1.buildDrawingCache 構(gòu)建一個(gè)繪制緩存對(duì)象
- 2.updateDisplayListIfDirty 硬件渲染更新臟區(qū)
buildDrawingCache
public void buildDrawingCache(boolean autoScale) {
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || (autoScale ?
mDrawingCache == null : mUnscaledDrawingCache == null)) {
try {
buildDrawingCacheImpl(autoScale);
} finally {
}
}
}
能看到實(shí)際上就是調(diào)用buildDrawingCacheImpl.
buildDrawingCacheImpl
private void buildDrawingCacheImpl(boolean autoScale) {
mCachingFailed = false;
int width = mRight - mLeft;
int height = mBottom - mTop;
final AttachInfo attachInfo = mAttachInfo;
final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired;
if (autoScale && scalingRequired) {
width = (int) ((width * attachInfo.mApplicationScale) + 0.5f);
height = (int) ((height * attachInfo.mApplicationScale) + 0.5f);
}
final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor;
final boolean opaque = drawingCacheBackgroundColor != 0 || isOpaque();
final boolean use32BitCache = attachInfo != null && attachInfo.mUse32BitDrawingCache;
final long projectedBitmapSize = width * height * (opaque && !use32BitCache ? 2 : 4);
final long drawingCacheSize =
ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize();
if (width <= 0 || height <= 0 || projectedBitmapSize > drawingCacheSize) {
if (width > 0 && height > 0) {
Log.w(VIEW_LOG_TAG, getClass().getSimpleName() + " not displayed because it is"
+ " too large to fit into a software layer (or drawing cache), needs "
+ projectedBitmapSize + " bytes, only "
+ drawingCacheSize + " available");
}
destroyDrawingCache();
mCachingFailed = true;
return;
}
boolean clear = true;
Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache;
if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {
Bitmap.Config quality;
if (!opaque) {
// Never pick ARGB_4444 because it looks awful
// Keep the DRAWING_CACHE_QUALITY_LOW flag just in case
switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) {
case DRAWING_CACHE_QUALITY_AUTO:
case DRAWING_CACHE_QUALITY_LOW:
case DRAWING_CACHE_QUALITY_HIGH:
default:
quality = Bitmap.Config.ARGB_8888;
break;
}
} else {
quality = use32BitCache ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
}
if (bitmap != null) bitmap.recycle();
try {
bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(),
width, height, quality);
bitmap.setDensity(getResources().getDisplayMetrics().densityDpi);
if (autoScale) {
mDrawingCache = bitmap;
} else {
mUnscaledDrawingCache = bitmap;
}
if (opaque && use32BitCache) bitmap.setHasAlpha(false);
} catch (OutOfMemoryError e) {
if (autoScale) {
mDrawingCache = null;
} else {
mUnscaledDrawingCache = null;
}
mCachingFailed = true;
return;
}
clear = drawingCacheBackgroundColor != 0;
}
Canvas canvas;
if (attachInfo != null) {
canvas = attachInfo.mCanvas;
if (canvas == null) {
canvas = new Canvas();
}
canvas.setBitmap(bitmap);
attachInfo.mCanvas = null;
} else {
canvas = new Canvas(bitmap);
}
if (clear) {
bitmap.eraseColor(drawingCacheBackgroundColor);
}
computeScroll();
final int restoreCount = canvas.save();
if (autoScale && scalingRequired) {
final float scale = attachInfo.mApplicationScale;
canvas.scale(scale, scale);
}
canvas.translate(-mScrollX, -mScrollY);
mPrivateFlags |= PFLAG_DRAWN;
if (mAttachInfo == null || !mAttachInfo.mHardwareAccelerated ||
mLayerType != LAYER_TYPE_NONE) {
mPrivateFlags |= PFLAG_DRAWING_CACHE_VALID;
}
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().draw(canvas);
}
} else {
draw(canvas);
}
canvas.restoreToCount(restoreCount);
canvas.setBitmap(null);
if (attachInfo != null) {
attachInfo.mCanvas = canvas;
}
}
- 1.在創(chuàng)建繪制的緩存bitmap之前,如果當(dāng)前View的寬高其中之一小于等于0,或者當(dāng)前View需要內(nèi)存大于最大允許的緩存View大小。
這個(gè)過程中,View繪制后的緩存計(jì)算方法如下:
projectedBitmapSize = width * height * (opaque && !use32BitCache ? 2 : 4);
能看到這個(gè)過程中判斷是否需要透明且關(guān)閉32的緩存,一個(gè)像素就會(huì)2位,否則則是4位。
如果計(jì)算出來的結(jié)果比MAXIMUM_DRAWING_CACHE_SIZE大則銷毀繪制緩存。
private static final int MAXIMUM_DRAWING_CACHE_SIZE = 480 * 800 * 4; // ARGB8888
能看到每一個(gè)View最大只能是由寬480,高800且是ARGB8888模式內(nèi)存大小。
2.如果View的大小發(fā)生了變化,則調(diào)用Bitmap.createBitmap的方法創(chuàng)建一個(gè)對(duì)應(yīng)View大小的Bitmap。
3.如果attachInfo不為空,則判斷是否存在一個(gè)全局的Canvas,如果不存在就創(chuàng)建一個(gè)新的Canvas,并把bitmap設(shè)置到Canvas中。
4.如果需要進(jìn)行伸縮,則伸縮緩存bitmap。如果需要滑動(dòng),則移動(dòng)整個(gè)Canvas的繪制原點(diǎn)。
5.如果PFLAG_SKIP_DRAW 打開了,則直接調(diào)用dispatchDraw,繼續(xù)分發(fā)繪制流程。關(guān)閉了則調(diào)用draw方法,先調(diào)用onDraw后調(diào)用dispatchDraw。
能看到在上面draw方法中因?yàn)闄z測(cè)存在繪制緩存而跳過的流程,在這個(gè)方法中都進(jìn)行處理了。
updateDisplayListIfDirty
public RenderNode updateDisplayListIfDirty() {
final RenderNode renderNode = mRenderNode;
if (!canHaveDisplayList()) {
return renderNode;
}
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
|| !renderNode.isValid()
|| (mRecreateDisplayList)) {
if (renderNode.isValid()
&& !mRecreateDisplayList) {
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchGetDisplayList();
return renderNode; // no work needed
}
mRecreateDisplayList = true;
int width = mRight - mLeft;
int height = mBottom - mTop;
int layerType = getLayerType();
final DisplayListCanvas canvas = renderNode.start(width, height);
try {
if (layerType == LAYER_TYPE_SOFTWARE) {
buildDrawingCache(true);
Bitmap cache = getDrawingCache(true);
if (cache != null) {
canvas.drawBitmap(cache, 0, 0, mLayerPaint);
}
} else {
computeScroll();
canvas.translate(-mScrollX, -mScrollY);
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
// Fast path for layouts with no backgrounds
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().draw(canvas);
}
if (debugDraw()) {
debugDrawFocus(canvas);
}
} else {
draw(canvas);
}
}
} finally {
renderNode.end(canvas);
setDisplayListProperties(renderNode);
}
} else {
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
}
return renderNode;
}
- 1.如果canHaveDisplayList為 false也就是mThreadedRenderer為null,則直接返回。
如果PFLAG_DRAWING_CACHE_VALID關(guān)閉,或者renderNode是無(wú)效的,或者mRecreateDisplayList是false。則進(jìn)入到RenderNode的Canvas繪制中。
2.如果renderNode是有效的,且不需要進(jìn)行重新構(gòu)建整個(gè)硬件渲染的DisplayList(mRecreateDisplayList為false),說明不是第一次繪制了已經(jīng)有繪制結(jié)果了,則調(diào)用dispatchGetDisplayList。
-
3.接下來的情景說明是第一次繪制,renderNode還是屬于無(wú)效狀態(tài)。其初始化流程如下:
- 1.renderNode.start(width, height) 調(diào)用start方法創(chuàng)建一個(gè)全新的DisplayListCanvas
- 2.如果LayerType是LAYER_TYPE_SOFTWARE,就算是Layer的模式是軟件渲染模式,如果打開了硬件渲染模式,還是會(huì)把當(dāng)前View對(duì)應(yīng)繪制緩存bitmap通過setBitmap的方式設(shè)置到DisplayListCanvas中。
- 3.如果不是LAYER_TYPE_SOFTWARE,調(diào)用computeScroll進(jìn)行滑動(dòng)計(jì)算后。把整個(gè)RenderNode的繪制原點(diǎn)退回到滑動(dòng)之前的狀態(tài),并且打上PFLAG_DRAWN和PFLAG_DRAWING_CACHE_VALID兩個(gè)標(biāo)志位。
- 4.如果打開PFLAG_SKIP_DRAW,則直接調(diào)用dispatchDraw,分發(fā)繪制流程。
- 5.沒有打開PFLAG_SKIP_DRAW,則直接調(diào)用draw方法,先回調(diào)onDraw再調(diào)用dispatchDraw進(jìn)行分發(fā)。
4.最后調(diào)用renderNode.end(canvas) 結(jié)束整個(gè)RenderNode的繪制。
我們來看看非第一次繪制時(shí)候dispatchGetDisplayList做了什么?
ViewGroup dispatchGetDisplayList
protected void dispatchGetDisplayList() {
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
final View child = children[i];
if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null)) {
recreateChildDisplayList(child);
}
}
final int transientCount = mTransientViews == null ? 0 : mTransientIndices.size();
for (int i = 0; i < transientCount; ++i) {
View child = mTransientViews.get(i);
if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null)) {
recreateChildDisplayList(child);
}
}
if (mOverlay != null) {
View overlayView = mOverlay.getOverlayView();
recreateChildDisplayList(overlayView);
}
if (mDisappearingChildren != null) {
final ArrayList<View> disappearingChildren = mDisappearingChildren;
final int disappearingCount = disappearingChildren.size();
for (int i = 0; i < disappearingCount; ++i) {
final View child = disappearingChildren.get(i);
recreateChildDisplayList(child);
}
}
}
能看到實(shí)際上很簡(jiǎn)單,對(duì)4種View進(jìn)行recreateChildDisplayList處理。
- 1.所有的可見子View或者帶著動(dòng)畫的子View
- 2.mTransientViews 通過addTransientView添加進(jìn)來的臨時(shí)View
- 3.overlayView 每一個(gè)View的浮層
- 4.mDisappearingChildren 通過addDisappearingView 添加進(jìn)來的當(dāng)View移除時(shí)候需要的動(dòng)畫View。
ViewGroup recreateChildDisplayList
private void recreateChildDisplayList(View child) {
child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) != 0;
child.mPrivateFlags &= ~PFLAG_INVALIDATED;
child.updateDisplayListIfDirty();
child.mRecreateDisplayList = false;
}
能看到這個(gè)過程實(shí)際上就是調(diào)用了子View的updateDisplayListIfDirty方法。
pendingDrawFinished
當(dāng)一切都處理完畢之后,就會(huì)調(diào)用pendingDrawFinished。如果mDrawsNeededToReport計(jì)數(shù)為0,則說明所有需要繪制的命令全部完成了。最后調(diào)用reportDrawFinished。
void pendingDrawFinished() {
if (mDrawsNeededToReport == 0) {
throw new RuntimeException("Unbalanced drawPending/pendingDrawFinished calls");
}
mDrawsNeededToReport--;
if (mDrawsNeededToReport == 0) {
reportDrawFinished();
}
}
reportDrawFinished
private void reportDrawFinished() {
try {
mDrawsNeededToReport = 0;
mWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
// Have fun!
}
}
能看到最后會(huì)調(diào)用reportDrawFinished,通知WindowSession已經(jīng)finishDrawing。
public void finishDrawing(IWindow window) {
if (WindowManagerService.localLOGV) Slog.v(
TAG_WM, "IWindow finishDrawing called for " + window);
mService.finishDrawingWindow(this, window);
}
WMS finishDrawingWindow
void finishDrawingWindow(Session session, IWindow client) {
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mWindowMap) {
WindowState win = windowForClientLocked(session, client, false);
if (win != null && win.mWinAnimator.finishDrawingLocked()) {
if ((win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
win.getDisplayContent().pendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
win.setDisplayLayoutNeeded();
mWindowPlacerLocked.requestTraversal();
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
會(huì)調(diào)用WindowState的setDisplayLayoutNeeded。設(shè)置DisplayContent的mLayoutNeeded為true。
調(diào)用WindowSurfacePlacer的requestTraversal。而這個(gè)方法會(huì)在AnimationHandler 窗體動(dòng)畫的handler中調(diào)用performSurfacePlacement。而這里的邏輯可以閱讀WMS在Activity啟動(dòng)中的職責(zé) 計(jì)算窗體的大小
總結(jié)
到這列就完成了onDraw的解析。從onMeasure,onLayout,onDraw四個(gè)流程已經(jīng)過了一遍。但是還沒有仔細(xì)聊聊硬件渲染,但是沒關(guān)系,從軟件渲染也能一窺整個(gè)核心流程了。
老規(guī)矩,先來一副時(shí)序圖:
整個(gè)onDraw的入口依次執(zhí)行了如下的方法:
- 1.執(zhí)行該View的background 背景的繪制
- 2.執(zhí)行該View重寫的onDraw流程,進(jìn)行繪制
- 3.dispatchDraw 把繪制行為分發(fā)到子View中
- 4.判斷是否有overlay,有則繪制每一個(gè)View的浮層的dispatchDraw
- 5.onDrawForeground 繪制View的前景drawable
- 6.drawDefaultFocusHighlight 繪制默認(rèn)的焦點(diǎn)高亮。
每一次進(jìn)行一次onDraw之前對(duì)dirtyOpaque標(biāo)志位進(jìn)行判斷,實(shí)際上就是判斷是否是透明的,是透明的就不會(huì)調(diào)用該View的onDraw方法。
其中dispatchDraw進(jìn)行繪制行為分發(fā)后,就會(huì)調(diào)用drawChild的方法會(huì)每一個(gè)子View的draw方法。同時(shí)會(huì)通過buildOrderedChildList插入排序計(jì)算z軸上的繪制的順序,保證z軸上會(huì)根據(jù)大小順序調(diào)用drawChild進(jìn)行覆蓋繪制。
這個(gè)過程中,軟件渲染過程中會(huì)伴隨著繪制一個(gè)Bitmap的緩存。每一個(gè)View都能夠申請(qǐng)到的緩存最大數(shù)值就是:
(寬/高)480 * (寬/高)800 * 4(ARGB8888)
如果超出這個(gè)數(shù)值就不會(huì)出現(xiàn)緩存,或者直到OOM了也會(huì)銷毀緩存。
當(dāng)執(zhí)行完畢之后,必定會(huì)調(diào)用WMS的finishDrawingWindow,告訴WMS ViewRootImpl已經(jīng)完成了繪制工作。這個(gè)方法會(huì)設(shè)置DisplayContent的mLayoutNeeded為true。這樣就能告訴WMS,當(dāng)下一輪WMS的relayoutWindow對(duì)窗體進(jìn)行重新測(cè)量的時(shí)候,允許遍歷DisplayContent所有的內(nèi)容窗體(詳細(xì)的可以看看我寫的WMS在Activity啟動(dòng)中的職責(zé) 計(jì)算窗體的大小)。
本文已經(jīng)涉及到不少關(guān)于硬件渲染的邏輯,下一篇就來聊聊硬件繪制的原理。