Android 布局的加載

Android 布局的加載

此次分析是基于Android sdk 28,看到網(wǎng)絡(luò)上好多有寫的不錯(cuò)的博客,絕大多數(shù)都沒有說明是基于Android 那個(gè)版本來分析,因?yàn)殡S著Android版本的演進(jìn),api是有變化的

在開始之前先看一個(gè)Actiivty 頁面的結(jié)構(gòu)圖

image

開始

設(shè)置Activity 的布局,是通過 ==AppCompatActivity== 的:

    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
    }

然后看看這個(gè)里面 ==setContentView== 是從哪里來的?這是 ==AppCompatDelegate== 這個(gè)的一個(gè)函數(shù),可以看看這個(gè)類是做什么用的

 This class represents a delegate which you can use to extend AppCompat's support to any
 * {@link android.app.Activity}.

翻譯過來就是: 此類表示可用于將AppCompat支持?jǐn)U展到任何Activity的委托茎毁。比較拗口,直白點(diǎn)就是這個(gè)類支持actiivty 的一些擴(kuò)展功能,是一個(gè)代理類,支持的功能如下:

  • addContentView(android.view.View, android.view.ViewGroup.LayoutParams)
  • setContentView(int)
  • setContentView(android.view.View)
  • setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
  • requestWindowFeature(int)
  • hasWindowFeature(int)
  • invalidateOptionsMenu()
  • startSupportActionMode(android.support.v7.view.ActionMode.Callback)
  • setSupportActionBar(android.support.v7.widget.Toolbar)
  • getSupportActionBar()
  • getMenuInflater()
  • findViewById(int)

其實(shí)這部分功能的主要實(shí)現(xiàn)還是在window中,只不過是將這部分功能將window與activity 進(jìn)行隔離,避免耦合

可以對(duì)比一下:
以 ==setContentView(viewId)== 為例:

  • android sdk 28:

AppCompatActivity$setContentView

    @Override
    public void setContentView(@LayoutRes int layoutResID) {
     // 拿到AppCompatDelegate的實(shí)例,調(diào)用函數(shù)
       getDelegate().setContentView(layoutResID);
      
    }
  • android sdk 23

Activity$setContentView

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

可以看出,同一個(gè)方法,android的不同版本是有區(qū)別的,在具體看看 ==getDelegate().setContentView(layoutResID);==

AppCompatDelegate是抽像類,具體的實(shí)現(xiàn)是AppCompatDelegateImp

AppCompatDelegateImp$setContentView

     @Override
    public void setContentView(int resId) {
        //初始化DecorView
        ensureSubDecor();
        //設(shè)置setContentView 要記載的父布局
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        //加載之前移除所有的布局
        contentParent.removeAllViews();
        //完成布局的加載
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mOriginalWindowCallback.onContentChanged();
    }

然后我們可以看看AppCompatDelegateImp$ensureSubDecor這個(gè)函數(shù)里面做了什么事:

 private void ensureSubDecor() {
            //默認(rèn)是false,如果為true的表明 subDecor已經(jīng)被加載過了
        if (!mSubDecorInstalled) {
        
            mSubDecor = createSubDecor();


            //設(shè)置actiivty 的title
            // If a title was set before we installed the decor, propagate it now
            CharSequence title = getTitle();
            if (!TextUtils.isEmpty(title)) {
                if (mDecorContentParent != null) {
                    mDecorContentParent.setWindowTitle(title);
                } else if (peekSupportActionBar() != null) {
                    peekSupportActionBar().setWindowTitle(title);
                } else if (mTitleView != null) {
                    mTitleView.setText(title);
                }
            }

            //為subDecor 設(shè)置固定的大小
            applyFixedSizeWindow();

            //空方法
            onSubDecorInstalled(mSubDecor);

            //設(shè)置 subDecor 已經(jīng)被加載過了
            mSubDecorInstalled = true;

            // Invalidate if the panel menu hasn't been created before this.
            // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
            // being called in the middle of onCreate or similar.
            // A pending invalidation will typically be resolved before the posted message
            // would run normally in order to satisfy instance state restoration.
            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
            if (!mIsDestroyed && (st == null || st.menu == null)) {
                invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
            }
        }
    }

