Understanding Android's LayoutInflater.inflate()

理解Android的LayoutInflater.inflate()方法

[Android 官方文档](http://developer.android.com/reference/android/view/LayoutInflater.html#inflate(int, android.view.ViewGroup, boolean))
BigNerdranch 博客
注:相对于官方博客,BigNerdranch上的博客提供了更接地气的通俗易懂的解释。

下文为汲取了诸多前辈的经验知识后个人总结所得,如有理解或解释错误的地方,请诸位看客老爷指正!


inflate意思是膨胀,打气。从XML到View,原本的code到可视化的view,就是一个充气、具象化的过程。所以我理解为具象化。

LayoutInflater 的作用是 将一个XML布局文件实例化到其对应的View对象中。它不能直接用,需要检索当前的上下文来获取配置,从而实例化。

LayoutInflater.inflate:

    View inflate (int resource, ViewGroup root, boolean attachToRoot)
参数名 解释
resource int,要加载的XML布局文件ID
root ViewGroup,可选项(取决于第三个参数),作为要加载的布局文件的父节点
attachToRoot boolean,是否将新具象化的层次结构附加到根参数

官方文档给的解释到此为止了,那么这个attachToRoot到底做了什么?


通俗解释

如果attachToRoot被设为true,那么第一个参数中被指定的布局会被附加到第二个参数中指定的ViewGroup中。
然后该方法返回它们组合形成的View,并将第二个参数ViewGroup作为根节点。

如果attachToRoot被设为false,那么第一个参数中指定的布局被具象化,并作为一个View返回。
这个意思是,之后这个View会以其他某种方式加入到ViewGroup中。

在以上两种情况下,我们都需要ViewGroup的布局参数,来确定我们第一个参数具象化的View的size和它的位置。


设 attachToRoot 为 true

假设我们在一个XML布局文件 custom_button 中定义了一个Button,并设置了它的宽高为match_parent。

<Button xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
            android:id="@+id/custom_button"/>

现在,我们通过代码方式把这个Button添加到一个Fragment或者Activity的线性布局里。假设这个LinearLayout已经是一个
成员变量,记为 mLinearLayout,我们这样添加Button:

inflater.inflate(R.layout.custom_button, mLinearLayout, true);

我们首先指明我们想要从这个button的布局文件中具象化它;之后我们告诉inflater,我们想把这个button添加到mLinearLayout中。那布局参数就很高兴,因为知道了button已经被添加到一个线性布局文件中了。那这Button的布局参数就应该是LinearLayout.LayoutParams

另一种传递true值给attachToRoot的情况是使用定制View。看一个例子。在一个布局文件中用<merge>标签作为它的根节点。
这表示这个布局文件 允许 根据它可能具有的根ViewGroup的类型 来灵活安排。

public class MyCustomView extends LinearLayout {
    ...
    private void init() {
        LayoutInflater inflater = LayoutInflater.from(getContext());
        inflater.inflate(R.layout.view_with_merge_tag, this);
    }
}

在这个例子中,布局文件没有作为根的ViewGroup,所以我们指定了我们定制的LinearLayout作为它的根。
如果我们的布局文件有一个FrameLayout作为它的根而不是<merge>,那么这个FrameLayout和它的子节点就会正常具象化。然后这个FrameLayout和子节点会被加载到这个LinearLayout,使得LinearLayout成为根节点包含其它节点。


设 attachToRoot 为 false

在这种情况下,在第一个参数中指定的View不会在这个时候被添加到第二个参数所指的ViewGroup。
还是刚才的Button。当我们传false给attachToRoot的时候,依然可以把Button添加到mLinearLayout上————只要我们后面自己手动添加即可。

    Button button = (Button) inflater.inflate(R.layout.custom_button, mLinearLayout, false);
    mLinearLayout.addView(button);

上面这两行代码和之前的效果是相同的。传入false后,我们不想第一时间把这具象化的view添加到第二个参数指定的ViewGroup。而是,之后某个时间添加。在这个例子中,某个时间就是在具象化后,调用addView()方法的时候。

上面的例子中,为了实现相同的效果,传入false的做法比传入true的做法要费事一点。那么为什么还这么做?有没有一些情况下必须要这样做的呢?答案是肯定的!有些情况下,必须这么做~

一个RecyclerView的子类在具象化的时候,attachToRoot必须传入false。子视图是在onCreateViewHolder()方法中具象化的:

public ViewHolder onCreateViewHolder(LayoutInflater inflater, ViewGroup parent, int viewType){
    View view = inflater.inflate(android.R.layout.list_item_recyclerView, parent, false);
    return new ViewHolder(view);
}

这里,RecyclerView(而不是我们)负责决定什么时候具象化并且展示它的子视图。因此,任何时候attachToRoot参数就一定要是false,我们不负责添加它到ViewGroup。

当我们在onCreateView()中具象化并且返回一个Fragment的视图时,要确保传false到attachToRoot中。如果我们传入true,就会得到一个IllegalStateException,因为指定的孩子View已经有父ViewGroup了。我们应该做的是,指明这个Fragment的view将会放在我们的Activity的什么地方。这个,就是FragmentManager的工作了,它负责添加、移除和替换Fragments:

FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentById(R.id.root_viewGroup);

if (fragment == null) {
    fragment = new MainFragment();
    fragmentManager.beginTransaction()
        .add(R.id.root_viewGroup, fragment)
        .commit();
}

root_viewGroup容器是我们指定给onCreateView()的第一个参数,作为ViewGroup来容纳我们的Fragment。同时,它也作为LayoutInflater.inflate()的第二个参数。FragmentManager会将Fragment的View添加到这个ViewGroup当中,当然,我们不希望添加它两次,所以,传递给attachToRoot的值是false:

public View onCreateView(LayoutInflater inflater, ViewGroup parentViewGroup, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_layout, parentViewGroup, false);
    …
    return view;
}

如果我们不想在onCreateView()中将View附加给ViewGroup,那为什么我们还要将Fragment的父ViewGroup放在第一位置呢?又为什么inflate()方法要求有一个根ViewGroup呢?
这是因为,即使我们不想立刻将具象化的View添加到它的父ViewGroup中去,为了知道当这个View最终被添加的时候,它的size和位置,我们也要需要父ViewGroup的布局参数LayoutParams

最后一点建议,通常来说,当我们可以添加父ViewGroup的时候,我们还是加上,不将其设置为null。


总结

希望这篇理解文章可以帮你在用LayoutInflater时避免程序崩溃、误解或者不能掌控它的行为。
下面是在特定场合使用时的一些注意点:

  • 当我们有一个父ViewGroup可以传入ViewGroup时,将它传进去。
  • 尽量避免传入null给root ViewGroup。
  • 当我们不需要负责将布局文件的视图添加给它的root ViewGroup的时候,设 attachToRoot 为 false。
  • 不要给一个已经被添加到ViewGroup的View传入true。
  • 定制视图是传入true值的经典运用。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容