解決布局加載那些奇奇怪怪的事??

1.引言

Android中,我們需要?jiǎng)討B(tài)添加View的時(shí)候历葛,通常會(huì)先去加載布局恤溶,那加載布局的方式一般有以下兩種方式:

//第一種方式
View.inflate(this,R.layout.xx,null);
//第二種方式
LayoutInflater.from(this).inflate(R.layout.xx,null,false);

一般有經(jīng)驗(yàn)的人會(huì)跟你說,用第二種方式靠譜宏娄,那如果你偏偏要用第一種呢孵坚,也不是不可以卖宠,用不好的話扛伍,就會(huì)出現(xiàn)一些奇奇怪怪的事:

2. 怪事一

我現(xiàn)在有一個(gè) Activity 類的布局刺洒,里面只有一個(gè)LInearLayout ,我需要?jiǎng)討B(tài)往這個(gè)LinearLayout 中添加一個(gè) TextView, 這個(gè)TextView 我設(shè)置了高度為 100dp, 背景顏色設(shè)置為 紅色逆航,直接看下面代碼:

//activity_activity_inflate
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/ll_container"
    android:orientation="vertical" />
___________________________________________________________
//item_child
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="#ff5232"
    android:text="Hello world"
    android:textColor="#ffffff"
    android:layout_width="match_parent"
    android:id="@+id/tv"
    android:layout_height="100dp" />

動(dòng)態(tài)添加 TextView

LinearLayout ll_container = findViewById(R.id.ll_container);
View child = View.inflate(this,R.layout.item_child,null);
ll_container.addView(child);

運(yùn)行結(jié)果圖:

image

怪事來了因俐,??我們明明設(shè)置 TextView 的高度為 100dp , 怎么就顯示 wrap_content 呢?這里我們記錄一下抹剩,為怪事1.

3.怪事二

我們修改一下我們動(dòng)態(tài)添加的代碼:

LinearLayout ll_container = findViewById(R.id.ll_container);
View child = View.inflate(this,R.layout.item_child,ll_container);
ll_container.addView(child);

第三個(gè)參數(shù)把 null 修改為 ll_container , 再次運(yùn)行:

oh..mygod,程序直接崩潰了蛉艾,錯(cuò)誤日志信息如下:

The specified child already has a parent. You must call removeView() on the child's parent first.

當(dāng)然在我們使用RecyclerViewadapter 里伺通,也會(huì)要加載布局罐监,使用不當(dāng),也是會(huì)出現(xiàn)所寫非所得的效果沟堡,那到底是怎么回事呢?

這里假設(shè)你對(duì)布局加載的流程有一定了解禀横,無論是哪種布局加載方式柏锄,最終都會(huì)調(diào)用到這里:

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
    final Resources res = getContext().getResources();
    ...
    final XmlResourceParser parser = res.getLayout(resource);
    try {
        return inflate(parser, root, attachToRoot);
    } finally {
        parser.close();
    }
}

LayoutInflater#inflate 方法趾娃,三個(gè)參數(shù)的抬闷。繼續(xù)跟進(jìn)的話就是 inflate 方法笤成,以下這個(gè)方法是我們重點(diǎn)關(guān)注的眷茁,我只留下重要代碼:

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        ....
        View result = root;//-------------------1

        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("**************************");
            }
            ...
                // 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) {//---------2
                    if (DEBUG) {
                        System.out.println("Creating params from root: " +
                                root);
                    }
                    // 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);
                    }
                }
                rInflateChildren(parser, temp, attrs, true);
                
                if (root != null && attachToRoot) {//---------3
                    root.addView(temp, params);
                }

                if (root == null || !attachToRoot) {//---------4
                    result = temp;
                }
            }
        return result;
    }

注釋1:將root 賦值為result , 用于返回的喊崖;

注釋2:root!=null,就會(huì)解析我們?cè)?xml中設(shè)置的屬性值轉(zhuǎn)換為 :params , 同時(shí) attachToRoot 如果為 false的話荤懂,就可以進(jìn)入if條件里节仿,就可以為我們從xml解析出來的temp設(shè)置LayoutParams 了廊宪。

PS:這里需要主要的是箭启,我們通過 final View temp = createViewFromTag(root, name, inflaterContext, attrs); 這句話只是得到一個(gè) View ,如果xml中寫的TextView 解析出來就是一個(gè) TextViewtemp ,沒有攜帶 寬高傅寡,邊距等信息荐操。

注釋3:root!=null && attachToRoot(true) ,我們xml中解析出的View托启,會(huì)自動(dòng)幫我們addroot里,并且是攜帶LayoutParams.

注釋4:root==null || !attachToRoot 拐迁,把 result 修改為 temp , xml解析出來的 ,注意這時(shí)候是沒有任何 LayoutParams 的唠亚,這也就意味著我們?cè)?code>xml 中設(shè)置的屬性值會(huì)失效。

??回顧一下工窍,我們之前遇到的問題患雏。
我們通過:

LinearLayout ll_container = findViewById(R.id.ll_container);
View child = View.inflate(this,R.layout.item_child,null);
ll_container.addView(child);

這種方式addView. 對(duì)應(yīng)到源碼里是怎么樣的呢淹仑?
跟進(jìn)去層層調(diào)用就到了這里:

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

我們傳入的第三個(gè)參數(shù)為null ,就意味著:

