探索 Android Inflater.inflate()

android-knowledge.jpg

在 Android 開發(fā)中,有很多看起來 “約定俗成” 的代碼慎菲,有時候不妨停下來想一想深層次的含義嫁蛇。最近我在給 Fragment 填充布局時,寫到一句熟悉的代碼:

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.activity_main, container, false);
        return view;
    }

我又一次陷入了思索露该,其中的第三個參數(shù) false 到底是什么意思睬棚,此處能不能變?yōu)?true

探究

首頁我們還是通過函數(shù)本身的注釋來看各參數(shù)的作用,通過源碼查看如下:

    /**
     * Inflate a new view hierarchy from the specified xml resource. Throws
     * {@link InflateException} if there is an error.
     * 
     * @param resource ID for an XML layout resource to load (e.g.,
     *        <code>R.layout.main_page</code>)
     * @param root Optional view to be the parent of the generated hierarchy (if
     *        <em>attachToRoot</em> is true), or else simply an object that
     *        provides a set of LayoutParams values for root of the returned
     *        hierarchy (if <em>attachToRoot</em> is false.)
     * @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.
     * @return The root View of the inflated hierarchy. If root was supplied and
     *         attachToRoot is true, this is root; otherwise it is the root of
     *         the inflated XML file.
     */
    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        ...
    }
   

上面的解釋比較官方抑党,但是不一定好理解包警。至少我就看了好幾遍,最后結(jié)合網(wǎng)上的總結(jié)才明白其含義新荤。

首先就 LayoutInflater.inflate()這個函數(shù)而言揽趾,功能是將一段 XML 資源文件加載成為 View。所以通常用于將 XML 文件實例化為 View苛骨。然后獲取 View 上的組件最后操作之篱瞎。

對于這個函數(shù),我們通常容易引發(fā)歧義的地方在 attachToRoot的值痒芝,當這個值為 true/false 時俐筋,是由顯著的區(qū)別的。

attachToRoot = true

當設(shè)置 attachToRoot = true 時, 表示 xml 實例化的 View 會被添加到第二個參數(shù) rootView 中严衬。 所以此時相當于處理了兩件事:

  • 將 XML 實例化為 View澄者;
  • 將實例化的 View 添加到非空的 rootView 中;

經(jīng)典的使用方法请琳,對于不為空的 container粱挡,

LayoutInflater.from(getContext()).inflate(R.layout.demo, container, true);

或者:

LayoutInflater.from(getContext()).inflate(R.layout.demo, container);

當 ViewGroup 不為空時,第三個 true 參數(shù)可以直接省略俄精。

attachToRoot = false

當設(shè)置 attachToRoot = false 時询筏,表示直接將 xml 實例化為 View,而不用將 View 添加到指定的 ViewGroup 中竖慧。如果需要將 View 加入到 ViewGroup 中嫌套,還需要手動的調(diào)用 ViewGroup.addView(view)

經(jīng)典寫法為:

LayoutInflater.from(getContext()).inflate(R.layout.demo, container, false);

注意:對于上面的第二個參數(shù) container圾旨,如果直接設(shè)置為 null踱讨,有可能導(dǎo)致 View 的位置或者大小等顯示不正確。所以上面代碼做了兩件事:

  • 將 XML 實例化為 View;
  • 根據(jù) ViewGroup 為子元素 View 設(shè)置正確的位置砍的、大小等 LayoutParams 屬性痹筛;

所以,實例化 View 時挨约,如果已經(jīng)明確知道父容器 ViewGroup味混,請直接傳遞 ViewGroup,避免出現(xiàn)子 View 顯示時的位置诫惭、大小等異常情形。

比較

對于非空的 ViewGroup continer蔓挖,以下兩句是等效的:

View buttonView = LayoutInflater.from(getContext()).inflate(R.layout.ugly_button, container, false);
container.addView(buttonView);

View buttonView = LayoutInflater.from(getContext()).inflate(R.layout.ugly_button, container, true);

應(yīng)用

通常將 XML 實例化為 View 涉及到以下常見情形:

ListView/RecyclerView 中 Adapter.getView

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder viewHolder;

    if (convertView == null) {
        convertView = LayoutInflater.from(context).inflate(R.layout.ugly_button, parent, false);
        viewHolder = new ViewHolder();
        convertView.setTag(viewHolder);
    } else {
        viewHolder = (ViewHolder) convertView.getTag();
    }

    // ...
    return convertView;
}

此處應(yīng)該設(shè)置 attachToRoot = true

因為 ListView 何時顯示夕土,以及如何顯示。應(yīng)該是由 ListView 決定的。Adapter.getView() 主要是根據(jù)指定的函數(shù)返回相應(yīng)的 View 實例怨绣。顯示邏輯則由 ListView 處理角溃,因此設(shè)置 attachToRoot = true

自定義 View

為了使得布局的層級足夠淺篮撑,我們在布局時通常使用 <merge></merge> 定義 layout 的根元素减细,然后將 layout 填充到自定義的 View 中。

res/layout/bottom_tab_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical">
        <ImageView
            android:id="@+id/tab_icon"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:paddingTop="2dp" />
        <TextView
            android:id="@+id/tab_label"
            android:layout_marginTop="1dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="14dp"
            tools:text="Tab"/>
    </LinearLayout>
