setContentView是如何把布局加上去的

在Android開發(fā)中馏艾,最常見的代碼就是setContentView,然后傳入你寫的布局ID歼捐,那么布局就被加載到界面中了氓拼,系統(tǒng)究竟是怎么被加到界面中的你画,就需要通過源碼來查看了。

點(diǎn)擊setContentView方法桃漾,進(jìn)去會(huì)發(fā)現(xiàn)調(diào)用了以下的代碼

public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

可以看到是通過getWindow方法來設(shè)置布局文件的坏匪,那我們在看一下這個(gè)getWindow做了什么事情,在點(diǎn)擊進(jìn)去看一下

/**
 * Retrieve the current {@link android.view.Window} for the activity.
 * This can be used to directly access parts of the Window API that
 * are not available through Activity/Screen.
 *
 * @return Window The current window, or null if the activity is not
 *         visual.
 */
public Window getWindow() {
    return mWindow;
}

其實(shí)這個(gè)getWindow方法就是返回了當(dāng)前的window窗口對(duì)象呈队,而且通過注釋我們還可以知道剥槐,如果當(dāng)前的Activity不可見的時(shí)候唱歧,這個(gè)window對(duì)象是為空的宪摧,那么這個(gè)window到底是什么,我們在進(jìn)去看一下window類是什么樣的颅崩,

/**
* Abstract base class for a top-level window look and behavior policy.  An
* instance of this class should be used as the top-level view added to the
* window manager. It provides standard UI policies such as a background, title
* area, default key processing, etc.
 *
* <p>The only existing implementation of this abstract class is
* android.view.PhoneWindow, which you should instantiate when needing a
* Window.
*/
public abstract class Window {
 /** Flag for the "options panel" feature.  This is enabled by default. */
    public static final int FEATURE_OPTIONS_PANEL = 0;
    /** Flag for the "no title" feature, turning off the title at the top
    *  of the screen. */
    public static final int FEATURE_NO_TITLE = 1;

關(guān)于window類的源碼截取了一部分几于,可以看到這是一個(gè)抽象類,通過注釋沿后,我們獲取信息沿彭,這個(gè)類有一個(gè)唯一的實(shí)現(xiàn)類PhoneWindow,所以接下來我們就需要主要去分析一下這個(gè)PhoneWindow類了尖滚,去看看這個(gè)類的setContentView做了什么

@Override
public void setContentView(int layoutResID) {
    // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
    // decor, when theme attributes and the like are crystalized. Do not check the feature
    // before this happens.
    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 {
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}

PhoneWindow這個(gè)類中setContentView方法中喉刘,我們重要關(guān)注兩個(gè)方法瞧柔,一個(gè)是installDecormLayoutInflater.inflate(layoutResId,mContentParent);而且在調(diào)用installDecor方法的時(shí)候睦裳,還對(duì)mContentParent進(jìn)行了判斷造锅,那這個(gè)mContentParent是什么呢,我們通過installDector方法點(diǎn)進(jìn)去看一下

if (mDecor == null) {
        mDecor = generateDecor(-1);
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    } else {
        mDecor.setWindow(this);
    }

由于源碼里面中的方法代碼太長了廉邑,這里只做部分截取哥蔚,這里看到,如果mDecor==null的話蛛蒙,我們對(duì)mDecor進(jìn)行了一個(gè)初始化糙箍,初始化的方法是generateDecor(-1),我們點(diǎn)擊去看一下

protected DecorView generateDecor(int featureId) {
    // System process doesn't have application context and in that case we need to directly use
    // the context we have. Otherwise we want the application context, so we don't cling to the
    // activity.
    Context context;
    if (mUseDecorContext) {
        Context applicationContext = getContext().getApplicationContext();
        if (applicationContext == null) {
            context = getContext();
        } else {
            context = new DecorContext(applicationContext, getContext().getResources());
            if (mTheme != -1) {
                context.setTheme(mTheme);
            }
        }
    } else {
        context = getContext();
    }
    return new DecorView(context, featureId, this, getAttributes());
}

可以看到牵祟,其實(shí)這個(gè)方法就是對(duì)DecorView做了一個(gè)初始化深夯,最后也是返回了一個(gè)DecorView

DecorView是window的一個(gè)頂層容器,繼承自FrameLayout

看完這個(gè)generateDecor之后课舍,我們在回到installDecor方法塌西,接著往下看,

if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);
        ...
}

我們發(fā)現(xiàn)筝尾,在初始化mDecor之后捡需,又對(duì)mContentParent進(jìn)行了初始化,那么這個(gè)mContentParent是什么筹淫,在通過generateLayour(mDecor)方法來對(duì)里面的具體實(shí)現(xiàn)進(jìn)行探索

代碼點(diǎn)進(jìn)去有點(diǎn)多站辉,在開始的時(shí)候,是通過系統(tǒng)內(nèi)部的一些樣式來進(jìn)行一些特性樣式的設(shè)置损姜,這里可以略過饰剥,然后真正需要研究的代碼是從注釋中 Inflate the window decor開始
可以在這里看到,源碼中初始化了一個(gè)int layoutResource;那么我們往下研究摧阅,發(fā)現(xiàn)下面就是對(duì)這個(gè)layoutResource字段進(jìn)行賦值操作汰蓉。簡單的理解就是通過不同的樣式來加載系統(tǒng)中一些默認(rèn)的布局文件,

在對(duì)這個(gè)layoutResource字段進(jìn)行賦值之后棒卷,我們重點(diǎn)關(guān)注兩行代碼顾孽,

mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

第一行代碼中,主要是這個(gè)mDecor.onResourcesLoaded()方法比规,傳入了之前賦值的layoutResource字段若厚,這個(gè)方法點(diǎn)進(jìn)去之后發(fā)現(xiàn),其實(shí)就是對(duì)這個(gè)layoutResource指定的布局進(jìn)行的繪制然后設(shè)置給了mDecor蜒什,其實(shí)也就是相當(dāng)于理解為給頂層DecorView設(shè)置了一個(gè)布局测秸,而這個(gè)布局是系統(tǒng)內(nèi)置的,可以通過樣式來指定加載哪些不同的布局文件。

第二行代碼中霎冯,發(fā)現(xiàn)進(jìn)行了一個(gè)findViewById操作铃拇,那這個(gè)ID是什么,點(diǎn)擊去看一下

/**
 * The ID that the main layout in the XML layout file should have.
 */
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;

通過注釋可以獲取到信息就是沈撞,這個(gè)id是主要的入口布局ID锚贱,并且必須有,在獲取到這個(gè)contentParent之后关串,這個(gè)方法就將這個(gè)對(duì)象進(jìn)行了返回拧廊,那這里就很疑問了,為什么這個(gè)id一定含有晋修,我們通過layouttResource字段來看看之前加載的系統(tǒng)的布局文件吧碾。我們以系統(tǒng)中的R.layout.screen_simple布局為例,發(fā)現(xiàn)他的布局是這樣寫的

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<ViewStub android:id="@+id/action_mode_bar_stub"
          android:inflatedId="@+id/action_mode_bar"
          android:layout="@layout/action_mode_bar"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:theme="?attr/actionBarTheme" />
<FrameLayout
     android:id="@android:id/content"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:foregroundInsidePadding="false"
     android:foregroundGravity="fill_horizontal|top"
     android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

可以看出墓卦,這有一個(gè)布局id為content的FrameLayout倦春,所以其實(shí)我們通過findviewViewById來獲取的控件就是這個(gè)FrameLayout,通過查看系統(tǒng)中其他的布局文件落剪,我們都能發(fā)現(xiàn)有一個(gè)ID為content的FrameLayout控件睁本。所以是shuld have

那么這里我們一層層的返回,就會(huì)發(fā)現(xiàn)忠怖,其實(shí)PhoneWindow類中的mContentParent就是DecorView中的一個(gè)FrameLayout呢堰,在回到setContentView方法中,在對(duì)mContentParent進(jìn)行初始化完成后凡泣,調(diào)用了mLayoutInflater.inflate(layoutResID, mContentParent);方法枉疼,這里的layoutResId就是我們傳進(jìn)來的布局ID,然后將布局進(jìn)行填充添加到界面中鞋拟,這樣我們的setContentView的整個(gè)工作就完成了骂维。

總結(jié)

看一張示意圖

WX20190807-154652@2x.png

我們以R.layout.screen_simple.xml為例來進(jìn)行講解,當(dāng)調(diào)用setContentView的時(shí)候贺纲,系統(tǒng)會(huì)先對(duì)DecorView進(jìn)行判斷航闺,如果為空的話就初始化,初始化完DecorView之后猴誊,在對(duì)其布局進(jìn)行一個(gè)初始化潦刃,這個(gè)布局會(huì)根據(jù)開發(fā)者指定的樣式來指定不同的布局,但是每一個(gè)布局文件中都會(huì)有一個(gè)id為contentFrameLayout控件稠肘,初始化完DevorView的布局的時(shí)候福铅,也會(huì)初始化這個(gè)FrameLayout萝毛,源碼中的字段就是mContentParent项阴,在拿到這個(gè)mContentParent之后,會(huì)將我們傳入的布局文件加載到這個(gè)FrameLayout中,這樣我們就能看見自己寫的布局文件了

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末环揽,一起剝皮案震驚了整個(gè)濱河市略荡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌歉胶,老刑警劉巖汛兜,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異通今,居然都是意外死亡粥谬,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門辫塌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漏策,“玉大人,你說我怎么就攤上這事臼氨〔粲鳎” “怎么了?”我有些...
    開封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵储矩,是天一觀的道長感耙。 經(jīng)常有香客問我,道長持隧,這世上最難降的妖魔是什么即硼? 我笑而不...
    開封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮屡拨,結(jié)果婚禮上谦絮,老公的妹妹穿的比我還像新娘。我一直安慰自己洁仗,他們只是感情好层皱,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著赠潦,像睡著了一般叫胖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上她奥,一...
    開封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天瓮增,我揣著相機(jī)與錄音,去河邊找鬼哩俭。 笑死绷跑,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的凡资。 我是一名探鬼主播砸捏,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了垦藏?” 一聲冷哼從身側(cè)響起梆暖,我...
    開封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎掂骏,沒想到半個(gè)月后轰驳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡弟灼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年级解,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片田绑。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蠕趁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出辛馆,到底是詐尸還是另有隱情俺陋,我是刑警寧澤,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布昙篙,位于F島的核電站腊状,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏苔可。R本人自食惡果不足惜缴挖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望焚辅。 院中可真熱鬧映屋,春花似錦、人聲如沸同蜻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽湾蔓。三九已至瘫析,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間默责,已是汗流浹背贬循。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留桃序,地道東北人杖虾。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像媒熊,于是被迫代替她去往敵國和親奇适。 傳聞我的和親對(duì)象是個(gè)殘疾皇子坟比,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

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