從點(diǎn)擊App圖標(biāo)到View顯示出來(lái)主要流程(二)源碼解析setContentView主要工作及View整體布局層次

setContentView主要工作以及VIew整體布局層次

View測(cè)量布局繪制流程都是從根View(DecorView)開始的佣赖,DecorView究竟在那里創(chuàng)建熊痴?在Activity.onCreate生命周期中setContentView()主要做了哪些工作;以及Activity中View的整體布局層次微渠;帶著這些疑問(wèn)诫舅,我們來(lái)一步步尋找問(wèn)題的答案卦羡;
在Activity.onCreate中會(huì)調(diào)用setContentView(R.layout.xxx),Activity的視圖由setContentView提供,R.layout.xxx是布局文件的資源id祟滴;

    public void setContentView(@LayoutRes int layoutResID) {
        /* Activity的視圖由setContentView提供振惰,layoutResID是布局文件資源id;
        Window是一個(gè)抽象類,具體實(shí)現(xiàn)位于Phonewindow中 
        */
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

PhoneWindow.setContentView代碼如下:

    //在onResume的makeVisible方法中垄懂,DecorView會(huì)真正完成添加和顯示著兩個(gè)過(guò)程骑晶,那時(shí)Activity的視圖才能被看到
    @Override
    public void setContentView(int layoutResID) {
        // 1.Acitivity剛啟動(dòng)時(shí),mContentParent為null埠偿,會(huì)執(zhí)行installDecor();
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            // 2.將Activity中的布局文件添加到DecorView的mContentParent中
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
    }

在PhoneWindow.setContentView中有兩個(gè)關(guān)鍵的方法:
installDecor()和mLayoutInflater.inflate(layoutResID, mContentParent);
下面來(lái)分別看下這兩個(gè)方法主要做了哪些工作:
PhoneWindow.installDecor();

    private void installDecor() {
        mForceDecorInstall = false;
        //第一次執(zhí)行installDecor時(shí)透罢,mDecor和mContentParent都為null榜晦;
        if (mDecor == null) {
            /*mDecor是DecorView實(shí)例冠蒋,DecorView的根View.
            generateDecor會(huì)創(chuàng)建DecorView*/
            mDecor = generateDecor(-1);
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            //generateLayout主要工作:找到系統(tǒng)默認(rèn)布局,并將默認(rèn)布局文件添加到DecorView中
            mContentParent = generateLayout(mDecor);
        }
    }

PhoneWindow.generateDecor(-1)如下乾胶,可以看出DecorView的是在PhoneWindow.installDecor()中創(chuàng)建的抖剿。

    protected DecorView generateDecor(int featureId) {
        //主要工作是創(chuàng)建并返回DecorView實(shí)例;
        return new DecorView(context, featureId, this, getAttributes());
    }

PhoneWindow.generateLayout(mDecor);

    protected ViewGroup generateLayout(DecorView decor) {
        // Inflate the window decor.
        /*layoutResource是系統(tǒng)默認(rèn)的布局文件的資源id识窿;
        以下代碼根據(jù)設(shè)置的window屬性(notitle斩郎、noactionbar等)查找對(duì)應(yīng)系統(tǒng)根布局文件;
        系統(tǒng)布局文件位于/framworks/base/core/res/layout/screen_title喻频、screen_simple(默認(rèn)布局文件)缩宜;
        這些系統(tǒng)布局文件中都有ID為Content的控件,并且id為content控件基本都是一個(gè)FrameLayout;
        */
        int layoutResource;
        int features = getLocalFeatures();
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            //根據(jù)window屬性選擇對(duì)應(yīng)的默認(rèn)布局文件甥温;
            layoutResource = R.layout.screen_swipe_dismiss;
            setCloseOnSwipeEnabled(true);
        } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleIconsDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_title_icons;
            }
        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
            layoutResource = R.layout.screen_progress;
        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
            // Special case for a window with a custom title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogCustomTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_custom_title;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
            // If no other features and not embedded, only need a title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
                layoutResource = a.getResourceId(
                        R.styleable.Window_windowActionBarFullscreenDecorLayout,
                        R.layout.screen_action_bar);
            } else {
                //根據(jù)window屬性選擇對(duì)應(yīng)的默認(rèn)布局文件锻煌;
                layoutResource = R.layout.screen_title;
            }
            // System.out.println("Title!");
        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
            layoutResource = R.layout.screen_simple_overlay_action_mode;
        } else {
            layoutResource = R.layout.screen_simple;
        }
        //通過(guò)以上代碼可知:設(shè)置window的flag在setContentView之前才能起作用;
        /*DecorView.onResourcesLoaded方法:通過(guò)layoutInfalter將系統(tǒng)默認(rèn)布局加載出來(lái)姻蚓,
        然后調(diào)用decorview的addView方法宋梧,將系統(tǒng)默認(rèn)布局文件加載到DecorView中,這樣系統(tǒng)默認(rèn)布局文件
        就加載到了DecorView中狰挡。
        */
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
        /*通過(guò)findViewById(ID_ANDROID_CONTENT);查找ID為com.android.internal.R.id.content捂龄;布局文件。
        這就是應(yīng)用布局的父view加叁。Phonewindow.findViewById()實(shí)際是獲取Decor.findViewById()方法倦沧;
        因?yàn)镈ecorview已經(jīng)把系統(tǒng)默認(rèn)布局文件添加早自己的view中,默認(rèn)布局中有ID為content布局它匕,找到ID為content 
        FrameLayout布局之后展融,將該framelayout賦值給mContentParent;
        setContentView設(shè)置布局最后是加載到mContentParent中超凳。
        */
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }
        return contentParent;
    }