return inflate(resource, null, false);

來重新對(duì)應(yīng)一下匀借,inflate 的三個(gè)參數(shù):

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {

也就是意味著:root == null ,attachToRoot = false.
換一種寫法:

View.inflate(this,R.layout.item_child,null);
等價(jià)于
設(shè)置了:root == null ,attachToRoot = false

根據(jù)我們前面的分析吓肋,只有root!=null 的時(shí)候是鬼,才會(huì)去給 params 賦值均蜜。

if (root != null) {
     params = root.generateLayoutParams(attrs);
    ...
}

看到了吧囤耳,接下來就是 進(jìn)入注釋4那個(gè)代碼了:

if (root == null || !attachToRoot) {//---------4
     result = temp;
}

temp 返回了紫皇,這個(gè) temp 是啥聪铺,就是我們從xml中解析出的View(沒有任何params).

?? 第二個(gè)問題,為什么我們那么寫崩潰報(bào)錯(cuò)撒桨,把之前的拿來再看一下:

LinearLayout ll_container = findViewById(R.id.ll_container);
View child = View.inflate(this,R.layout.item_child,ll_container);
ll_container.addView(child);

與第一次不同的是,我傳入了 ll_container , 就意味著什么普气?意味著我們的 root!=null && attachToRoottrue了现诀。
這樣的話坐桩,就進(jìn)入了我們注釋3處 的代碼了:

 if (root != null && attachToRoot) {//---------3
       root.addView(temp, params);
 }

rootll_container , 系統(tǒng)自動(dòng)幫我們 addView 添加進(jìn)去了绵跷,
root.addView(temp, params); 等價(jià)于ll_container.addView(temp,params) ;
return root了碾局。

我們需要注意的是:

View child = View.inflate(this,R.layout.item_child,ll_container);

這句話本身并不會(huì)讓程序崩潰擦俐,真正讓程序崩潰的是:

ll_container.addView(child);

根據(jù)前面的分析蚯瞧,此時(shí)我們的 child 已經(jīng)被添加進(jìn)這個(gè)ll_container 里了埋合,你如果再把 child 添加到另外一個(gè)容器里,系統(tǒng)是不允許的萄传,一個(gè) child 只能有一個(gè) paraent.
所以當(dāng)你執(zhí)行那句代碼的時(shí)候甚颂,系統(tǒng)這里有判斷:

image

看到了吧蜜猾,所以我們想把 child 添加到 ll_container 中,只需要:

View.inflate(this,R.layout.item_child,ll_container);

寫這一行足矣振诬,下面那行就屬于畫蛇添足了蹭睡。
好了,今天分享就到此結(jié)束了赶么,有問題肩豁,評(píng)論區(qū)見~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌篡悟,老刑警劉巖旗吁,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)歹撒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門竭望,熙熙樓的掌柜王于貴愁眉苦臉地迎上來枫振,“玉大人雀扶,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵库菲,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng)广鳍,這世上最難降的妖魔是什么祖秒? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好址晕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著纷责,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天题禀,我揣著相機(jī)與錄音,去河邊找鬼沛励。 笑死铐拐,一個(gè)胖子當(dāng)著我的面吹牛棒厘,可吹牛的內(nèi)容都是我干的土辩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼楣铁!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起膛薛,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎虑润,沒想到半個(gè)月后冗澈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惜索,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蒲列,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了煤惩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嫉嘀。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖魄揉,靈堂內(nèi)的尸體忽然破棺而出剪侮,到底是詐尸還是另有隱情,我是刑警寧澤洛退,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布瓣俯,位于F島的核電站,受9級(jí)特大地震影響兵怯,放射性物質(zhì)發(fā)生泄漏彩匕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一媒区、第九天 我趴在偏房一處隱蔽的房頂上張望驼仪。 院中可真熱鬧,春花似錦袜漩、人聲如沸绪爸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽奠货。三九已至,卻和暖如春座掘,著一層夾襖步出監(jiān)牢的瞬間递惋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工溢陪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留萍虽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓形真,卻偏偏與公主長(zhǎng)得像杉编,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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

  • 本文主要從如下幾點(diǎn)學(xué)習(xí)LayoutInflater LayoutInflater是啥 LayoutInflater...
    dashingqi閱讀 610評(píng)論 0 3
  • 一王财、適用場(chǎng)景 ListViewListview是一個(gè)很重要的組件,它以列表的形式根據(jù)數(shù)據(jù)的長(zhǎng)自適應(yīng)展示具體內(nèi)容,用...
    Geeks_Liu閱讀 10,668評(píng)論 1 28
  • 通過實(shí)例引出問題 在開始之前我們先來做一個(gè)測(cè)試裕便,我們平時(shí)最常見的就是ListView的Adapter中使用Layo...
    黑色海鷗閱讀 398評(píng)論 0 1
  • RecyclerView Item 布局寬高無效問題探究 前言 這個(gè)問題很早之前就碰到過绒净,后來通過google找到...
    TinyMen閱讀 423評(píng)論 0 0
  • 碼中給容器動(dòng)態(tài)添加子View時(shí)遇到一些問題,當(dāng)時(shí)還是糾結(jié)許久的偿衰。擅總結(jié)者無敵挂疆,寫下此篇總結(jié),問題比較的簡(jiǎn)單下翎,希望對(duì)...
    墨染草閱讀 4,622評(píng)論 1 1