Activity.setContentView 方法源碼解析

一個 界面的布局關(guān)系

界面 --> StatusBar && Activity --> PhoneWindow --> DecorView --> Title && contentView --> layout

Activity 的 setContentView 方法和 AppCompatActivity 的 setContentView 是不同的肪康,接下來逐個分析

Activity 的 setContentView

// Activity
public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID); // 調(diào)用 PhoneWindow 的 setContentView 方法
    initWindowDecorActionBar(); // 初始化 ActionBar
}

// 初始化 ActionBar
private void initWindowDecorActionBar() {
    Window window = getWindow();
    window.getDecorView();

    // 判讀 Activity 是否帶有 ActionBar缺虐,如果沒有糠亩,直接返回
    if (isChild() || !window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
        return;
    }

    // 如果有 ActionBar 則創(chuàng)建一個默認的 ActionBar 锭魔,并為 ActionBar 設置 Activity 相關(guān)的 Icon 和 Logo
    mActionBar = new WindowDecorActionBar(this);
    mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);

    mWindow.setDefaultIcon(mActivityInfo.getIconResource());
    mWindow.setDefaultLogo(mActivityInfo.getLogoResource());
}    

// PhoneWindow
@Override
public void setContentView(int layoutResID) {
    
    if (mContentParent == null) {
        installDecor(); // 初始化 DecorView 也就是整個 Window 的根 View,主要過程為 new 一個 DecorView
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews(); // 移除 mContentParent 中的所有子 View mContentParent 為 DecorView 中的用于顯示用戶設置的布局的 ViewGroup
    }

    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { // 有過度動畫時的處理
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                getContext());
        transitionTo(newScene);
    } else {
        mLayoutInflater.inflate(layoutResID, mContentParent); // 由 layoutId 初始化 View 并添加到 mContentParent
    }
    ...
}

由此可以看出 Activity 中使用過 PhoneWindow 來向界面中添加用戶設置的布局的。并且 DecorView 的創(chuàng)建也是在 PhoneWindow 中完成的。

AppCompatActivity 的 setContentView 方法

// AppCompatActivity
@Override
public void setContentView(@LayoutRes int layoutResID) {
    getDelegate().setContentView(layoutResID); // getDelegate() 方法的返回值是一個 AppCompatDelegate 對象
}

// AppCompatActivity
public AppCompatDelegate getDelegate() {
    if (mDelegate == null) {
        mDelegate = AppCompatDelegate.create(this, this);
    }
    return mDelegate;
}

// AppCompatDelegate
private static AppCompatDelegate create(Context context, Window window,
        AppCompatCallback callback) {
    final int sdk = Build.VERSION.SDK_INT;
    if (BuildCompat.isAtLeastN()) {
        return new AppCompatDelegateImplN(context, window, callback);
    } else if (sdk >= 23) {
        return new AppCompatDelegateImplV23(context, window, callback);
    } else if (sdk >= 14) {
        return new AppCompatDelegateImplV14(context, window, callback);
    } else if (sdk >= 11) {
        return new AppCompatDelegateImplV11(context, window, callback);
    } else {
        return new AppCompatDelegateImplV9(context, window, callback);
    }
}

// AppCompatDelegateImplV9 所有 AppCompatDelegate 子類的 setContentView 繼承自了 AppCompatDelegateImplV9 的方法 
@Override
public void setContentView(int resId) {
    ensureSubDecor();
    ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
    contentParent.removeAllViews();
    LayoutInflater.from(mContext).inflate(resId, contentParent);
    mOriginalWindowCallback.onContentChanged();
}

AppCompatDelegateImplV9 中的 mSubDecor 對象是一個 ViewGroup,其構(gòu)造過程為根據(jù) Activity 的主題創(chuàng)建一個根 View洪己,其創(chuàng)建好之后,就會調(diào)用 PhoneWindow 的 setContentView 方法秩仆,將其作為參數(shù)添加到 Decorview 中