相關(guān)說(shuō)明:
(1)首先根據(jù)設(shè)置的window屬性(notitle愈污、noactionbar等)查找對(duì)應(yīng)系統(tǒng)根布局文件耀态;系統(tǒng)布局文件位于/framworks/base/core/res/layout/screen_title、screen_simple(默認(rèn)布局文件)暂雹;這些系統(tǒng)布局文件中都有ID為Content的控件首装,并且id為content控件基本都是一個(gè)FrameLayout;
(2)找到系統(tǒng)默認(rèn)布局文件的資源id之后,調(diào)用DecorView.onResourcesLoaded(layoutinflater杭跪,系統(tǒng)默認(rèn)布局文件資源id)
(3)DecorView.onResourcesLoaded方法:步驟一:通過(guò)layoutInfalter將系統(tǒng)默認(rèn)布局加載出來(lái)仙逻,然后調(diào)用decorview的addView方法,將系統(tǒng)默認(rèn)布局文件加載到DecorView中涧尿,這樣系統(tǒng)默認(rèn)布局文件就加載到了DecorView中系奉;
(4)通過(guò)findViewById(ID_ANDROID_CONTENT);查找ID為com.android.internal.R.id.content;布局文件姑廉。這就是應(yīng)用布局的父view缺亮。Phonewindow.findViewById()實(shí)際是獲取Decor.findViewById()方法;因?yàn)镈ecorview已經(jīng)把系統(tǒng)默認(rèn)布局文件添加早自己的view中桥言,默認(rèn)布局中有ID為content布局萌踱,找到ID為content FrameLayout布局之后,將該framelayout賦值給mContentParent号阿;setContentView設(shè)置布局最后是加載到mContentParent中并鸵。
(5)installDecor執(zhí)行之后,回到setContentView中扔涧,繼續(xù)執(zhí)行mLayoutInflater.inflate(layoutResID,mContentParent);layoutResID:我們應(yīng)用中setContentView的布局資源id园担; mContentParent:系統(tǒng)默認(rèn)布局文件中id為content的Framelayout中;這樣setContentView工作就完成了枯夜。
(6)以上解釋了為什么設(shè)置window的屬性必須在setContentView之前才能起作用弯汰;
(7)View整體層次為:DecorView>系統(tǒng)默認(rèn)根布局文件>應(yīng)用setContentView的布局添加到系統(tǒng)默認(rèn)布局文件中Id為android.R.id.content的布局中。
(8)以上已經(jīng)完成將布局文件添加到系統(tǒng)根布局文件中,Android中所有視圖都是通過(guò)Window來(lái)呈現(xiàn)的卤档,此時(shí)DecorView還沒(méi)有被WM添加到Window中蝙泼。Window addView之后,會(huì)調(diào)用ViewRootImpl來(lái)完成界面的繪制工作劝枣;因?yàn)閂iew還沒(méi)有開始繪制汤踏,這時(shí)候是不會(huì)顯示出來(lái)的。View測(cè)量布局繪制是從ViewRootImpl的performTraversals方法中開始的舔腾,
(9)通過(guò)以上分析可以得出View的整體層次結(jié)構(gòu)如下圖所示(轉(zhuǎn)自工匠若水

View整體層次結(jié)構(gòu).png

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末溪胶,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子稳诚,更是在濱河造成了極大的恐慌哗脖,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異才避,居然都是意外死亡橱夭,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門桑逝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)棘劣,“玉大人,你說(shuō)我怎么就攤上這事楞遏〔缦荆” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵寡喝,是天一觀的道長(zhǎng)糙俗。 經(jīng)常有香客問(wèn)我,道長(zhǎng)预鬓,這世上最難降的妖魔是什么巧骚? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮珊皿,結(jié)果婚禮上网缝,老公的妹妹穿的比我還像新娘。我一直安慰自己蟋定,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布草添。 她就那樣靜靜地躺著驶兜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪远寸。 梳的紋絲不亂的頭發(fā)上抄淑,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音驰后,去河邊找鬼肆资。 笑死,一個(gè)胖子當(dāng)著我的面吹牛灶芝,可吹牛的內(nèi)容都是我干的郑原。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼夜涕,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼犯犁!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起女器,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤酸役,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涣澡,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贱呐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了入桂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吼句。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖事格,靈堂內(nèi)的尸體忽然破棺而出惕艳,到底是詐尸還是另有隱情,我是刑警寧澤驹愚,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布远搪,位于F島的核電站,受9級(jí)特大地震影響逢捺,放射性物質(zhì)發(fā)生泄漏谁鳍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一劫瞳、第九天 我趴在偏房一處隱蔽的房頂上張望倘潜。 院中可真熱鬧,春花似錦志于、人聲如沸涮因。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)养泡。三九已至,卻和暖如春奈应,著一層夾襖步出監(jiān)牢的瞬間澜掩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工杖挣, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留肩榕,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓惩妇,卻偏偏與公主長(zhǎng)得像株汉,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子屿附,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

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