ViewGroup的繪制源碼學(xué)習(xí)筆記

ViewGroup

A ViewGroup is a special view that can contain other views. The view group is the base class for layouts and views containers. This class also defines the
android.view.ViewGroup.LayoutParams class which serves as the base class for layouts parameters.

Overview

首先看下ViewGroup的繼承層次:

public abstract class ViewGroup extends View implements ViewParent, ViewManager 

可以看到ViewGroup與View第一個不同點在于衙猪,ViewGroup是一個抽象類哮幢,繼承自View竖般,實現(xiàn)了ViewParent和ViewManager兩個接口

ViewParent和ViewManager

首先是ViewParent這個接口,內(nèi)含的部分方法如下:


ViewParent部分方法

其次是ViewManager的方法:


ViewManager

ViewManager的方法很好理解习勤,對View進(jìn)行的增刪改3個操作

構(gòu)造方法

和View類似类少,ViewGroup也有4個構(gòu)造函數(shù):

public ViewGroup(Context context)
public ViewGroup(Context context, AttributeSet attrs)
public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr)
public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)

但是實際上砌庄,前3個構(gòu)造函數(shù)最終也只是調(diào)用到第4個昭雌,而第4個構(gòu)造函數(shù)的實現(xiàn)也非常簡單明了:

public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);

        initViewGroup();
        initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
    }

即兩個init方法的調(diào)用:

private void initViewGroup() {
        // ViewGroup doesn't draw by default
        if (!debugDraw()) {
            setFlags(WILL_NOT_DRAW, DRAW_MASK);
        }
        mGroupFlags |= FLAG_CLIP_CHILDREN;
        mGroupFlags |= FLAG_CLIP_TO_PADDING;
        mGroupFlags |= FLAG_ANIMATION_DONE;
        mGroupFlags |= FLAG_ANIMATION_CACHE;
        mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;

        if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
            mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
        }

        setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);

        mChildren = new View[ARRAY_INITIAL_CAPACITY];
        mChildrenCount = 0;

        mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
    }

首先initViewGroup()方法對其自身的一些標(biāo)志位等做了初始操作复唤;然后調(diào)用initFromAttributes(),從函數(shù)名稱也可以猜到烛卧,這個函數(shù)作用是從xml屬性中進(jìn)行初始化操作:

private void initFromAttributes(
            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewGroup, defStyleAttr,
                defStyleRes);

        final int N = a.getIndexCount();
        for (int i = 0; i < N; i++) {
            int attr = a.getIndex(i);
            switch (attr) {
                case R.styleable.ViewGroup_clipChildren:
                    setClipChildren(a.getBoolean(attr, true));
                    break;
                case R.styleable.ViewGroup_clipToPadding:
                    setClipToPadding(a.getBoolean(attr, true));
                    break;
                case R.styleable.ViewGroup_animationCache:
                    setAnimationCacheEnabled(a.getBoolean(attr, true));
                    break;
                ……
                case R.styleable.ViewGroup_touchscreenBlocksFocus:
                    setTouchscreenBlocksFocus(a.getBoolean(attr, false));
                    break;
            }
        }

        a.recycle();
    }

繪制相關(guān)

從View的繪制源碼學(xué)習(xí)中已經(jīng)知道佛纫,一個View的繪制,包括measure/layout/draw三個階段总放,那么作為View的子類呈宇,ViewGroup的繪制也無非這3個階段:

measure

因為View的measure()方法是個final方法,因此在ViewGroup中不能重寫改方法间聊,而ViewGroup中也沒有實現(xiàn)onMeasure()方法攒盈,但在measure階段,ViewGroup提供了幾個measure相關(guān)的方法:

protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec)
protected void measureChild(View child, int parentWidthMeasureSpec,
            int parentHeightMeasureSpec)
protected void measureChildWithMargins(View child,
            int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) 

這幾個方法在實現(xiàn)上哎榴,關(guān)鍵的點是一致的型豁,因此選擇相對簡潔的measureChild()來看:

protected void measureChild(View child, int parentWidthMeasureSpec,
            int parentHeightMeasureSpec) {
        final LayoutParams lp = child.getLayoutParams();

        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom, lp.height);

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

最關(guān)鍵的點在于最后一句,child.measure()尚蝌,從View繪制過程已經(jīng)知道迎变,measure階段的目的在于,讓View得到自己所占據(jù)的大小飘言,因此對ViewGroup來說衣形,measure階段,就需要讓自己的所有子View知道自己的大小姿鸿。

layout

在layout階段谆吴,ViewGroup重寫了View的layout()和onlayout()方法

實際上layout并沒有做什么實質(zhì)的工作:

public final void layout(int l, int t, int r, int b) {
        if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
            if (mTransition != null) {
                mTransition.layoutChange(this);
            }
            super.layout(l, t, r, b);
        } else {
            // record the fact that we noop'd it; request layout when transition finishes
            mLayoutCalledWhileSuppressed = true;
        }
    }

更加關(guān)鍵的是onLayout()方法,這里變成了abstract抽象方法苛预,這也是為什么ViewGroup是一個抽象類的原因

    protected abstract void onLayout(b
            oolean changed,
            int l, int t, int r, int b);
draw

在draw階段句狼,ViewGroup里相關(guān)的函數(shù)是dispatchDraw(),drawChild():在dispatchDraw()中遍歷所有的子View热某,對所有子View執(zhí)行drawChild()方法腻菇,而drawChild()方法的實現(xiàn)非常簡單:

protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        return child.draw(canvas, this, drawingTime);
    }

ViewGroup的繪制,最終也就是各個子View自身的繪制昔馋。

總結(jié)

最后總結(jié)一下ViewGroup和View的差異:

  • View是一個普通類筹吐,而ViewGroup是一個抽象類
  • measure階段,View通過measure()和onMeasure()來確定自身的占據(jù)范圍秘遏,ViewGroup通過measureChild()等方法來確定各個子View的占據(jù)范圍
  • layout階段丘薛,View通過layout()和onLayout()函數(shù)來確定位置,而ViewGroup中onLayout()變成抽象方法垄提,需要子類實現(xiàn)榔袋;
  • draw階段周拐,View通過draw()和onDraw()方法來實現(xiàn)繪制铡俐,ViewGroup通過dispatchDraw()和drawChild()方法來讓各個子View繪制自身凰兑。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市审丘,隨后出現(xiàn)的幾起案子吏够,更是在濱河造成了極大的恐慌,老刑警劉巖滩报,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锅知,死亡現(xiàn)場離奇詭異,居然都是意外死亡脓钾,警方通過查閱死者的電腦和手機售睹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來可训,“玉大人昌妹,你說我怎么就攤上這事∥战兀” “怎么了飞崖?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長谨胞。 經(jīng)常有香客問我固歪,道長,這世上最難降的妖魔是什么胯努? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任牢裳,我火速辦了婚禮,結(jié)果婚禮上叶沛,老公的妹妹穿的比我還像新娘蒲讯。我一直安慰自己,他們只是感情好恬汁,可當(dāng)我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布伶椿。 她就那樣靜靜地躺著,像睡著了一般氓侧。 火紅的嫁衣襯著肌膚如雪脊另。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天约巷,我揣著相機與錄音偎痛,去河邊找鬼。 笑死独郎,一個胖子當(dāng)著我的面吹牛踩麦,可吹牛的內(nèi)容都是我干的枚赡。 我是一名探鬼主播,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼谓谦,長吁一口氣:“原來是場噩夢啊……” “哼贫橙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起反粥,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤卢肃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后才顿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體莫湘,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年郑气,在試婚紗的時候發(fā)現(xiàn)自己被綠了幅垮。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡尾组,死狀恐怖忙芒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情演怎,我是刑警寧澤匕争,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站爷耀,受9級特大地震影響甘桑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜歹叮,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一跑杭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧咆耿,春花似錦德谅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至慰技,卻和暖如春椭盏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吻商。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工掏颊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓乌叶,卻偏偏與公主長得像盆偿,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子准浴,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,724評論 2 351