LayoutInflate是什么?
答:一个用于加载布局的系统服务,就是实例化与Layout XML文件对应的View对象,称之为布局加载器,不能直接使用, 需要通过getLayoutInflater( )方法或getSystemService( )方法来获得与当前Context绑定的 LayoutInflater实例!
在介绍LayoutInflate使用,可以回顾一下哪些地方用过LayoutInflate:
LayoutInflater inflater = getLayoutInflater();
View view = inflater.inflate(R.layout.activity_main, null);
View view = inflater.inflate(R.layout.fragment_guide_one, container, false);
return view;
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = LayoutInflater.from(convertView.getContext()).inflate(R.layout.activity_main, parent, false);
return view;
}
LayoutInflater inflater =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
这些使用场景都有一个共同的特点:需要将一个XML布局文件转化为View,LayoutInflate的主要功能就是将XML文件实例化为相应的Veiw对象。
关于LayoutInflate的简单使用流程如下:
LayoutInflate本身是一个抽象类,不可以直接通过new的方式去获得它的实例,主要有以下三种方法:
LayoutInflater inflater1 = LayoutInflater.from(context);
LayoutInflater inflater2 = getLayoutInflater(); // 在Activity中调用
LayoutInflater inflater3 = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
三种方法的底层实现其实一样。
这里看一下前两种方法的底层实现:
public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
在Activity内部调用getLayoutInflater方法,其实是调用PhoneWindow的mLayoutInflate:
public PhoneWindow(Context context) {
super(context);
mLayoutInflater = LayoutInflater.from(context);
}
所以,这几个方法实际上殊途同归,都是通过调用Context的getSystemService方法去获取。获取到的是PhoneLayoutInflater
这个实现类,具体的获取过程就不在这里展开分析了。
public class Policy implements IPolicy {
...
public LayoutInflater makeNewLayoutInflater(Context context) {
return new PhoneLayoutInflater(context);
}
}
关于加载布局的方法:
public View inflate (int resource, ViewGroup root, boolean attachToRoot)
该方法的三个参数依次是:
除了上述的加载布局方法,还有以下重载方法:
inflate(int resource, ViewGroup root);
inflate(XmlPullParser parser, ViewGroup root);
inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)
加载布局完,通过LayoutInflate.LayoutParams设置相关属性
特定情况下,需要使用Java代码往布局中动态添加组件或布局
加载布局流程:
LinearLayout ly = new LinearILayout(this);
Button btnOne = new Button(this);
ly.setOrientation(LinearLayout.VERTICAL);
LinearLayout.LayoutParams lp1 = new LinearLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
设置位置,一般考虑RelativeLayout
,用到addRule()
方法
RelativeLayout rly = new RelativeLayout(this);
RelativeLayout.LayoutParams lp2 = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp2.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
Button btnOne = new Button(this);
rly.addView(btnOne, lp2);
简单布局加载示例:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RelativeLayout rly = new RelativeLayout(this);
Button btnOne = new Button(this);
btnOne.setText("按钮1");
Button btnTwo = new Button(this);
btnTwo.setText("按钮2");
// 为按钮1设置一个id值
btnOne.setId(123);
// 设置按钮1的位置,在父容器中居中
RelativeLayout.LayoutParams rlp1 = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
rlp1.addRule(RelativeLayout.CENTER_IN_PARENT);
// 设置按钮2的位置,在按钮1的下方,并且对齐父容器右面
RelativeLayout.LayoutParams rlp2 = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
rlp2.addRule(RelativeLayout.BELOW, 123);
rlp2.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
// 将组件添加到外部容器中
rly.addView(btnTwo, rlp2);
rly.addView(btnOne, rlp1);
// 设置当前视图加载的View即rly
setContentView(rly);
}
}
备注:前述的setId()
方法会报错
方案一:通过调用View.generateViewId()
作为setId的参数,但此方案不是最佳方案,因为View.generateViewId()方法必须为SDK版本17及以上才行,否则报错。
方案二:在res/values/下添加id.xml(名字可随意)文件,代码如下:
<resources>
<item name="my_view" type="id" />
resources>
然后在代码中做如下设置即可:
my_view.setId(R.id.my_view);
setContentView()
方法加载布局对象,移除组件可以调用容器.removeView(要移除的组件)
在实际中更多时候时动态地添加View控件或XML布局
动态增加View
动态添加组件的写法有两种,区别在于是否需要先setContentView(R.layout.activity_main)
假设布局文件为:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/RelativeLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/txtTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="我是xml文件加载的布局"/>
RelativeLayout>
第一种写法:最后setContentView()
加载布局
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button btnOne = new Button(this);
btnOne.setText("我是动态添加的按钮");
RelativeLayout.LayoutParams lp2 = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp2.addRule(RelativeLayout.CENTER_IN_PARENT);
LayoutInflater inflater = LayoutInflater.from(this);
RelativeLayout rly = (RelativeLayout) inflater.inflate(
R.layout.activity_main, null)
.findViewById(R.id.RelativeLayout1);
rly.addView(btnOne,lp2);
setContentView(rly);
}
}
第二种写法:先setContentView()
加载布局
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnOne = new Button(this);
btnOne.setText("我是动态添加的按钮");
RelativeLayout.LayoutParams lp2 = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp2.addRule(RelativeLayout.CENTER_IN_PARENT);
RelativeLayout rly = (RelativeLayout) findViewById(R.id.RelativeLayout1);
rly.addView(btnOne,lp2);
}
}
二种写法分析:
代码的主要流程是:创建按钮组件,创建一个LayoutParams对象,设置Button大小,通过addRule()
方法设置Button位置
第一种方法:通过LayoutInflate的inflate()
方法加载了activity_main布局,获得了外层容器, 接着addView添加按钮进容器,最后setContentView();
第二种方法:因为我们已经通过setContetView()方法加载了布局,此时我们就可以通过 findViewById找到这个外层容器,接着addView,最后setContentView()即可!
动态加载XML布局
假定主布局文件和动态加载的布局文件如下:
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/RelativeLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/btnLoad"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="动态加载布局"/>
RelativeLayout>
inflate.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:id="@+id/ly_inflate" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我是Java代码加载的布局" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我是布局里的一个小按钮" />
LinearLayout>
在MainActivity动态加载XML布局:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获得LayoutInflater对象;
final LayoutInflater inflater = LayoutInflater.from(this);
//获得外部容器对象
final RelativeLayout rly = (RelativeLayout) findViewById(R.id.RelativeLayout1);
Button btnLoad = (Button) findViewById(R.id.btnLoad);
btnLoad.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//加载要添加的布局对象
LinearLayout ly = (LinearLayout) inflater.inflate(
R.layout.inflate, null, false).findViewById(
R.id.ly_inflate);
//设置加载布局的大小与位置
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp.addRule(RelativeLayout.CENTER_IN_PARENT);
rly.addView(ly,lp);
}
});
}
}
①获取容器对象:
final RelativeLayout rly = (RelativeLayout) findViewById(R.id.RelativeLayout1);
②获得Inflater对象,同时加载被添加的布局的xml,通过findViewById找到最外层的根节点
final LayoutInflater inflater = LayoutInflater.from(this);
LinearLayout ly = (LinearLayout) inflater.inflate(R.layout.inflate, null, false)
.findViewById(R.id.ly_inflate);
③为这个容器设置大小与位置信息:
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp.addRule(RelativeLayout.CENTER_IN_PARENT);
④添加到外层容器中:
rly.addView(ly,lp);