LayoutInflater类做过android开发的人都用过,它是负责把一个layout文件变成View对象的,LayoutInflater类有一个inflater(),它重载的方法有四个
我们就说前二个,现在对他的形参做下解释:
第一个参数是resource:表示是传递的layout文件
第二个参数:把第一个参数layout文件是否挂载在该ViewGroup中,
第三个参数:是一个boolean类型,是否第一个参数是否挂载在第二个参数viewgroup中
我在网上看了下对第三个参数的解释,还是挺好的:
被填充的层是否应该附在root参数内部?如果是false,root参数只是用于为XML根元素View创建正确的LayoutParams的子类
其实意思就是:如果attachToRoot是true的话,那第一个参数的layout文件就会被填充并附加在第二个参数所指定的ViewGroup内。方法返回结合后的View,根元素是第二个参数ViewGroup。如果是false的话,第一个参数所指定的layout文件会被填充并作为View返回。这个View的根元素就是layout文件的根元素。不管是true还是false,都需要ViewGroup的LayoutParams来正确的测量与放置layout文件所产生的View对象。
attachToRoot传入true代表layout文件填充的View会被直接添加进ViewGroup,而传入false则代表创建的View会以其他方式被添加进ViewGroup
相当于viewgroup.addView(view,layoutparams)现在写个demo演示下:
package com.zhimore.layoutinflaterdemo;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
public class MainActivity extends AppCompatActivity {
private LinearLayout ll_root;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.content_main);
ll_root = (LinearLayout) findViewById(R.id.ll_root);
LayoutInflater inflater = LayoutInflater.from(this);
inflater.inflate(R.layout.btn_content,ll_root,true);
}
}
content_main.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<TextView
android:text="Hello World!"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
/>
<TextView
android:text="Hello World!"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
/>
<TextView
android:text="Hello World!"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
/>
<TextView
android:text="Hello World!"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
/>
</LinearLayout>
我们运行起来看下:
现在我们把第二个和第三方参数改成
inflater.inflate(R.layout.btn_content,null,false);
发现这样并没有任何效果,
我们现在自定义一个LinearLayout,
package com.zhimore.layoutinflaterdemo.com.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
import com.zhimore.layoutinflaterdemo.R;
/**
* Created by Administrator on 2016/2/25.
*/
public class MyLinearLayout extends LinearLayout{
public MyLinearLayout(Context context) {
super(context);
}
public MyLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.btn_content,this,true);
}
public MyLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
运行起来
我们发现在自定义ViewGroup的时候,使用这个就是往viewgroup中添加子view的,
当在Fragment的onCreateView()方法中填充并返回View时,要将attachToRoot设为false。如果传入true,会抛出IllegalStateException,因为指定的子View已经有父View了。你需要指定在哪里将Fragment的View放进Activity里,而添加、移除或替换Fragment则是FragmentManager的事情。
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就是Activity中用于放置Fragment的容器,它会作为inflate()方法中的第二个参数被传入onCreateView()中。它也是你在inflate()方法中传入的ViewGroup。FragmentManager会将Fragment的View添加到ViewGroup中,你可不想添加两次。
public View onCreateView(LayoutInflater inflater, ViewGroup parentViewGroup, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_layout, parentViewGroup,
false
);
…
return
view;
}
问题是:如果我们不需在onCreateView()中将View添加进ViewGroup,为什么还要传入ViewGroup呢?为什么inflate()方法必须要传入根ViewGroup?
原因是及时不需要马上将新填充的View添加进ViewGroup,我们还是需要这个父元素的LayoutParams来在将来添加时决定View的size和position。
你在网上一定会遇到一些不正确的建议。有些人会建议你如果将attachToRoot设置为false的话直接将根ViewGroup传入null。但是,如果有父元素的话,还是应该传入的。
Lint会警告你不要讲null作为root传入。你的App不会挂掉,但是可能会表现异常。当你的子View没有正确的LayoutParams时,它会自己通过generateDefaultLayoutParams)计算。
你可能并不想要这些默认的LayoutParams。你在XML指定的LayoutParams会被忽略。我们可能已经指定了子View要填充父元素的宽度,但父View又wrap_content导致最终的View小很多。
下面是一种没有ViewGroup作为root传入inflate()方法的情况。当为AlertDialog创建自定义View时,还无法访问父元素。
AlertDialog.Builder dialogBuilder =
new
AlertDialog.Builder(mContext);
View customView = inflater.inflate(R.layout.custom_alert_dialog,
null
);
...
dialogBuilder.setView(customView);
dialogBuilder.show();
在这种情况下,可以将null作为root ViewGroup传入。后来我发现AlertDialog还是会重写LayoutParams并设置各项参数为match_parent。但是,规则还是在有ViewGroup可以传入时传入它。
避开崩溃、异常表现与误解
希望这篇文章可以帮助你在使用LayoutInflater时避开崩溃、异常表现与误解。下面整理了文章的要点:
1:如果可以传入ViewGroup作为根元素,那就传入它。
2:避免将null作为根ViewGroup传入。
3:当我们不负责将layout文件的View添加进ViewGroup时设置attachToRoot参数为false。
4:不要在View已经被添加进ViewGroup时传入true。
5:自定义View时很适合将attachToRoot设置为true