Android角落 - LayoutInflater第三個參數

通常情況下,我們需要將一個xml解析為view,我們都可以很順手的寫出這樣的語句:

LayoutInflater.from(context).inflate(id,parent,false);

或者是這樣

LayoutInflater.from(context).inflate(id,null);

又或者是這樣

View.inflate(context,id,parent);

一般而言,前兩種用的比較多,最后一種估計比較少人用吧。

但我們今天不討論inflate應該如何使用,而是討論一下以下幾個問題:

  • inflate()的第三個參數是用來干啥的。
  • 第三個參數為啥我們平時都寫false,或者直接不要第二第三個給null
  • 什么情況下第三個參數應該給true

而我們的探索步驟也很簡單:

  • 測試inflate()第三個參數的不同值的情況
  • 往源碼挖掘
  • 總結

Step 1:測試

測試之前,我們先進行一下基本的布局

activity的布局很簡單,只有一個RelativeLayout,其id命名為parent

activity布局

而測試的view則是一個textview,其id命名為test_text

test view

首先我們測試一下第三個參數為true的情況:

在java中find出parent,然后通過LayoutInflater將我們的測試的view的xml轉換為view,其中第三個參數設定為true:

public class MainActivity extends AppCompatActivity {
    private RelativeLayout parent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
       ...略
        parent= (RelativeLayout) findViewById(R.id.parent);
        View view=LayoutInflater.from(this).inflate(R.layout.item_test,parent,true);
    }
}

結果:

result

結果很普通,跟我們平時addView沒差別。

然后我們到Hierarchy View工具查看view的層次,得到以下結果:

hierarchy view

留意一下紅框位置,我們的測試view變成了parent的子view。

接下來我們測試一下第三個參數為false的情況:

基本步驟同上,不過我們將textview的text改為"false of the 3rd param",并將第三個參數改為false

結果:

result2

這次我們得到的是一片空白。

同樣我們到Hierarchy View工具查看:

hierarchy view2

跟上次比較,我們發現我們的textview并沒有被add到parent里面。

到這里我們不妨猜測一下,這第三個參數會不會是用來將xml轉化得到的view給add到第二個參數root中的?


Step 2:源碼

事實上,在第一步我們的猜測基本可以確認這個結論了。

不過了解一個框架原理或代碼原理,最直接有效的還是去查看源碼,查看源碼之前,還需要查看注釋。

關于inflate第三個參數的注釋是這樣的:

@param attachToRoot

  • Whether the inflated hierarchy should be attached to
    the root parameter? If false, root is only used to create the
    correct subclass of LayoutParams for the root view in the XML.

渣翻:

這個參數決定被inflate的view是否會附加(add)到根(父,即第二個參數的viewgroup)布局。如果不是,那么根(父)布局只會被用來創建適合子類的LayoutParams(測量)。

我們都知道,子view的LayoutParams實際上是通過其父View得到的,父View是什么viewgroup,那么子view就會得到什么樣的LayoutParams。

而這第三個參數的作用只有一個:是否將inflate出來的view添加到第二個參數的父view中,而無論該參數如何,被轉換出來的view都會創建第二個參數的父view的LayoutParams。

看完注釋我們大概了解到第三個參數的作用,接下來我們扎入到源碼中,看看到底是怎么玩的。

inflate()源碼:

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        if (DEBUG) {
            Log.d(TAG, "INFLATING from resource: \\\\\\\\"" + res.getResourceName(resource) + "\\\\\\\\" ("
                    + Integer.toHexString(resource) + ")");
        }

        final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }

在這里我們可以看到,在調用這個方法的時候會做以下兩個步驟:

  • 通過上下文得到Resource類
  • 通過得到的Resource再得到xml解析類,如果寫過Android解析xml文件的話,那么對這個東東應該不難理解

接下來我們看看調用的inflate方法

代碼有100多行,我們就挑選與第三個參數有關的代碼查看

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            ...Trace配置,attrs獲取
            View result = root;
            try {
                ...略

                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);
                        if (!attachToRoot) {
                            // Set the layout params for temp if we are not
                            // attaching. (If we are, we use addView, below)
                            temp.setLayoutParams(params);
                        }
                    }

                    ...略

                    // 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;
                    }
                }

            }
            ...異常捕獲(xml解析異常/inflate異常)等

            return result;
        }
    }

整理過后的代碼如上,我們逐步分析一下:

  • 首先需要注意的是,在超大的try/catch前,會將root賦值給result,這個就是最終返回的結果。

  • 在第一次出現attachToRoot參數,是在判斷標簽是否為merge的時候。

    • 我們都知道,merge是減少布局層次的神器,特別是相同viewgroup的嵌套里面真的很好用,但因為merge并不會產生根布局(本身就是根標簽),所以如果我們inflate的話,就必須傳入root,這樣很好理解為什么inflate帶merge標簽時為什么必須傳入rootView

    • 同時,我們在別的布局里使用merge的布局文件的話,都是通過<include>把該布局給引用進來,也就是說,merge并不能單獨存在,必須依附在父布局里面。

    • 所以如果父控件和attachToRoot不滿足其一就會拋異常。

  • rInflate()方法我們暫時略過,因為在這個if里面執行到rInflate只會是繼續拋異常。

  • 第二次出現attachToRoot參數則是在接下來的else里面,在看它的方法前,不妨看看它前面執行了什么:

    • final View temp = createViewFromTag(root, name, inflaterContext, attrs);在這段代碼里,創建了一個臨時的view,至于createViewFromTag是什么,這里只告訴你它通過工廠創建一個view,并且根據是否需要賦予屬性來創建對應的view。
  • 第二次出現attachToRoot在臨時的view創建了之后,通過代碼我們可以看到,此時因為在root!=null的if里面,所以通過root生成它的LayoutParams,如果第三個參數為false,就把這個LayoutParams賦值給這個臨時的view

  • 第三次和第四次出現attachToRoot參數,就是在返回view之前了,這里可以看到

    • 如果root不空而且attachToRoot=true,就把臨時的view給添加到root里面(帶LayoutParams),此時result依然等于root,最終返回
    • 如果root為空或者attachToRoot=false,那么就將臨時的view返回賦值給result,最終返回。

上面就是對這段代碼的分析,從中我們可以總結一下:

  • 無論root是否為空,都會產生一個臨時的view。

    • 如果root不為空,則這個臨時的view會有root的LayoutParams
    • 如果root為空,則這個臨時的view就僅僅是一個不帶參數的view,跟我們直接new出一個對象差不多,只是包含有attr屬性而已。
  • 第二個參數(root)與第三個參數(attachToRoot)的問題

    • 如果root不空同時attachToRoot=true,那么返回的不是這個view,而是root
    • 如果上述條件不成立,那么返回的才是這個臨時的view

Step 3:attachToRoot的取值問題

說了這么多,那么我們的attachToRoot到底應該如何取。

在這里,我更傾向于給false。因為通過第二步的分析我們知道,如果同時滿足root不空同時attachToRoot=true,我們得到的是root。

普通情況下可能沒問題,但如果有時候遇到跟本例一樣,需要inflate的xml是一個控件作為根節點,而我們需要得到這個view,如果第二個和第三個參數都滿足的話,我們得到的view如果強轉則會拋出cast異常。當然,我們可以通過view.findViewById,但何必多此一舉呢對吧。

當然,這個還是看情況,畢竟知道了里面做了什么后我們就不怕為何無端端報錯了對吧。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評論 6 538
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,278評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,241評論 2 375

推薦閱讀更多精彩內容