理解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添加到一个Fragment或者Activity的线性布局里。假设这个LinearLayout已经是一个
成员变量,记为 mLinearLayout,我们这样添加Button:

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

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

另一种传递true值给attachToRoot的情况是使用定制View。看一个例子。在一个布局文件中用标签作为它的根节点。
这表示这个布局文件 允许 根据它可能具有的根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作为它的根而不是,那么这个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值的经典运用。

更多相关文章

  1. Android实现书籍翻页效果--扩展版
  2. Android(安卓)在 LinearLayout 添加分割线 divider
  3. Android实现书籍翻页效果--扩展版(转)
  4. 【Unity3D】与Android相互传递消息
  5. Android快速开发架构PlanA,一周一个APP,持续维护中!
  6. Android中调用Unity3D探索
  7. 【月入41万】Mono For Android中使用百度地图SDK
  8. Android中的prelink技术
  9. Android: AIDL --- Android中的远程接口

随机推荐

  1. 关于android avd目录的设置
  2. Android中的持续集成CI系统、代码review
  3. Android Studio使用小指南
  4. android studio第一个项目hello world之A
  5. Android应用数据备份
  6. Android 五大布局
  7. Android File类
  8. Android中的常用控件
  9. Android Bluetooth源码分析总结 - framew
  10. Android软键盘弹出,界面整体上移