LayoutInflater深入解析

在開發(fā)過程中,我們經(jīng)常會需要動態(tài)的加載布局晶衷,最常見的是ListView中的getView()(雖然現(xiàn)在ListView已經(jīng)沒多少人在用了)和RecyclerView中的onCreateViewHolder()方法中動態(tài)的加載我們已經(jīng)在xml文件中定義好的item文件恭应,之前經(jīng)常用到抄邀,但是內(nèi)部的源碼之前一直沒看過,知道前幾天用RecyclerView的時候昼榛,在onCreateViewHolder()方法中直接使用了View.inflate()方法來加載布局境肾,卻發(fā)現(xiàn)我的item中的TextView始終是wrapContent(注:我的item中只有一個TextView),然后我就打開源碼看了一下。發(fā)現(xiàn)View.inflate()和LayoutInflater.inflate()的各個重載方法最終都是調(diào)用了

 public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) 

三個參數(shù)中中
第一個參數(shù)是解析xml的類胆屿,是由我們傳入的要加載的布局得到的奥喻;
第二個參數(shù)是我們傳入的ViewGroup。
第三個參數(shù)是最難理解的非迹,根據(jù)源碼注釋大概可以翻譯為:被填充的層是否應該附在root參數(shù)內(nèi)部环鲤?如果是false,root參數(shù)只適用于為XML根元素View創(chuàng)建正確的LayoutParams的子類憎兽。其實就是可以這么理解:attachToRoot傳入true代表layout文件填充的View會被直接添加進ViewGroup冷离,而傳入false則代表創(chuàng)建的View會以其他方式被添加進ViewGroup。舉個簡單的例子:

創(chuàng)建一個LinearLayout 并向其中動態(tài)的添加一個我們定義好的一個layout_menu_item
LinearLayout layout = (LinearLayout)findViewById(R.id.container);
View view = View.inflate(this, R.layout.layout_menu_item, layout);
layout.addView(view);

假如這么寫纯命,就會發(fā)現(xiàn)程序是報錯的西剥,而我們把layout.addView(view)這一句去掉,就會發(fā)現(xiàn)不但不報錯了亿汞,而且layout_menu_item已經(jīng)被添加進了layout瞭空。

LinearLayout layout = (LinearLayout)findViewById(R.id.container);
View view = View.inflate(this, R.layout.layout_menu_item, layout);

這就是attachToRoot這個參數(shù)的意思,總結(jié)一下,就是:
如果為true,那第一個參數(shù)的layout文件就會被填充并附加在第二個參數(shù)所指定的ViewGroup內(nèi)咆畏。方法返回結(jié)合后的View图甜,根元素是第二個參數(shù)ViewGroup。如果是false的話鳖眼,第一個參數(shù)所指定的layout文件會被填充并作為View返回黑毅。這個View的根元素就是layout文件的根元素。
下面分析一下源碼:

最終返回的是result
  public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");

            final Context inflaterContext = mContext;
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            Context lastContext = (Context) mConstructorArgs[0];
            mConstructorArgs[0] = inflaterContext;
//0.對返回的result進行賦值钦讳,為我們傳進來的root矿瘦,也就是ViewGroup
            View result = root;

            try {
                // Look for the root node.
                int type;
                while ((type = parser.next()) != XmlPullParser.START_TAG &&
                        type != XmlPullParser.END_DOCUMENT) {
                    // Empty
                }

                if (type != XmlPullParser.START_TAG) {
                    throw new InflateException(parser.getPositionDescription()
                            + ": No start tag found!");
                }

                final String name = parser.getName();
                
                if (DEBUG) {
                    System.out.println("**************************");
                    System.out.println("Creating root view: "
                            + name);
                    System.out.println("**************************");
                }

                if (TAG_MERGE.equals(name)) {
                    if (root == null || !attachToRoot) {
                        throw new InflateException("<merge /> can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }

                    rInflate(parser, root, inflaterContext, attrs, false);
                } else {
                    // Temp is the root view that was found in the xml
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                    ViewGroup.LayoutParams params = null;

                    if (root != null) {
                        if (DEBUG) {
                            System.out.println("Creating params from root: " +
                                    root);
                        }
                        // Create layout params that match root, if supplied
                        params = root.generateLayoutParams(attrs);
//1.注意這一點,如果root不為null愿卒,并且我們的attachToRoot為false缚去,這里會設(shè)置了我們的View的tLayoutParams,由于最后返回的是result琼开,相當于返回的是我們的子View.
                        if (!attachToRoot) {
                            // Set the layout params for temp if we are not
                            // attaching. (If we are, we use addView, below)
                            temp.setLayoutParams(params);
                        }
                    }

                    if (DEBUG) {
                        System.out.println("-----> start inflating children");
                    }

                    // Inflate all children under temp against its context.
                    rInflateChildren(parser, temp, attrs, true);

                    if (DEBUG) {
                        System.out.println("-----> done inflating children");
                    }

                    // We are supposed to attach all the views we found (int temp)
                    // to root. Do that now.
//2.這里易结,如果root不為null,并且attachToRoot為true時,這里通過父View的addView()方法,也設(shè)置了View的LayoutParams柜候,搞动,并且返回的是我們的父View.
                    if (root != null && attachToRoot) {
                        root.addView(temp, params);
                    }
//3.這里,如果root為null,并且attachToRoot為false時渣刷,我們沒有設(shè)置了View的LayoutParams鹦肿,這樣的話View的我們在xml中設(shè)置寬高屬性就不會起作用,這一步就解釋了開頭布局不能充滿的問題,并且這里返回的是我們的子View.
                    // Decide whether to return the root that was passed in or the
                    // top view found in xml.
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }

            } catch (XmlPullParserException e) {
                final InflateException ie = new InflateException(e.getMessage(), e);
                ie.setStackTrace(EMPTY_STACK_TRACE);
                throw ie;
            } catch (Exception e) {
                final InflateException ie = new InflateException(parser.getPositionDescription()
                        + ": " + e.getMessage(), e);
                ie.setStackTrace(EMPTY_STACK_TRACE);
                throw ie;
            } finally {
                // Don't retain static reference on context.
                mConstructorArgs[0] = lastContext;
                mConstructorArgs[1] = null;

                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }

            return result;
        }
    }

