ViewGroup,它本身并沒有任何可畫的東西,它是一個(gè)透明的控件,因些并不會觸發(fā)onDraw,但是你現(xiàn)在給LinearLayout設(shè)置一個(gè)背景色,其實(shí)這個(gè)背景色不管你設(shè)置成什么顏色,系統(tǒng)會認(rèn)為,這個(gè)LinearLayout上面有東西可畫了,因此會調(diào)用onDraw方法。
android代碼一直在優(yōu)化,我看了幾個(gè)版本的源碼,目前,我用的是API30的源碼,再去看ViewGroup為什么不走onDraw()的時(shí)候,已經(jīng)不是一句 if (!dirtyOpaque) 就能決定是否執(zhí)行onDraw()的事了。
原因詳解
在API27中,還是我們熟悉的那個(gè) if 判斷決定 onDraw()的執(zhí)行
在API27以后,你會發(fā)現(xiàn)在draw()方法里找不到 上面這個(gè) if 語句,那么問題來了:他是如何控制 ViewGroup 不執(zhí)行 onDraw() 的呢?
這個(gè)時(shí)候,我們的目光該放在這兩個(gè)片段上了,還是在 View 這個(gè)類里面
片段一:
view.java
/**
* This method is called by ViewGroup.drawChild() to have each child view draw itself.
*
* This is where the View specializes rendering behavior based on layer type,
* and hardware acceleration.
*/
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime)方法
...
// Fast path for layouts with no backgrounds
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchDraw(canvas);
} else {
draw(canvas);
}
...
從這一段我們能獲取兩個(gè)信息:
注釋:
- ViewGroup.drawChild()調(diào)用此方法,使每個(gè)子視圖都繪制自己。這是視圖根據(jù)圖層類型專門處理渲染行為的地方,硬件加速。
- 是否走draw()方法由兩個(gè)標(biāo)志決定 mPrivateFlags & PFLAG_SKIP_DRAW
片段二 :
public RenderNode updateDisplayListIfDirty() {
// 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 (isShowingLayoutBounds()) {
debugDrawFocus(canvas);
}
} else {
draw(canvas);
}
}
從這一段我們能獲取這么個(gè)信息:是否走draw()方法由兩個(gè)標(biāo)志決定 mPrivateFlags & PFLAG_SKIP_DRAW
硬件加速
現(xiàn)在Android默認(rèn)開啟硬件加速,什么是硬件加速呢?為了加快Android繪制速度,適當(dāng)解放cpu資源,Android將一部分繪制放到gpu執(zhí)行。而對應(yīng)的Android里面的canvas,也分為是否支持硬件加速,因此繪制流程也有所差異,流程圖簡示如下:
[]表示該調(diào)用該類里的對應(yīng)方法。
()表示方法里的參數(shù)
從上圖可以看出,不管是否開啟硬件加速,都會經(jīng)歷“跳過繪制”的邏輯判斷,而該判斷的分支就決定了viewGroup的ondraw()方法是否執(zhí)行。如果“跳過繪制”成立,那么調(diào)用dispatchDraw()方法,繼而調(diào)用子view進(jìn)行繪制(如果有子view)。如果“跳過繪制”不成立,那么調(diào)用draw(x1),該方法上面分析過了:會調(diào)用dispatchDraw()和ondraw()方法。
draw(x1)的方法如下:
public void draw(Canvas canvas) {
//省略
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// 繪制自身內(nèi)容
onDraw(canvas);
// 繪制子view
dispatchDraw(canvas);
//省略
// we're done...
return;
}
//省略
}
viewGroup和View初始化時(shí)對于PFLAG_SKIP_DRAW標(biāo)記做了不同的處理。
viewGroup初始化的時(shí)候,默認(rèn)設(shè)置了WILL_NOT_DRAW,從字面意思來看是“不會繪制”標(biāo)記,這個(gè)標(biāo)記是否和PFLAG_SKIP_DRAW有聯(lián)系呢?繼續(xù)查看setFlags方法:
vew.java setFlags方法
//省略
if ((changed & DRAW_MASK) != 0) {
if ((mViewFlags & WILL_NOT_DRAW) != 0) {
if (mBackground != null
|| mDefaultFocusHighlight != null
|| (mForegroundInfo != null && mForegroundInfo.mDrawable != null)) {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
} else {
mPrivateFlags |= PFLAG_SKIP_DRAW;
}
} else {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
}
requestLayout();
invalidate(true);
}
//省略
到此處就比較明朗,將兩個(gè)標(biāo)記值聯(lián)系起來了:
1、如果設(shè)置了WILL_NOT_DRAW標(biāo)記,那么繼續(xù)檢查background、foreground(mDrawable字段)、focusHighLight是否有值,如果三者任意一個(gè)設(shè)置了,那么將PFLAG_SKIP_DRAW標(biāo)記清除,否則將該標(biāo)記加上。
2、如果沒有設(shè)置WILL_NOT_DRAW標(biāo)記,那么將PFLAG_SKIP_DRAW標(biāo)記清除。
如何讓viewGroup onDraw()執(zhí)行
既然知道了MyFrameLayout沒有繪制的原因,那么就有方法讓它執(zhí)行繪制流程。
先來看看WILL_NOT_DRAW
view.java
/**
* If this view doesn't do any drawing on its own, set this flag to
* allow further optimizations. By default, this flag is not set on
* View, but could be set on some View subclasses such as ViewGroup.
*
* Typically, if you override {@link #onDraw(android.graphics.Canvas)}
* you should clear this flag.
*
* @param willNotDraw whether or not this View draw on its own
*/
public void setWillNotDraw(boolean willNotDraw) {
setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
}
/**
* Returns whether or not this View draws on its own.
*
* @return true if this view has nothing to draw, false otherwise
*/
@ViewDebug.ExportedProperty(category = "drawing")
public boolean willNotDraw() {
return (mViewFlags & DRAW_MASK) == WILL_NOT_DRAW;
}
View類里暴露了設(shè)置WILL_NOT_DRAW標(biāo)記的接口:
setWillNotDraw(boolean willNotDraw),可以在viewgroups里使用setWillNotDraw(false)。
不想設(shè)置該標(biāo)記也是可行的,前面說過即使設(shè)置了WILL_NOT_DRAW,后面還是有判斷background、foreground、focusHighLight是否有值。
background:view背景
foreground(mDrawable字段):view前景
focusHighLight:view獲得焦點(diǎn)時(shí)高亮
我們只要設(shè)置了其中一個(gè)值,PFLAG_SKIP_DRAW標(biāo)記將會被清空。
來看看這三個(gè)值如何影響PFLAG_SKIP_DRAW標(biāo)記文章來源:http://www.zghlxwxcb.cn/news/detail-588186.html
view.java
public void setBackgroundDrawable(Drawable background) {
if (background != null) {
if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
requestLayout = true;
}
}
}
public void setForeground(Drawable foreground) {
if (foreground != null) {
if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
}
}
}
private void setDefaultFocusHighlight(Drawable highlight) {
mDefaultFocusHighlight = highlight;
mDefaultFocusHighlightSizeChanged = true;
if (highlight != null) {
if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
}
}
}
總結(jié)
若要ViewGroup onDraw()執(zhí)行,只需要setWillNotDraw(false)、設(shè)置背景、設(shè)置前景、設(shè)置焦點(diǎn)高亮,4個(gè)選項(xiàng)其中一項(xiàng)滿足即可。
當(dāng)然也可以重寫dispatchDraw()方法,在該方法里繪制自定義view的內(nèi)容。文章來源地址http://www.zghlxwxcb.cn/news/detail-588186.html
到了這里,關(guān)于Android ViewGroup onDraw為什么沒調(diào)用的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!