從源碼看LayoutInflater創(chuàng)建View的過(guò)程

public View inflate(int resource, ViewGroup root) {
        return inflate(resource, root, root != null);
}

public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources(); 
       final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            Context lastContext = (Context)mConstructorArgs[0];
            mConstructorArgs[0] = mContext;
            View result = root;
            try {
                // 查找根節(jié)點(diǎn)
               ...
                final String name = parser.getName();
                if (TAG_MERGE.equals(name)) {
                    rInflate(parser, root, attrs, false, false);
                } else {
                    //創(chuàng)建根View
                    final View temp = createViewFromTag(root, name, attrs, false);
                    ViewGroup.LayoutParams params = null;
                    if (root != null) {
                        params = root.generateLayoutParams(attrs);
                        if (!attachToRoot) {
                            temp.setLayoutParams(params);
                        }
                    }
                    //遞歸創(chuàng)建子View
                    rInflate(parser, temp, attrs, true, true);
                    if (root != null && attachToRoot) {
                        root.addView(temp, params);
                    }
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }
            return result;
        }
    }

過(guò)程大致如下:

  1. 首先根據(jù)layout的資源id察藐,創(chuàng)建對(duì)應(yīng)的XmlResouceParser
  2. XMLResourceParser找到根標(biāo)簽。
  3. 如果根標(biāo)簽不是merge的話(huà)舟扎,則通過(guò)createViewFromTag創(chuàng)建相應(yīng)的View分飞,然后遞歸的創(chuàng)建子View
  4. 如果設(shè)置了attachToRoot,則將將根View添加到rootView中睹限,然后返回rootView譬猫;否則返回根view讯檐。

xml中根節(jié)點(diǎn)設(shè)置的layout_width和Layout_height有時(shí)不起作用,關(guān)鍵要看infalte時(shí)染服,是否傳了rootView别洪,并設(shè)置attachToRoot為true。