但是辅柴,在RecyclerView中箩溃,如果我們傳入了ViewGroup作為parent,并且把 attachToRoot設(shè)置為true,是行不通的碌嘀,這是因為RecyclerView負責決定什么時候展示它的子View涣旨,這個不由我們決定。在任何我們不負責將View添加進ViewGroup的情況下都應該將attachToRoot設(shè)置為false股冗。所以霹陡,當設(shè)置為true的時候會報錯···子view已經(jīng)有父view了,可以看上述代碼中注釋的 1和2魁瞪,所有的使用inflate方法的最后的是否有l(wèi)ayoutParams屬性都與上述代碼的注釋的 1 2 3有關(guān)系穆律。
總結(jié):
1.調(diào)用LayoutInflater.inflate方法惠呼,并且將root參數(shù)設(shè)置為null导俘,就等于忽略了xml布局文件中的layout_×參數(shù),最后返回的是我們要inflate的View的本身剔蹋。
2.調(diào)用LayoutInflater.inflate方法旅薄,并且將root參數(shù)設(shè)置不為null,attachRoot設(shè)置為false,最后返回的是我們要inflate的View的本身(View的layoutparams參數(shù)并沒有丟失)少梁。
2.如果root不為null洛口,并且attachRoot=true,那么就會根據(jù)root生成一個布局文件View的LayoutParam對象凯沪,并且將這個View添加到root中去第焰,并且返回這個root的View(也就解釋了本文開頭時的addView()的錯誤)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末妨马,一起剝皮案震驚了整個濱河市挺举,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌烘跺,老刑警劉巖湘纵,帶你破解...
    沈念sama閱讀 222,378評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異滤淳,居然都是意外死亡梧喷,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評論 3 399
  • 文/潘曉璐 我一進店門脖咐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來铺敌,“玉大人,你說我怎么就攤上這事屁擅∈实叮” “怎么了?”我有些...
    開封第一講書人閱讀 168,983評論 0 362
  • 文/不壞的土叔 我叫張陵煤蹭,是天一觀的道長笔喉。 經(jīng)常有香客問我,道長硝皂,這世上最難降的妖魔是什么常挚? 我笑而不...
    開封第一講書人閱讀 59,938評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮稽物,結(jié)果婚禮上奄毡,老公的妹妹穿的比我還像新娘。我一直安慰自己贝或,他們只是感情好吼过,可當我...
    茶點故事閱讀 68,955評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著咪奖,像睡著了一般盗忱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上羊赵,一...
    開封第一講書人閱讀 52,549評論 1 312
  • 那天趟佃,我揣著相機與錄音,去河邊找鬼。 笑死闲昭,一個胖子當著我的面吹牛罐寨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播序矩,決...
    沈念sama閱讀 41,063評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼鸯绿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了簸淀?” 一聲冷哼從身側(cè)響起楞慈,我...
    開封第一講書人閱讀 39,991評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎啃擦,沒想到半個月后囊蓝,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,522評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡令蛉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,604評論 3 342
  • 正文 我和宋清朗相戀三年聚霜,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片珠叔。...
    茶點故事閱讀 40,742評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡蝎宇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出祷安,到底是詐尸還是另有隱情姥芥,我是刑警寧澤,帶...
    沈念sama閱讀 36,413評論 5 351
  • 正文 年R本政府宣布汇鞭,位于F島的核電站凉唐,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏霍骄。R本人自食惡果不足惜台囱,卻給世界環(huán)境...
    茶點故事閱讀 42,094評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望读整。 院中可真熱鬧簿训,春花似錦、人聲如沸米间。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽屈糊。三九已至的榛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間另玖,已是汗流浹背困曙。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留谦去,地道東北人慷丽。 一個月前我還...
    沈念sama閱讀 49,159評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像鳄哭,于是被迫代替她去往敵國和親要糊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,747評論 2 361

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