然后在看看 ==AppCompatDelegateImp$applyFixedSizeWindow()== 函數(shù)

 private void applyFixedSizeWindow() {
        //注意這個(gè)控件,它在界面的位置看下面的這張圖
        ContentFrameLayout cfl = (ContentFrameLayout) mSubDecor.findViewById(android.R.id.content);

        // This is a bit weird. In the framework, the window sizing attributes control
        // the decor view's size, meaning that any padding is inset for the min/max widths below.
        // We don't control measurement at that level, so we need to workaround it by making sure
        // that the decor view's padding is taken into account.
        
        //獲取 DecorView
        final View windowDecor = mWindow.getDecorView();
        
        //設(shè)置ContentFrameLayout 布局的間距
        cfl.setDecorPadding(windowDecor.getPaddingLeft(),
                windowDecor.getPaddingTop(), windowDecor.getPaddingRight(),
                windowDecor.getPaddingBottom());

        TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
        a.getValue(R.styleable.AppCompatTheme_windowMinWidthMajor, cfl.getMinWidthMajor());
        a.getValue(R.styleable.AppCompatTheme_windowMinWidthMinor, cfl.getMinWidthMinor());

        if (a.hasValue(R.styleable.AppCompatTheme_windowFixedWidthMajor)) {
            a.getValue(R.styleable.AppCompatTheme_windowFixedWidthMajor,
                    cfl.getFixedWidthMajor());
        }
        if (a.hasValue(R.styleable.AppCompatTheme_windowFixedWidthMinor)) {
            a.getValue(R.styleable.AppCompatTheme_windowFixedWidthMinor,
                    cfl.getFixedWidthMinor());
        }
        if (a.hasValue(R.styleable.AppCompatTheme_windowFixedHeightMajor)) {
            a.getValue(R.styleable.AppCompatTheme_windowFixedHeightMajor,
                    cfl.getFixedHeightMajor());
        }
        if (a.hasValue(R.styleable.AppCompatTheme_windowFixedHeightMinor)) {
            a.getValue(R.styleable.AppCompatTheme_windowFixedHeightMinor,
                    cfl.getFixedHeightMinor());
        }
        a.recycle();

        cfl.requestLayout();
    }

說了半天可能還是有點(diǎn)暈,看著張圖解明白了:

image

到這里就結(jié)束了!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末克懊,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子七蜘,更是在濱河造成了極大的恐慌,老刑警劉巖橡卤,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扮念,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡碧库,警方通過查閱死者的電腦和手機(jī)柜与,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嵌灰,“玉大人弄匕,你說我怎么就攤上這事」敛t!?“怎么了迁匠?”我有些...
    開封第一講書人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)驹溃。 經(jīng)常有香客問我城丧,道長(zhǎng),這世上最難降的妖魔是什么豌鹤? 我笑而不...
    開封第一講書人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任亡哄,我火速辦了婚禮,結(jié)果婚禮上布疙,老公的妹妹穿的比我還像新娘蚊惯。我一直安慰自己,他們只是感情好灵临,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開白布拣挪。 她就那樣靜靜地躺著,像睡著了一般俱诸。 火紅的嫁衣襯著肌膚如雪菠劝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,521評(píng)論 1 304
  • 那天,我揣著相機(jī)與錄音赶诊,去河邊找鬼笼平。 笑死,一個(gè)胖子當(dāng)著我的面吹牛舔痪,可吹牛的內(nèi)容都是我干的寓调。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼锄码,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼夺英!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起滋捶,我...
    開封第一講書人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤痛悯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后重窟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體载萌,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年巡扇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扭仁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡厅翔,死狀恐怖乖坠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情刀闷,我是刑警寧澤熊泵,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站涩赢,受9級(jí)特大地震影響戈次,放射性物質(zhì)發(fā)生泄漏轩勘。R本人自食惡果不足惜筒扒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望绊寻。 院中可真熱鬧花墩,春花似錦、人聲如沸澄步。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽村缸。三九已至祠肥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間梯皿,已是汗流浹背仇箱。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工县恕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人剂桥。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓忠烛,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親权逗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子美尸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

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