AppCompatActivity 中是通過 AppCompatDelegateImplV9 這個類來完成向界面中添加布局的

AppCompatDelegateImplV9 的 setContentView 方法中码泛,我們將需要添加的布局添加到 mSubDecor ,也就添加到了 DecorView 中澄耍,Activity 的 onResume 方法回調(diào)之后也就顯示到了界面上噪珊。

總結(jié)

由源碼分析得出,setContentView 方法完成之后齐莲,Activity 需要顯示的布局已經(jīng)添加到了 DecorView 中

在 Activity 的 onResume 方法回調(diào)之后痢站,布局就會顯示到界面中。

有關(guān) LayoutInflater 的工作過程选酗,請期待下篇博客阵难。

setContentView 的重載方法

setContentView(int layoutId);

setContentView(View view);

setContentView(View view,LayoutParams params);

setContentView(int layoutId);

這個方法有三個重載,第一個我們剛才已經(jīng)分析過了芒填,不管是 Activity 或者是 AppCompatActivity 呜叫,在添加是都會使用布局文件中自己設置寬高來構(gòu)造 LayoutParams ,根布局在界面上的顯示的寬高也完全由 xml 中設置的決定空繁。

setContentView(View view);

Activity Activity 的 setContentView 加載是通過 PhoneWindow 的 setContentView 方法實現(xiàn)的,如果 setContentView 的參數(shù)為一個 View 對象朱庆,不管是否 View 有自己的 LayoutParams 屬性盛泡,都會將 View 的 LayoutParams 設為新的,并且新的的寬高都為 MATH_PARENT

AppCompatActivity
AppCompatActivity setContentView 在添加 View 時娱颊,如果 View 有 LayoutParams 傲诵,則使用 View 原有的 LayoutParams ,如果 View 沒有 LayoutParams 箱硕,則使用寬高都為 MATH_PARENT 的 LayoutParems (在 ViewGroup 添加 View 時如果 View 沒有 LayoutParams 拴竹,此時應該是 默認 Warp_content,不過這里卻是 MATH_PARENT 剧罩,在代碼中沒有找到原因)

setContentView(View view,LayoutParams params);

帶 LayoutParams 參數(shù)的栓拜,不管是 Activity 還是 AppCompatActivity ,都會為 View 設置傳入的 LayoutParams 屬性值

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末斑响,一起剝皮案震驚了整個濱河市菱属,隨后出現(xiàn)的幾起案子钳榨,更是在濱河造成了極大的恐慌舰罚,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件薛耻,死亡現(xiàn)場離奇詭異营罢,居然都是意外死亡,警方通過查閱死者的電腦和手機饼齿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門饲漾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人缕溉,你說我怎么就攤上這事考传。” “怎么了证鸥?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵僚楞,是天一觀的道長。 經(jīng)常有香客問我枉层,道長泉褐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任鸟蜡,我火速辦了婚禮膜赃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘揉忘。我一直安慰自己跳座,他們只是感情好端铛,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著疲眷,像睡著了一般沦补。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上咪橙,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天夕膀,我揣著相機與錄音,去河邊找鬼美侦。 笑死产舞,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的菠剩。 我是一名探鬼主播易猫,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼具壮!你這毒婦竟也來了准颓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤棺妓,失蹤者是張志新(化名)和其女友劉穎攘已,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體怜跑,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡样勃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了性芬。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片峡眶。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖植锉,靈堂內(nèi)的尸體忽然破棺而出辫樱,到底是詐尸還是另有隱情,我是刑警寧澤俊庇,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布狮暑,位于F島的核電站,受9級特大地震影響暇赤,放射性物質(zhì)發(fā)生泄漏心例。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一鞋囊、第九天 我趴在偏房一處隱蔽的房頂上張望止后。 院中可真熱鬧,春花似錦、人聲如沸译株。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽歉糜。三九已至乘寒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間匪补,已是汗流浹背伞辛。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留夯缺,地道東北人蚤氏。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像踊兜,于是被迫代替她去往敵國和親竿滨。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

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