視圖樹形結(jié)構(gòu),每一個(gè)節(jié)點(diǎn)均有Java層DisplayListCanvas和底層DisplayListCanvas。在Java層,調(diào)用Canvas#drawXxx方法,如drawPoint,drawPath,drawRect,DisplayListCanvas或Canvas中有JNI#方法,根據(jù)mNativeCanvasWrapper指針獲取底層DisplayListCanvas。底層DisplayListCanvas繼承底層Canvas。頭文件定義在/frameworks/base/libs/hwui/Canvas.h。
此外,除了繪制方法drawXxx,還有變換方法,如translate,scale等,以及save和restore方法,下面會(huì)通過一個(gè)繪圖實(shí)例具體分析這些方法實(shí)現(xiàn)的操作。
前面已經(jīng)介紹過,從頂層視圖DecorView#updateDisplayListIfDirty方法開始繪制樹形視圖結(jié)構(gòu),為了分析簡(jiǎn)單,截取一小段樹分支(虛線框內(nèi)),通過下面視圖結(jié)構(gòu),分析繪制時(shí)具體操作。


容器視圖繪制
虛線框的樹形結(jié)構(gòu)截取如圖。
父視圖0是LinearLayout,三個(gè)子視圖分別是自定義CanvasView和兩個(gè)TextView。
當(dāng)遍歷到達(dá)LinearLayout節(jié)點(diǎn)#updateDisplayListIfDirty方法時(shí),再看一下此方法代碼,如下。
public RenderNode updateDisplayListIfDirty() {
final RenderNode renderNode = mRenderNode;
// ThreadedRenderer是空,直接返回節(jié)點(diǎn)
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
|| !renderNode.isValid()//false,還未記錄繪制
|| (mRecreateDisplayList)) {//重建Canvas
...//省略掉不需要重建Canvas的部分代碼
//第一次進(jìn)來肯定需要建立Canvas,renderNode也還未記錄。
mRecreateDisplayList = true;//重建Canvas
int width = mRight - mLeft;
int height = mBottom - mTop;
int layerType = getLayerType();
//創(chuàng)建DisplayListCanvas
final DisplayListCanvas canvas = renderNode.start(width, height);
//判斷LayerType,以及獲取HardwareLayer的部分代碼省略掉,LinearLayout不需要。
try {
// 一般視圖走硬件渲染都執(zhí)行下面程序
computeScroll();
canvas.translate(-mScrollX, -mScrollY);
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
//LinearLayout視圖會(huì)跳過繪制,則直接派發(fā)給子視圖
dispatchDraw(canvas);
} else {
draw(canvas);//繪制,包括繪制自身,修飾,以及派發(fā),共六個(gè)步驟,此處不執(zhí)行。
}
} finally {
renderNode.end(canvas);//繪制結(jié)束,保存canvas記錄內(nèi)容
setDisplayListProperties(renderNode);
}
} else {
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
}
return renderNode;
}
LinearLayout視圖創(chuàng)建畫布DisplayListCanvas后,因有PFLAG_SKIP_DRAW標(biāo)志,選擇跳過繪制,它沒有設(shè)置Background,在View框架源碼中會(huì)設(shè)置此標(biāo)志。跳過繪制,即不會(huì)走LinearLayout的onDraw方法,另外四個(gè)步驟都不會(huì)觸發(fā),僅僅觸發(fā)父類ViewGroup的dispatchDraw方法。繪制直接向子視圖派發(fā)。
ViewGroup#dispatchDraw方法。
@Override
protected void dispatchDraw(Canvas canvas) {
...
boolean more = false;
final long drawingTime = getDrawingTime();
//LinearLayout的畫布寫入Reorder柵欄
if (usingRenderNodeProperties) canvas.insertReorderBarrier();
...
//繪制子視圖
for (int i = 0; i < childrenCount; i++) {
...
int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
final View child = (preorderedList == null)
? children[childIndex] : preorderedList.get(childIndex);
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
more |= drawChild(canvas, child, drawingTime);
}
}
...
//LinearLayout的畫布寫入Inorder柵欄
if (usingRenderNodeProperties) canvas.insertInorderBarrier();
...
}
//drawChild方法
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
dispatchDraw源碼比較多,主要功能是派發(fā)子視圖繪制。觸發(fā)每個(gè)子視圖三個(gè)參數(shù)的draw重載方法。在遍歷子視圖前后,有兩個(gè)方法,LinearLayout畫布的insertReorderBarrier和insertInorderBarrier方法。
他們?cè)谟|發(fā)底層DisplayListCanvas的方法一樣,只是enableReorder標(biāo)志不同,insertReorderBarrier支持重排。
void DisplayListCanvas::insertReorderBarrier(bool enableReorder) {
flushRestoreToCount();
flushTranslate();
mDeferredBarrierType = enableReorder ? kBarrier_OutOfOrder : kBarrier_InOrder;
}
柵欄設(shè)置成kBarrier_OutOfOrder或kBarrier_InOrder類型,kBarrier_OutOfOrder用于標(biāo)記Chunk的一個(gè)變量值reorderChildren。DisplayListCanvas的prepareDirty方法初始化值默認(rèn)是kBarrier_InOrder,這個(gè)值后續(xù)addOpAndUpdateChunk方法會(huì)用到,在繪制子視圖之前,已經(jīng)創(chuàng)建過第一個(gè)Chunk。
在繪制子視圖前,插入一個(gè)kBarrier_OutOfOrder柵欄,LinearLayout畫布drawXxx方法繪制會(huì)創(chuàng)建一個(gè)新Chunk,該Chunk索引LinearLayout畫布繪制的子視圖節(jié)點(diǎn)。繪制子視圖結(jié)束后,再次插入一個(gè)kBarrier_InOrder柵欄,再次新建一個(gè)Chunk塊,索引LinearLayout畫布后續(xù)繪制的內(nèi)容。
LinearLayout子視圖是葉子節(jié)點(diǎn),子視圖繪制,三個(gè)參數(shù)的draw重載方法。
View#draw方法。
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated();
boolean drawingWithRenderNode = mAttachInfo != null
&& mAttachInfo.mHardwareAccelerated
&& hardwareAcceleratedCanvas;
boolean more = false;
...
RenderNode renderNode = null;
Bitmap cache = null;
...
//硬件渲染
if (drawingWithRenderNode) {
//在這里觸發(fā)子視圖渲染方法。
renderNode = updateDisplayListIfDirty();
if (!renderNode.isValid()) {
renderNode = null;
drawingWithRenderNode = false;
}
}
...
if (!drawingWithDrawingCache) {
if (drawingWithRenderNode) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
//父視圖畫布繪制子視圖RenderNode節(jié)點(diǎn)
((DisplayListCanvas) canvas).drawRenderNode(renderNode);
} else {
...
}
} else if (cache != null) {
...
}
if (restoreTo >= 0) {
canvas.restoreToCount(restoreTo);
}
..
mRecreateDisplayList = false;
return more;
}
draw方法很長,這里指摘取硬件渲染相關(guān)的。關(guān)注兩個(gè)點(diǎn)。
1:子視圖#updateDisplayListIfDirty方法,重建Canvas,傳遞給一個(gè)參數(shù)的draw重載方法,繪制,返回子視圖RenderNode節(jié)點(diǎn),該節(jié)點(diǎn)相關(guān)畫布已經(jīng)完成繪制內(nèi)容記錄。
2:LinearLayout畫布drawRenderNode繪制子視圖RenderNode節(jié)點(diǎn)。
void DisplayListCanvas::drawRenderNode(RenderNode* renderNode) {
DrawRenderNodeOp* op = new (alloc()) DrawRenderNodeOp(
renderNode,
*mState.currentTransform(),
mState.clipIsSimple());
addRenderNodeOp(op);
}
創(chuàng)建一個(gè)DrawRenderNodeOp,DrawRenderNodeOp繼承DrawBoundedOp,DrawBoundedOp繼承DrawOp,基類是DisplayListOp。
size_t DisplayListCanvas::addRenderNodeOp(DrawRenderNodeOp* op) {
//增加一個(gè)繪制操作到mDisplayListData的displayListOps數(shù)組。
int opIndex = addDrawOp(op);
//mDisplayListData是LinearLayout底層存儲(chǔ)繪制數(shù)據(jù)的對(duì)象
//增加操作到DisplayListData的mChildren數(shù)組,代表子視圖
int childIndex = mDisplayListData->addChild(op);
//寫入了一個(gè)繪制子節(jié)點(diǎn)的操作,告訴Chunk的endChildIndex自增。
DisplayListData::Chunk& chunk = mDisplayListData->chunks.editTop();
chunk.endChildIndex = childIndex + 1;
if (op->renderNode()->stagingProperties().isProjectionReceiver()) {
mDisplayListData->projectionReceiveIndex = opIndex;
}
return opIndex;
}
addDrawOp方法增加一個(gè)繪制Op,后面會(huì)詳細(xì)介紹,總之,將DrawRenderNodeOp加入到DisplayListData的displayListOps數(shù)組中。
addChild方法將操作加入到DisplayListData的mChildren數(shù)組中。
注意,因前期插入一個(gè)kBarrier_OutOfOrder柵欄,因此在addDrawOp觸發(fā)的addOpAndUpdateChunk中,會(huì)創(chuàng)建一個(gè)新Chunk塊,寫入DrawRenderNodeOp后,更新該Chunk中指向mChildren數(shù)組的endChildIndex 索引。
三個(gè)葉子RenderNode節(jié)點(diǎn)操作寫入LinearLayout底層數(shù)據(jù)數(shù)組中,他們?cè)谝粋€(gè)Chunk塊中。子視圖繪制完畢,再次插入一個(gè)kBarrier_InOrder柵欄,創(chuàng)建一個(gè)新Chunk。

