自定義View - 4.ViewGroup不會調(diào)用onDraw()的源碼分析

1.繼承ViewGroup為什么不會調(diào)用onDraw方法?

1.1 ViewGroup的源碼,ViewGroup里面沒有onDraw(Canvas canvas)方法.
1.2 找到ViewGroup的父類View.
1.3 主要的畫方法是draw(Canvas canvas) 方法

public void draw(Canvas canvas) {
    // Step 3, draw the content 調(diào)用onDraw方法
     if (!dirtyOpaque) onDraw(canvas);

    // Step 4, draw the children
     dispatchDraw(canvas);

    // Step 6, draw decorations (foreground, scrollbars)
     onDrawForeground(canvas);
}

我們自定義View 繼承 ViewGroup,復(fù)寫onDraw()方法.onDraw()沒有被調(diào)用.
從上面的 draw(Canvas canvas) 的方法的

if (!dirtyOpaque) onDraw(canvas);

只要dirtyOpaque為false才會調(diào)用onDraw方法.而dirtyOpaque 由下面這行代碼控制.

final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);

其實(shí)dirtyOpaque 就是由privateFlags決定的,而privateFlags是由mPrivateFlags決定的.
下面找mPrivateFlags怎么賦值的.
在View的構(gòu)造方法中的computeOpaqueFlags()方法中賦的值.

    protected void computeOpaqueFlags() {
        // Opaque if:
        //   - Has a background
        //   - Background is opaque
        //   - Doesn't have scrollbars or scrollbars overlay

        if (mBackground != null && mBackground.getOpacity() == PixelFormat.OPAQUE) {
            mPrivateFlags |= PFLAG_OPAQUE_BACKGROUND;
        } else {
            mPrivateFlags &= ~PFLAG_OPAQUE_BACKGROUND;
        }

        final int flags = mViewFlags;
        if (((flags & SCROLLBARS_VERTICAL) == 0 && (flags & SCROLLBARS_HORIZONTAL) == 0) ||
                (flags & SCROLLBARS_STYLE_MASK) == SCROLLBARS_INSIDE_OVERLAY ||
                (flags & SCROLLBARS_STYLE_MASK) == SCROLLBARS_OUTSIDE_OVERLAY) {
            mPrivateFlags |= PFLAG_OPAQUE_SCROLLBARS;
        } else {
            mPrivateFlags &= ~PFLAG_OPAQUE_SCROLLBARS;
        }
    }

在ViewGroup的構(gòu)造方法找的initViewGroup()方法

    private void initViewGroup() {
        // ViewGroup doesn't draw by default
        if (!debugDraw()) {
            //這個調(diào)用了View的方法.
            setFlags(WILL_NOT_DRAW, DRAW_MASK);
        }
       ......
    }

setFlags方法導(dǎo)致mPrivateFlags 重新賦值 為 true.所以 if (!dirtyOpaque) onDraw(canvas); 這樣的onDraw() 方法就進(jìn)不來了.導(dǎo)致ViewGroup不會調(diào)用onDraw() 方法.

源碼分析完畢.

2.為什么給設(shè)置背景又可以調(diào)用onDraw方法?

那我們看一下設(shè)置背景的方法

    public void setBackground(Drawable background) {
        setBackgroundDrawable(background);
    }

調(diào)用了setBackgroundDrawable方法

/**
     * @deprecated use {@link #setBackground(Drawable)} instead
     */
    @Deprecated
    public void setBackgroundDrawable(Drawable background) {
        computeOpaqueFlags();
        ....省略一大段代碼
        computeOpaqueFlags();
        ....省略一點(diǎn)點(diǎn)代碼
        invalidate(true);
        invalidateOutline();
    }

我們可以發(fā)現(xiàn)在setBackgroundDrawable調(diào)用了computeOpaqueFlags方法重新計算mPrivateFlags的值.接著調(diào)用invalidate方法觸發(fā)onDraw方法.

3.怎么解決繼承ViewGroup不能會繪制的問題?

目的:改成mPrivateFlags就可以解決了.

3.1 重寫 dispatchDraw(canvas) 方法,在里面做繪制.因?yàn)?dispatchDraw方法沒有做判斷.一定會調(diào)用.

public void draw(Canvas canvas) {
    // Step 3, draw the content 調(diào)用onDraw方法
     if (!dirtyOpaque) onDraw(canvas);

    // Step 4, draw the children
     dispatchDraw(canvas);

    // Step 6, draw decorations (foreground, scrollbars)
     onDrawForeground(canvas);
}

3.2 設(shè)置背景

//默認(rèn)給一個透明背景
setBackgroundColor(Color.TRANSPARENT);

當(dāng)然前提要判斷有沒有背景.

3.3 調(diào)用setFlags改成 mPrivateFlags值.

    public void setWillNotDraw(boolean willNotDraw) {
        setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
    }

還有其他方法.主要的思路就是去改成mPrivateFlags的值.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容