View createViewFromTag(View parent, String name, AttributeSet attrs, boolean inheritContext) {
        if (name.equals("view")) {
            name = attrs.getAttributeValue(null, "class");
        }
        Context viewContext;
        if (parent != null && inheritContext) {
            viewContext = parent.getContext();
        } else {
            viewContext = mContext;
        }

        // 應(yīng)用主題
        final TypedArray ta = viewContext.obtainStyledAttributes(attrs, ATTRS_THEME);
        final int themeResId = ta.getResourceId(0, 0);
        if (themeResId != 0) {
            viewContext = new ContextThemeWrapper(viewContext, themeResId);
        }
        ta.recycle();
        try {
            View view;
            if (mFactory2 != null) {
                view = mFactory2.onCreateView(parent, name, viewContext, attrs);
            } else if (mFactory != null) {
                view = mFactory.onCreateView(name, viewContext, attrs);
            } else {
                view = null;
            }

            if (view == null && mPrivateFactory != null) {
                view = mPrivateFactory.onCreateView(parent, name, viewContext, attrs);
            }
            if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = viewContext;
                try {
                    if (-1 == name.indexOf('.')) {
                        view = onCreateView(parent, name, attrs);
                    } else {
                        view = createView(name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }
            return view;

        } 

依次分別由mFactory2柳刮、mFactory挖垛、mPrivateFactory創(chuàng)建View,如果都沒(méi)有創(chuàng)建诚亚,則由LayoutInflater自己創(chuàng)建晕换。
下面看看LayoutInflater是如何創(chuàng)建的

 public final View createView(String name, String prefix, AttributeSet attrs)
            throws ClassNotFoundException, InflateException {
        Constructor<? extends View> constructor = sConstructorMap.get(name);
        Class<? extends View> clazz = null;

        try {
            if (constructor == null) {
                clazz = mContext.getClassLoader().loadClass(
                        prefix != null ? (prefix + name) : name).asSubclass(View.class);
                constructor = clazz.getConstructor(mConstructorSignature);
                sConstructorMap.put(name, constructor);
            } else {
            Object[] args = mConstructorArgs;
            args[1] = attrs;
            constructor.setAccessible(true);
            final View view = constructor.newInstance(args);
            return view;
        } 

LayoutInflater內(nèi)有View構(gòu)造函數(shù)的緩存午乓,當(dāng)存在該View的構(gòu)造函數(shù)時(shí)站宗,直接拿來(lái)創(chuàng)建實(shí)例,否則通過(guò)context.getClassLoader益愈,由classLoader去loadClass梢灭,然后獲得該類(lèi)的構(gòu)造函數(shù),并存入緩存蒸其,然后調(diào)用構(gòu)造函數(shù)創(chuàng)建View敏释。

那mFactory2、mFactory和mPrivateFactory是用來(lái)干什么的呢摸袁?它們之間是什么關(guān)系钥顽?這三個(gè)變量是何時(shí)被賦值的?
第一個(gè)問(wèn)題的答案是靠汁,可以對(duì)LayoutInflater從xml中創(chuàng)建View的過(guò)程進(jìn)行擴(kuò)展蜂大。
對(duì)于第二個(gè)問(wèn)題,來(lái)看一下Factory和Factory2的定義

public interface Factory {
       public View onCreateView(String name, Context context, AttributeSet attrs);
}
public interface Factory2 extends Factory {
        public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
}

Factory2繼承于Factory蝶怔,增加了一個(gè)接口奶浦。其中mFractory是Factory類(lèi)型的,mFactory2和mPrivateFactory是Factory2類(lèi)型的踢星。你要是去看Android源碼修改日志的話(huà)澳叉,可以看到最初的時(shí)候只有mFactory,后來(lái)為了支持Fragment才增加了mFactory2沐悦,后來(lái)又有了mPrivateFactory成洗。
之所以還留著Factory,是為了向下兼容藏否。
第三個(gè)問(wèn)題瓶殃,三個(gè)Factory是何時(shí)被賦值的?
如果看一下Activity的定義的話(huà)秕岛,你會(huì)發(fā)現(xiàn)Activity實(shí)現(xiàn)了Factory2接口碌燕。并且在attach方法中误证,將自己設(shè)置為L(zhǎng)ayoutInflater的privateFactory。

mWindow.getLayoutInflater().setPrivateFactory(this);

這是你就會(huì)明白了修壕,當(dāng)我們使用LayoutInflater來(lái)inflate View的時(shí)候愈捅,首先會(huì)走到Activity的onCreateView中,如果返回了null慈鸠,才會(huì)由LayoutInflater繼續(xù)創(chuàng)建View蓝谨。我們來(lái)看一下Activity的onCreateView是如何實(shí)現(xiàn)的

public View onCreateView(String name, Context context, AttributeSet attrs) {
       return null;
}

public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
       if (!"fragment".equals(name)) {
           return onCreateView(name, context, attrs);
       }
       return mFragments.onCreateView(parent, name, context, attrs);
}

如果是fragment的話(huà),則由mFragments去創(chuàng)建青团,否則返回null譬巫。
而mFragments的類(lèi)型是FragmentManagerImpl。
這樣就說(shuō)的通了督笆,以前的時(shí)候Android是不支持Fragment的芦昔,View都由LayoutInflater直接創(chuàng)建,后來(lái)為了支持Fragment娃肿,讓Activity來(lái)實(shí)現(xiàn)Factory2咕缎,從而攔截對(duì)Fragment的創(chuàng)建,后來(lái)又把Fragment相關(guān)的代碼移到了FragmentManager中料扰。
這時(shí)候你可以會(huì)說(shuō)凭豪,我用的不是Activity,而是FragmentActivity晒杈。


在BaseFragmentActivityDonut代碼中

protected void onCreate(Bundle savedInstanceState) {
        if (Build.VERSION.SDK_INT < 11 && getLayoutInflater().getFactory() == null) {
            getLayoutInflater().setFactory(this);
        }
        super.onCreate(savedInstanceState);
    }

如果系統(tǒng)Build版本小于11嫂伞,LayoutInflater的mFactory被設(shè)置為Activity,而11之后拯钻,則是mPrivateFactory被設(shè)置為Activity帖努。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市说庭,隨后出現(xiàn)的幾起案子然磷,更是在濱河造成了極大的恐慌,老刑警劉巖刊驴,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姿搜,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡捆憎,警方通過(guò)查閱死者的電腦和手機(jī)舅柜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)躲惰,“玉大人致份,你說(shuō)我怎么就攤上這事〈〔Γ” “怎么了氮块?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵绍载,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我滔蝉,道長(zhǎng)击儡,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任蝠引,我火速辦了婚禮阳谍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘螃概。我一直安慰自己矫夯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布吊洼。 她就那樣靜靜地躺著训貌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪融蹂。 梳的紋絲不亂的頭發(fā)上旺订,一...
    開(kāi)封第一講書(shū)人閱讀 51,718評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音超燃,去河邊找鬼。 笑死拘领,一個(gè)胖子當(dāng)著我的面吹牛意乓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播约素,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼届良,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了圣猎?” 一聲冷哼從身側(cè)響起士葫,我...
    開(kāi)封第一講書(shū)人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎送悔,沒(méi)想到半個(gè)月后慢显,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡欠啤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年荚藻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片洁段。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡应狱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出祠丝,到底是詐尸還是另有隱情疾呻,我是刑警寧澤除嘹,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站岸蜗,受9級(jí)特大地震影響憾赁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜散吵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一龙考、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧矾睦,春花似錦晦款、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至赁温,卻和暖如春坛怪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背股囊。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工袜匿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人稚疹。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓居灯,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親内狗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子怪嫌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,180評(píng)論 25 707
  • 用法獲取LayoutInflater 首先要注意LayoutInflater本身是一個(gè)抽象類(lèi),我們不可以直接通過(guò)n...
    我本和圖閱讀 869評(píng)論 0 0
  • 一個(gè)Fragment看起來(lái)就是一個(gè)和Activity一樣的用戶(hù)界面柳沙。你可以結(jié)合多個(gè)Fragments到一個(gè)acti...
    kaiviak閱讀 2,260評(píng)論 0 8
  • 0 認(rèn)知 Fragment官方的翻譯名為:片段岩灭,表示 Activity中的行為或用戶(hù)界面部分。 相比Activit...
    我是Asha閱讀 2,939評(píng)論 2 25
  • 1.接口中不可以有實(shí)例域或靜態(tài)方法赂鲤,但是可以有常量2.不能使用new運(yùn)算符實(shí)例化一個(gè)接口3.可以聲明一個(gè)接口的變量...
    這是朕的江山閱讀 1,338評(píng)論 0 0