葉子節(jié)點(diǎn)視圖繪制
葉子節(jié)點(diǎn)繪制主要關(guān)注onDraw方法,CanvasView的onDraw方法,繪制三個(gè)矩形區(qū)域。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.parseColor("#ffffcc")); //畫布顏色
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);//設(shè)置非填充風(fēng)格
Rect rect1 = new Rect(20, 20, 180, 180);
canvas.drawRect(rect1, paint);//繪制一個(gè)正方形
/************************第一層*****************************/
canvas.save();//當(dāng)前狀態(tài)保存
/***********************第二層*****************************/
canvas.translate(180, 180);
Rect rect2 = new Rect(20, 20, 180, 180);
canvas.drawRect(rect2, paint);//再次繪制一個(gè)正方形
/************************第二層***************************/
canvas.restore();//恢復(fù)到第一層狀態(tài)
/************************第一層**************************/
Rect rect3 = new Rect(20, 200, 180, 360);
canvas.drawRect(rect3, paint);//再次繪制一個(gè)正方形
}
四個(gè)Canvas方法
drawRect,繪制一個(gè)矩形區(qū)域。
save,保存當(dāng)前圖層狀態(tài)。
restore,恢復(fù)上一個(gè)圖層狀態(tài)。
translate,兩個(gè)方向x和y,偏移一定距離。
先繪制一個(gè)矩形,然后保存狀態(tài),進(jìn)行偏移,繪制第二個(gè)矩形,第二個(gè)矩形受到偏移影響,恢復(fù)狀態(tài),繪制第三個(gè)矩形,第三個(gè)矩形不受偏移影響。
詳細(xì)分析
執(zhí)行順序
drawRect(1)
save
translate
drawRect(2)
restore
drawRect(3)
1,drawRect:觸發(fā)底層DisplayListCanvas的drawRect方法。該方法是
在DisplayListCanvas的頭文件中定義虛函數(shù),在Canvas的頭文件中也有定義。
底層 DisplayListCanvas#drawRect方法。
void DisplayListCanvas::drawRect(float left, float top, float right, float bottom,
const SkPaint& paint) {
addDrawOp(new (alloc()) DrawRectOp(left, top, right, bottom, refPaint(&paint)));
}
addDrawOp增加一個(gè)繪制Op,DrawRectOp類型,不同的繪制Op不同,如其drawPoints繪制增加的Op是DrawPointsOp類型,總之,他們都繼承DrawOp類。
addDrawOp增加一個(gè)繪制操作DrawOp。DrawOp繼承DisplayListOp類。
底層DisplayListCanvas#addDrawOp方法。
size_t DisplayListCanvas::addDrawOp(DrawOp* op) {
Rect localBounds;
...
mDisplayListData->hasDrawOps = true;
return flushAndAddOp(op);
}
觸發(fā)flushAndAddOp方法。
2,save:觸發(fā)底層DisplayListCanvas的save方法。注意:save和restore一般是成對(duì)出現(xiàn)的。
底層DisplayListCanvas#save方法。
int DisplayListCanvas::save(SkCanvas::SaveFlags flags) {
addStateOp(new (alloc()) SaveOp((int) flags));
return mState.save((int) flags);
}
addStateOp增加狀態(tài)操作StateOp,前面的是增加DrawOp,而現(xiàn)在是StateOp
和DrawOp一樣,狀態(tài)StateOp也繼承DisplayListOp類。
底層DisplayListCanvas#addStateOp方法。
size_t DisplayListCanvas::addStateOp(StateOp* op) {
return flushAndAddOp(op);
}
觸發(fā)flushAndAddOp方法。
除了flushAndAddOp保存,還會(huì)觸發(fā)CanvasState保存。
總結(jié):
DisplayListCanvas的save方法,addStateOp增加一個(gè)SaveOp,addStateOp方法和addDrawOp類似,都觸發(fā)flushAndAddOp方法,入?yún)⑹峭粋€(gè)基類DisplayListOp。
CanvasState#save方法。
int CanvasState::save(int flags) {
return saveSnapshot(flags);
}
int CanvasState::saveSnapshot(int flags) {
mSnapshot = new Snapshot(mSnapshot, flags);
return mSaveCount++;
}
在CanvasState類save操作中,創(chuàng)建一個(gè)Snapshot對(duì)象,封裝當(dāng)前對(duì)象,新建的Snapshot放到鏈表最前面,mSaveCount自增,mSaveCount代表當(dāng)前操作的是位于第幾層次,若上層調(diào)用了restore,則觸發(fā)CanvasState的restore方法,回退到上一層。
可以看出,每一個(gè)層次狀態(tài)的save都保存在Snapshot中。save之后的改變?cè)谛陆ǖ腟napshot保存,restore后回到之前的Snapshot。
3,translate:觸發(fā)底層DisplayListCanvas的translate方法,x和y軸方向上的偏移。
底層DisplayListCanvas#translate方法
void DisplayListCanvas::translate(float dx, float dy) {
if (dx == 0.0f && dy == 0.0f) return;
mHasDeferredTranslate = true;
mTranslateX += dx;
mTranslateY += dy;
flushRestoreToCount();
mState.translate(dx, dy, 0.0f);
}
增加偏移量dx和dy。
flushRestoreToCount方法,在mRestoreSaveCount>=0時(shí)才起作用,即執(zhí)行過restore或restoreToCount。
寫入一個(gè)RestoreToCountOp操作。
CanvasState的translate方法,當(dāng)前mSnapshot的transform觸發(fā)translate偏移。因前面觸發(fā)過一個(gè)save方法,因此有兩個(gè)Snapshot,當(dāng)前mSnapshot是表頭第一個(gè)。
4,restore:觸發(fā)底層DisplayListCanvas的restore方法,恢復(fù)到上一個(gè)繪制狀態(tài)。
底層DisplayListCanvas#restore方法。
void DisplayListCanvas::restore() {
if (mRestoreSaveCount < 0) {
restoreToCount(getSaveCount() - 1);
return;
}
mRestoreSaveCount--;
flushTranslate();
mState.restore();
}
若mRestoreSaveCount已經(jīng)>=0,則直接自減。每次restore只能回退一層。同時(shí)CanvasState回退,內(nèi)部mSaveCount保存層級(jí)。
若mRestoreSaveCount小于0,即有可能是初始值-1,則觸發(fā)restoreToCount方法,getSaveCount獲取當(dāng)前層級(jí)。若執(zhí)行過一個(gè)save,restore時(shí),getSaveCount得到2,那么restoreToCount恢復(fù)的值就是1,mRestoreSaveCount設(shè)置成1。
總結(jié):restore回退一層。
上層直接調(diào)用restoreToCount方法時(shí),直接回退saveCount層,這個(gè)方法中會(huì)設(shè)置mRestoreSaveCount值。
底層DisplayListCanvas#restoreToCount方法。
void DisplayListCanvas::restoreToCount(int saveCount) {
mRestoreSaveCount = saveCount;
flushTranslate();
mState.restoreToCount(saveCount);
}
save和restore成對(duì)出現(xiàn),多個(gè)save后,第一次restore時(shí),mRestoreSaveCount將會(huì)是初始值-1,restoreToCount方法會(huì)第一次設(shè)置其值,設(shè)置成restore回退后的層級(jí)。
即mRestoreSaveCount存儲(chǔ)當(dāng)前層級(jí)。
例如,3個(gè)save后,第一次restore后,則mRestoreSaveCount變?yōu)?。
總結(jié):
在上層一次save,將當(dāng)前的狀態(tài)保存下來,包括偏移,縮放等狀態(tài),后續(xù)的改變?nèi)匀辉诖嘶A(chǔ)上,偏移,繪制等,若執(zhí)行restore,則回到上一個(gè)save保存的狀態(tài)中,中間的改變忽略掉。

