LayoutInflater.inflate()方法两个参数和三个参数的区别:是否将新创建的 View 对象立即添加到指定的父级视图中。inflate()方法三个参数比两个参数多了一个名为 attachToRoot 的参数,表示是否将新创建的 View 对象添加到 root 参数指定的父级视图中。
一、LayoutInflater.inflate()方法两个参数和三个参数的区别
LayoutInflater.inflate() 方法是用于将 XML 布局文件转换为 Android 中的 View 对象。其具有两个重载版本:一个是接受两个参数的版本,另一个是接受三个参数的版本。
- inflate(int resource, ViewGroup root) 方法接受两个参数: resource 和 root 。其中 resource 指定需要转换的布局文件的资源 ID, root 是该文件在当前布局中的根视图,即父级视图。如果 root 参数为 null ,则将忽略此参数并将 resource 中定义的布局文件最外层的根视图设置为新 View 对象的父级视图。
- inflate(int resource, ViewGroup root, boolean attachToRoot) 方法接受三个参数:其中多了一个名为 attachToRoot 的布尔值参数。这个参数表示是否将新创建的 View 对象添加到 root 参数指定的父级视图中。如果 attachToRoot 参数为 true,则新的 View 对象将立即添加到 root 指定的父级视图中,而不是等到后面再次手动添加。如果为 false,则将新创建的 View 对象返回给调用者,并且可以在以后的代码中使用 addView() 方法将其添加到父级视图中。
因此,区别在于是否将新创建的 View 对象立即添加到指定的父级视图中。
二、三个参数的inflate方法示例
方法头如下:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)
分三种情况说明:
1、root不为null,attachToRoot为true
当root不为null,attachToRoot为true时,表示将resource指定的布局添加到root中,添加的过程中resource所指定的的布局的根节点的各个属性都是有效的。比如下面一个案例,Activity的布局如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="@+id/ll"
tools:context="org.sang.layoutinflater.MainActivity">
</LinearLayout>
linearlayout.xml的布局如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@color/colorPrimary"
android:gravity="center"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
把linearlayout.xml布局文件添加到activity的布局中:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout ll = (LinearLayout) findViewById(R.id.ll);
LayoutInflater inflater = LayoutInflater.from(this);
inflater.inflate(R.layout.linearlayout, ll,true);
}
注意到,这里没写将inflate出来的View添加到ll中的代码,但是linearlayout布局文件就已经添加进来了,这就是因为第三个参数设置为了true,表示将名列前茅个参数所指定的布局添加到第二个参数的View中。最终显示效果如下:
如果多写一行代码,如下:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout ll = (LinearLayout) findViewById(R.id.ll);
LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.linearlayout, ll, true);
ll.addView(view);
}
这个时候再运行,系统会抛如下异常:
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
原因就是当第三个参数为true时,会自动将名列前茅个参数所指定的View添加到第二个参数所指定的View中。
2、root不为null,attachToRoot为false
如果root不为null,而attachToRoot为false的话,表示不将名列前茅个参数所指定的View添加到root中,那么,既然不添加到root中,为什么不把第二个参数直接给null?其实不然,这里涉及到另外一个问题:在开发的过程中给控件所指定的layout_width和layout_height到底是什么意思?该属性的表示一个控件在容器中的大小,就是说这个控件必须在容器中,这个属性才有意义,否则无意义。这就意味着如果直接将linearlayout加载进来而不给它指定一个父布局,则inflate布局的根节点的layout_width和layout_height属性将会失效(因为这个时候linearlayout将不处于任何容器中,那么它的根节点的宽高自然会失效)。如果想让linearlayout的根节点有效,又不想让其处于某一个容器中,那就可以设置root不为null,而attachToRoot为false。这样,指定root的目的也就很明确了,即root会协助linearlayout的根节点生成布局参数,只有这一个作用。还是上面的布局文件,如果想将之添加到activity的布局中又该如何:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout ll = (LinearLayout) findViewById(R.id.ll);
LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.linearlayout, ll, false);
ll.addView(view);
}
注意,这个时候需要手动的将inflate加载进来的view添加到ll容器中,因为inflate的最后一个参数false表示不将linealayout添加到ll中。显示效果和上文一样。
3、root为null
当root为null时,不论attachToRoot为true还是为false,显示效果都是一样的。当root为null表示不需要将名列前茅个参数所指定的布局添加到任何容器中,同时也表示没有任何容器来来协助名列前茅个参数所指定布局的根节点生成布局参数。还是使用上文提到的linearlayout,来看下面一段代码:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout ll = (LinearLayout) findViewById(R.id.ll);
LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.linearlayout, null, false);
ll.addView(view);
}
当第二个参数为null,第三个参数为false时(即使为true显示效果也是一样的,这里以false为例),由于在inflate方法中没有将linearlayout添加到某一个容器中,所以需要手动添加,另外由于linearlayout并没有处于某一个容器中,所以它的根节点的宽高属性会失效,显示效果如下:
这个时候不管给linearlayout的根节点的宽高设置什么,都是没有效果的,它都是包裹button,如果修改button,则button会立即有变化,因为button是处于某一个容器中的。
三、两个参数的inflate方法
源码:
public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
return inflate(parser, root, root != null);
}
这是两个参数的inflate方法,注意两个参数实际上最终也是调用了三个参数。
两个参数的inflate方法分为如下两种情况:
- root为null,等同于第二点的第3种情况。
- root不为null,等同于第二点的第1种情况。
延伸阅读1:为什么Activity布局的根节点的宽高属性会生效
原因很简单,大部分情况下一个Activity页面由两部分组成(Android的版本号和应用主题会影响到Activity页面组成,这里以常见页面为例),页面中有一个拔尖View叫做DecorView,DecorView中包含一个竖直方向的LinearLayout,LinearLayout由两部分组成,名列前茅部分是标题栏,第二部分是内容栏,内容栏是一个FrameLayout,在Activity中调用setContentView就是将View添加到这个FrameLayout中,所以给大家一种错觉仿佛Activity的根布局很特殊,其实不然。