自定義ViewGroup不走onDraw()方法的問題

背景

前幾天,有位朋友和我說,他寫的自定義的View的onDraw()方法沒走戒祠,讓他把代碼發(fā)過來,一看他的這個自定義的View繼承的是一個ViewGroup速种,眾所周知姜盈,當(dāng)我們自定義一個View時會重寫他的3個方法,onMeasure(),onLayout(),onDraw()方法哟旗,但是自定義一個ViewGroup的時候要重寫onMeasure(),onLayout(),dispatchDraw()這3個方法贩据。當(dāng)我告訴他的時候問題解決了。但是闸餐,他又來了一句饱亮,為啥?好吧舍沙,自己之前也沒探究過這些東西近上,我認輸,我查查拂铡!

探究

image

之前在window源碼繪制流程中壹无,最后給了這福圖,其實View最后出現(xiàn)在手機屏幕上是通過ViewRootImpl來實現(xiàn)的感帅,但是有人就說了斗锭,不是應(yīng)該是調(diào)用那三個方法嗎?其實調(diào)用那三個方法是說你要出現(xiàn)個什么形狀失球,而具體的操作View到Window或者Activity上是在ViewRootImpl岖是。

ViewRootImpl中有這些有關(guān)繪制的方法


image

從圖中看方法名也能猜出來,performMeasure(),performLayout(),performDraw()也能想到這個不就和我們自定義View的那三個方法對應(yīng)上了嗎J蛋2虺拧!哈哈哈黔牵,不能光靠猜聪轿,還是看代碼吧!;帧陆错!因為現(xiàn)在的問題是Draw()方法不能用,所以我們只看performDraw()方法金赦。

  private void performDraw() {
        if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
            return;
        }

        final boolean fullRedrawNeeded = mFullRedrawNeeded;
        mFullRedrawNeeded = false;

        mIsDrawing = true;
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
        try {
            draw(fullRedrawNeeded);
        } finally {
            mIsDrawing = false;
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }

        // For whatever reason we didn't create a HardwareRenderer, end any
        // hardware animations that are now dangling
        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();
        }
    ......
 }

從代碼中可以看出調(diào)用了ViewRootImpl的draw方法危号。里面的參數(shù)是一個boolean值,判斷是否要重新繪制素邪⊥饬看下從代碼中可以看出調(diào)用了ViewRootImpl的draw方法

  private void draw(boolean fullRedrawNeeded) {
       ......
        if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                    return;
        }

        if (animating) {
            mFullRedrawNeeded = true;
            scheduleTraversals();
        }
    }

接下來調(diào)用了ViewRootImpl的drawSoftware()方法:

 private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty) {
            ......
            try {
                canvas.translate(-xoff, -yoff);
                if (mTranslator != null) {
                    mTranslator.translateCanvas(canvas);
                }
                canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
                attachInfo.mSetIgnoreDirtyState = false;

                mView.draw(canvas);

                drawAccessibilityFocusedDrawableIfNeeded(canvas);
           }
           ......
    }

看到這里終于出來了,最后調(diào)用了View.draw(canvas)方法。跳入View中的onDraw()方法來看看偷线。

 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;

        /*
         * Draw traversal performs several drawing steps which must be executed
         * in the appropriate order:
         *
         *      1. Draw the background
         *      2. If necessary, save the canvas' layers to prepare for fading
         *      3. Draw view's content
         *      4. Draw children
         *      5. If necessary, draw the fading edges and restore layers
         *      6. Draw decorations (scrollbars for instance)
         */

        // Step 1, draw the background, if needed
        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) {
            // Step 3, draw the content
            if (!dirtyOpaque) onDraw(canvas);//如果有背景色磨确,走onDraw()方法,如果沒有背景色声邦,不走onDraw()方法

            // Step 4, draw the children
            dispatchDraw(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);

            // we're done...
            return;
        }
        ......
    }

從代碼的注釋中能清楚的看到繪制的順序:

  1. 畫背景
  2. 畫canvas的圖層(非必須)
  3. 畫View自己
  4. 畫子View
  5. 如果2執(zhí)行了乏奥,這部要回復(fù)圖層(非必須)

這個并不是重點,下面的這塊代碼才是重點:

  if (!dirtyOpaque) onDraw(canvas);//如果有背景色亥曹,走onDraw()方法邓了,如果沒有背景色,不走onDraw()方法

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

如果有背景色就會走onDraw方法媳瞪。如果沒有背景色骗炉,不走onDraw()方法。再結(jié)合問題蛇受。寫個簡單的demo驗證一下句葵。

自定義一個ViewGroup:

/**
 * Created by xinchang on 2017/7/18.
 */

public class MyViewGroup extends ViewGroup {
    public MyView(Context context) {
        super(context);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.d("tag", "onDraw:執(zhí)行了");
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        Log.d("tag", "dispatchDraw:執(zhí)行了");
    }
}

第一次:不給MyViewGroup添加背景色:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="cx.com.hellotinker.MainActivity">

    <cx.com.hellotinker.MyView
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
       >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </cx.com.hellotinker.MyView>
</android.support.constraint.ConstraintLayout>

運行一下,看結(jié)果:


image

只執(zhí)行了dispatchDraw()這個方法

改下布局文件兢仰,給MyViewGroup添加一個背景色:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="cx.com.hellotinker.MainActivity">

    <cx.com.hellotinker.MyView
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:background="#00ff00"
       >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </cx.com.hellotinker.MyView>
</android.support.constraint.ConstraintLayout>

運行一下乍丈,看結(jié)果:


image

這次看到兩個方法都執(zhí)行了,onDraw方法也執(zhí)行了把将。

結(jié)論

自定義的ViewGroup并不是不會走onDraw()方法轻专,如果有背景色是要走的。正常情況下察蹲,重寫dispatchDraw就好了铭若。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市递览,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌瞳腌,老刑警劉巖绞铃,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異嫂侍,居然都是意外死亡儿捧,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門挑宠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來菲盾,“玉大人,你說我怎么就攤上這事各淀±良” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長临谱。 經(jīng)常有香客問我璃俗,道長,這世上最難降的妖魔是什么悉默? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任城豁,我火速辦了婚禮,結(jié)果婚禮上抄课,老公的妹妹穿的比我還像新娘唱星。我一直安慰自己,他們只是感情好跟磨,可當(dāng)我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布间聊。 她就那樣靜靜地躺著,像睡著了一般吱晒。 火紅的嫁衣襯著肌膚如雪甸饱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天仑濒,我揣著相機與錄音叹话,去河邊找鬼。 笑死墩瞳,一個胖子當(dāng)著我的面吹牛驼壶,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播喉酌,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼热凹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了泪电?” 一聲冷哼從身側(cè)響起般妙,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎相速,沒想到半個月后碟渺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡突诬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年苫拍,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片旺隙。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡绒极,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蔬捷,到底是詐尸還是另有隱情垄提,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站塔淤,受9級特大地震影響摘昌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜高蜂,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一聪黎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧备恤,春花似錦稿饰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至惭笑,卻和暖如春侣姆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背沉噩。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工捺宗, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人川蒙。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓蚜厉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親畜眨。 傳聞我的和親對象是個殘疾皇子昼牛,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容