1:drawRect,繪制正方形,第一層,寫入DrawRectOp,mRestoreSaveCount是-1,flushRestoreCount無作用,無偏移,flushTranslate無作用。
addOpAndUpdateChunk寫入繪制。
2:save,保存當(dāng)前層級(jí)狀態(tài),mRestoreSaveCount是-1,flushRestoreCount無作用,無偏移,flushTranslate無作用。
addOpAndUpdateChunk寫入狀態(tài)。
3:translate,偏移,第二層,mRestoreSaveCount是-1,flushRestoreCount無作用,設(shè)置偏移值mTranslateX與mTranslateY,當(dāng)前Snapshot(第二層)偏移。
4:drawRect,繪制正方形,第二層,寫入DrawRectOp,mRestoreSaveCount是-1,flushRestoreCount無作用,有偏移,
addOpAndUpdateChunk寫入TranslateOp,并恢復(fù)偏移值。
addOpAndUpdateChunk寫入繪制。
5:restore,恢復(fù)上一個(gè)層級(jí),前面僅有一個(gè)save,因此mRestoreSaveCount是-1,觸發(fā)restoreToCount方法,設(shè)置mRestoreSaveCount=1。無偏移,flushTranslate無作用。
6:drawRect,繪制正方形,第一層,寫入DrawRectOp,mRestoreSaveCount是1,無偏移,flushTranslate無作用,
addOpAndUpdateChunk寫入RestoreToCountOp。并恢復(fù)mRestoreSaveCount為-1。
addOpAndUpdateChunk寫入繪制。
flushAndAddOp方法在上層寫入DrawOp或StateOp時(shí)觸發(fā)。
最終目的是addOpAndUpdateChunk。
若當(dāng)前存在mRestoreSaveCount>=0或偏移,首先寫入相應(yīng)Op,即RestoreToCountOp與TranslateOp。
底層DisplayListCanvas#flushAndAddOp方法。
size_t DisplayListCanvas::flushAndAddOp(DisplayListOp* op) { //
flushRestoreToCount();
flushTranslate();
return addOpAndUpdateChunk(op);
}
三個(gè)方法。
flushRestoreToCount:寫入RestoreToCountOp。
flushTranslate:寫入TranslateOp。
addOpAndUpdateChunk:執(zhí)行寫入。
DisplayListCanvas#flushRestoreToCount方法。
void DisplayListCanvas::flushRestoreToCount() {
if (mRestoreSaveCount >= 0) {
addOpAndUpdateChunk(new (alloc()) RestoreToCountOp(mRestoreSaveCount));
mRestoreSaveCount = -1;
}
}
若mRestoreSaveCount>=0,增加一個(gè)RestoreToCountOp操作。mRestoreSaveCount在剛初始化時(shí)prepareDirty,設(shè)置成-1。
DisplayListCanvas#flushTranslate方法。
void DisplayListCanvas::flushTranslate() {
if (mHasDeferredTranslate) {
if (mTranslateX != 0.0f || mTranslateY != 0.0f) {
addOpAndUpdateChunk(new (alloc()) TranslateOp(mTranslateX, mTranslateY));
mTranslateX = mTranslateY = 0.0f;
}
mHasDeferredTranslate = false;
}
}
mTranslateX或mTranslateY不是0時(shí)寫入TranslateOp。
addOpAndUpdateChunk方法負(fù)責(zé)增加Op,不僅包括RestoreToCountOp和TranslateOp。
DisplayListCanvas#addOpAndUpdateChunk方法。
size_t DisplayListCanvas::addOpAndUpdateChunk(DisplayListOp* op) {//增加op,更新chunk
int insertIndex = mDisplayListData->displayListOps.add(op);
if (mDeferredBarrierType != kBarrier_None) {
// op is first in new chunk
mDisplayListData->chunks.push();
DisplayListData::Chunk& newChunk = mDisplayListData->chunks.editTop();//新Chunk
newChunk.beginOpIndex = insertIndex;
newChunk.endOpIndex = insertIndex + 1;
newChunk.reorderChildren = (mDeferredBarrierType == kBarrier_OutOfOrder);
int nextChildIndex = mDisplayListData->children().size();
newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
mDeferredBarrierType = kBarrier_None;
} else {
// standard case - append to existing chunk
mDisplayListData->chunks.editTop().endOpIndex = insertIndex + 1;//最上面Chunk修改
}
return insertIndex;
}
將操作DisplayListOp加入DisplayListData的內(nèi)部數(shù)組,類型全部都是DisplayListOp,返回插入的索引。
DisplayListData在底層Canvas的prepareDirty時(shí)創(chuàng)建,不管是繪制操作還是狀態(tài)操作,都屬于DisplayListOp類型,按照順序放到相同數(shù)組。

依次是DrawRectOp,SaveOp,TranslateOp,DrawRectOp,RestoreToCountOp,DrawRectOp。
任重而道遠(yuǎn)