</merge>

BottomTabItem.java:

public class BottomTabItem extends RelativeLayout {
    ...
    
    public void init() {
        LayoutInflater.from(getContext()).inflate(R.layout.bottom_tab_item, this, true);
    }
    ...
}

對于自定義的 BottomTabItem赢笨,需要將定義的 XML 文件添加到其中未蝌。所以需要直接將 XML 實例化 View 作為子元素,添加到 BottonTabItem 中茧妒。

所以萧吠,對于自定義 View, 需要設(shè)置 attachToRoot = true桐筏。

Fragment.onCreateView()

回顧我們最看我們最開始遇到的代碼塊:

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.activity_main, container, false);
    return view;
}

onCreateView 函數(shù)直接反饋 View纸型,可以看出函數(shù)只需要拿到實例化的 View,至于 View 是如何添加到 Fragnent 中梅忌,則是底層 Fragment 的邏輯狰腌。

如果在 Fragment 中不恰到的處理這個 attchToRoot 參數(shù),會直接導(dǎo)致嚴重的崩潰性問題牧氮。而且通常出現(xiàn)這種問題時很不容易被發(fā)現(xiàn)琼腔。

所以,對于 Fragment 添加 layout蹋笼,需要設(shè)置 attachToRoot = false展姐。

ListView headerView/footerView 等

對于 ListView.addHeaderView() / addFooterView() 也是我們經(jīng)常使用 LayoutInflator.inflate() 很多的場合。

從 ListView.addHeaderView(View view) 函數(shù)定義可以看到剖毯,ListView 需要將實例化的 View 添加作為 header/footer view圾笨,所以顯然需要的只是 View 的實例。添加或者刪除操作通過 ListView 內(nèi)部進行處理逊谋。

所以擂达,對于 ListView 添加 headerView, footerVuew 等,需要設(shè)置 attachToRoot = false胶滋。

結(jié)語

知道 LayoutInflater.inflate 的細微知識點板鬓,可能會花費你一下午的時間。但是卻可以讓你對所寫的代碼更加自信究恤,并且能夠?qū)κ挛镎J識更加深刻俭令。何樂而不為呢?

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末部宿,一起剝皮案震驚了整個濱河市抄腔,隨后出現(xiàn)的幾起案子瓢湃,更是在濱河造成了極大的恐慌,老刑警劉巖赫蛇,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绵患,死亡現(xiàn)場離奇詭異,居然都是意外死亡悟耘,警方通過查閱死者的電腦和手機落蝙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來暂幼,“玉大人筏勒,你說我怎么就攤上這事∷谑模” “怎么了奏寨?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鹰服。 經(jīng)常有香客問我病瞳,道長,這世上最難降的妖魔是什么悲酷? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任套菜,我火速辦了婚禮,結(jié)果婚禮上设易,老公的妹妹穿的比我還像新娘逗柴。我一直安慰自己,他們只是感情好顿肺,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布戏溺。 她就那樣靜靜地躺著,像睡著了一般屠尊。 火紅的嫁衣襯著肌膚如雪旷祸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天讼昆,我揣著相機與錄音托享,去河邊找鬼。 笑死浸赫,一個胖子當著我的面吹牛闰围,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播既峡,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼羡榴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了运敢?” 一聲冷哼從身側(cè)響起炕矮,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤么夫,失蹤者是張志新(化名)和其女友劉穎者冤,沒想到半個月后肤视,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡涉枫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年邢滑,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片愿汰。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡困后,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出衬廷,到底是詐尸還是另有隱情摇予,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布吗跋,位于F島的核電站侧戴,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏跌宛。R本人自食惡果不足惜酗宋,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望疆拘。 院中可真熱鬧蜕猫,春花似錦、人聲如沸哎迄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽漱挚。三九已至翔烁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間棱烂,已是汗流浹背租漂。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留颊糜,地道東北人哩治。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像衬鱼,于是被迫代替她去往敵國和親业筏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350

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

  • 一鸟赫、適用場景 ListViewListview是一個很重要的組件蒜胖,它以列表的形式根據(jù)數(shù)據(jù)的長自適應(yīng)展示具體內(nèi)容,用...
    Geeks_Liu閱讀 10,654評論 1 28
  • ¥開啟¥ 【iAPP實現(xiàn)進入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程消别,因...
    小菜c閱讀 6,373評論 0 17
  • 大家對LayoutInflater一定不陌生,它主要用于加載布局台谢,在Fragment的onCreateView方法...
    凡諾依曼閱讀 15,448評論 4 26
  • 2017年11月2號 星期四 貴陽 天氣晴 16℃~25℃ 今天是收獲滿滿的一天寻狂,開場舞蹈引人入場,讓人直接進...
    陳賀雄閱讀 152評論 1 1
  • 寫作朋沮,對一個人而言蛇券,是一段旅程,是一個自我發(fā)現(xiàn)的過程樊拓,我們在寫作這個旅途中纠亚,往往會自我迷失,不知所措筋夏。是的蒂胞,寫作需...
    海哥0001閱讀 227評論 0 1