今天在写Fragment加载布局文件时,用到了LayoutInflater 的 inflate方法。
当时用的是 inflate(resource, root)。但是当运行程序时,报了个异常:
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
后来经过调查,是inflate方法使用不当造成的。
下面整理一下:
获得LayoutInflater实例的四种方法:
- LayoutInflater inflater = getLayoutInflater();
- LayoutInflater localinflater =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- LayoutInflater inflater = LayoutInflater.from(context);
LayoutInflater 的inflate方法,重载了四种调用方式,分别为:
- public View inflate(int resource, ViewGroup root)
- public View inflate(int resource, ViewGroup root, boolean attachToRoot)
- public View inflate(XmlPullParser parser, ViewGroup root)
- public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)
通过看源码发现,其实这四个方法最后都是走得第四种方法。
先来介绍一下,第三个 Boolean型的参数 attachToRoot 。
其实从翻译来看的话,就是是否绑定到根组件的意思。
当我们第二个参数不为空的时候,attachToRoot 默认为true,即绑定到其父组件。
所以,在Fragment中, inflater.inflate(R.layout.fragment_alarm_list, container) 就相当于 inflater.inflate(R.layout.fragment_alarm_list, container,true)
但是Fragment 中是实现了绑定这个操作的,所以我们不需要手动去 addView,只需在oncreate方法中返回一个view。
这样,当我们这样写的时候 inflater.inflate(R.layout.fragment_alarm_list, container) ,就相当于 走了两遍 绑定,这就是上面提到异常的原因。
解决这个问题的方法有两种:
1. 第一个参数传一个 布局文件的ID,第二个直接传一个null。
inflater.inflate(R.layout.fragment_alarm_list, container)
当第二个参数为空时,attachToRoot 默认为false。
所以相当于 inflater.inflate(R.layout.fragment_alarm_list, null,false);
这样不会去绑定,当然就解决了这个问题。但是,虽然大部分时候可能没有问题,到时到了有些特定的场合的话,这样写就不行了。
当我们给加载的layout外层容器,设置了宽高时,不会起作用,全都变成了wrap_content了。
原因是,当第二个参数 ViewGroup 为null时,就会忽略 LayoutParams。
所以好的写法应该是下面这种
2.inflater.inflate(R.layout.fragment_alarm_list, container,false);
直接把第三个参数attachToRoot ,写成false。这样,我们设置的宽高属性会起作用,也不会出现异常。
同理,ListView 的 adapter 也是这样。
总结一下:
1.若我们采用 convertView = inflater.inflate(R.layout.item_list, null);方式填充视图,item布局中的根视图的layout_XX属性会被忽略掉,然后设置成默认的包裹内容方式
2.如果我们想保证item的视图中的参数不被改变,我们需要使用convertView = inflater.inflate(R.layout.item_list, parent,false);这种方式进行视图的填充
3.除了使用这种方式,我们还可以设置item布局的根视图为包裹内容,然后设置内部控件的高度等属性,这样就不会修改显示方式了。