[Android] LayoutInflater 工作流程

前言

感覺很長時(shí)間沒寫文章了蚀狰,這個(gè)星期因?yàn)榛丶液吞幚眄?xiàng)目問題,還是花了很多時(shí)間的歇僧。雖然知道很多東西如果只是看一下用一次,很快就會(huì)遺忘锋拖,但認(rèn)認(rèn)真真地做輸出還是需要一定恒心的诈悍。

這次寫 LayoutInflater 的工作流程,是由于小組一位成員在調(diào)用inflate 方法時(shí)兽埃,沒有傳入 parent 參數(shù)導(dǎo)致生成的布局寬高失效的問題侥钳。

這里先說原因,是因?yàn)槿绻?inflate 的 View柄错,沒有包含在某個(gè) Viewgroup 下舷夺,也沒有傳入 parent 參數(shù),那么他的 layout_width 等屬性就會(huì)失效售貌,這些屬性是需要 View 處在某個(gè)布局下才能生效给猾。

使用

獲取 LayoutInflater

通過 LayoutInflater layoutInflater = LayoutInflater.from(context) 就可以獲取到 LayoutInflater。

如果調(diào)用 View.inflate 也是先會(huì)獲取 LayoutInflater颂跨,再使用 LayoutInflater 去加載布局敢伸。獲取 LayoutInflater 的源碼如下:

public static LayoutInflater from(Context context) {
    LayoutInflater LayoutInflater =
            (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    if (LayoutInflater == null) {
        throw new AssertionError("LayoutInflater not found.");
    }
    return LayoutInflater;
}

可以看到,我們其實(shí)也可以自己去調(diào)用 getSystemService 來獲取 LayoutInflater恒削。

加載布局

接下來池颈,使用 layoutInflater.inflate 方法即可加載相應(yīng)布局尾序,有四個(gè)重載方法如下圖:


其中 parser 參數(shù)是一個(gè)描述 View 層次的 XML 文件,在 Android 中我們就是用 XML 來描述布局的躯砰,所以直接傳入布局的 int 值就可以了每币。

另外兩個(gè)需要注意的參數(shù)是 root 和 attachToRoot,root 是指定了本次加載布局的父容器琢歇,attachToRoot 則表示是否將本次加載的內(nèi)容添加到父容器中兰怠。

我們常常會(huì)碰到在 adapter 中,設(shè)置 attachToRoot 為 true 時(shí)會(huì)報(bào)錯(cuò)矿微,是因?yàn)?adapter 會(huì)自己將加載的布局添加到父容器里痕慢,如果自己設(shè)置的話尚揣,就會(huì)導(dǎo)致重復(fù)添加了涌矢。

工作流程

所有的 inflate 到最后都會(huì)返回到下面這個(gè)方法中來進(jìn)行布局加載:

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;
        View result = root;

        try {
            // Look for the root node.
            int type;
            ...

            final String name = parser.getName();
            

            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) {

                    // Create layout params that match root, if supplied
                    params = root.generateLayoutParams(attrs);
                    if (!attachToRoot) {
                        // Set the layout params for temp if we are not
                        // attaching. (If we are, we use addView, below)
                        temp.setLayoutParams(params);
                    }
                }

               

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

                

                // We are supposed to attach all the views we found (int temp)
                // to root. Do that now.
                if (root != null && attachToRoot) {
                    root.addView(temp, params);
                }

                // 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) {
            InflateException ex = new InflateException(e.getMessage());
            ex.initCause(e);
            throw ex;
        } catch (Exception e) {
            InflateException ex = new InflateException(
                    parser.getPositionDescription()
                            + ": " + e.getMessage());
            ex.initCause(e);
            throw ex;
        } finally {
            // Don't retain static reference on context.
            mConstructorArgs[0] = lastContext;
            mConstructorArgs[1] = null;
        }

        Trace.traceEnd(Trace.TRACE_TAG_VIEW);

        return result;
    }
}

代碼較多,我去掉了有關(guān) DEBUG 的部分快骗,同時(shí)最后異常處理的部分也可以略過不看娜庇,那么邏輯就比較清晰了。LayoutInflater 使用 XmlPullParser 來解析布局文件方篮,首先根據(jù)我們傳入的布局參數(shù)創(chuàng)建根布局:

final View temp = createViewFromTag(root, name, inflaterContext, attrs);

ViewGroup.LayoutParams params = null;

這個(gè) params 參數(shù)是用來為 temp 即加載的根布局進(jìn)行屬性參數(shù)設(shè)置的名秀,他有以下三種情況:

  1. ViewGroup root 參數(shù)為 null,則返回加載的布局不做其他設(shè)置

  2. root != null && attachToRoot == null藕溅,則使用父布局的參數(shù)對 temp 進(jìn)行設(shè)置

     params = root.generateLayoutParams(attrs);
     temp.setLayoutParams(params);
    
  3. root != null && attachToRoot != null匕得,則將加載的布局添加到父布局,再返回父布局:

     root.addView(temp, params);
    

接下來還會(huì)遍歷加載根布局的子元素:

rInflateChildren(parser, temp, attrs, true);

這樣就完成了一次布局的加載巾表,具體是如何加載的汁掠,就得去學(xué)習(xí) View 的工作原理了。

參考資料

這里也提示一下集币,你看到的資料不一定都是最正確的考阱,盡量接受多方的信息,比如一篇博客看完后鞠苟,最好是能把評論也翻看一遍乞榨。

Android LayoutInflater原理分析,帶你一步步深入了解View(一)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末当娱,一起剝皮案震驚了整個(gè)濱河市吃既,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌跨细,老刑警劉巖态秧,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異扼鞋,居然都是意外死亡申鱼,警方通過查閱死者的電腦和手機(jī)愤诱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捐友,“玉大人淫半,你說我怎么就攤上這事∠蛔” “怎么了科吭?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長猴鲫。 經(jīng)常有香客問我对人,道長,這世上最難降的妖魔是什么拂共? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任牺弄,我火速辦了婚禮,結(jié)果婚禮上宜狐,老公的妹妹穿的比我還像新娘势告。我一直安慰自己,他們只是感情好抚恒,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布咱台。 她就那樣靜靜地躺著,像睡著了一般俭驮。 火紅的嫁衣襯著肌膚如雪俘闯。 梳的紋絲不亂的頭發(fā)上液斜,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天居触,我揣著相機(jī)與錄音逾冬,去河邊找鬼。 笑死譬圣,一個(gè)胖子當(dāng)著我的面吹牛瓮恭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播厘熟,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼屯蹦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了绳姨?” 一聲冷哼從身側(cè)響起登澜,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎飘庄,沒想到半個(gè)月后脑蠕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年谴仙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了迂求。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡晃跺,死狀恐怖揩局,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情掀虎,我是刑警寧澤凌盯,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站烹玉,受9級特大地震影響驰怎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜二打,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一县忌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧址儒,春花似錦芹枷、人聲如沸衅疙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饱溢。三九已至喧伞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間绩郎,已是汗流浹背潘鲫。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留肋杖,地道東北人溉仑。 一個(gè)月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像状植,于是被迫代替她去往敵國和親浊竟。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

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