Android编程中的inflate方法探究
作者: 陈天超
摘要:在Android应用开发中,经常会遇到将xml布局文件转换为View对象的需求,解决方法是使用View控件中的inflate方法或使用LayoutInflater类的inflate方法进行转换,inflate方法的作用是将解析生成的XML布局文件创建生成一个View对象,将该View对象动态加载至指定的布局。该论文介绍了LayoutInflater类常见的使用场景和inflate方法的四种重载形式,绘制了inflate方法的调用链,通过具体实例详细介绍了inflate方法各个参数的作用,并验证了inflate方法四种重载形式中最常用的方式。
关键词:Android应用;布局管理器;inflate方法;重载
中图分类号:TP311.56 文献标识码:A
文章编号:1009-3044(2022)20-0048-02
1 引言
在讲授Android 移动开发基础课程过程中,经常会遇到将xml布局文件转换为View对象的需求,Android中提供的解决方案是使用View控件中的inflate方法或者使用LayoutInflater(布局管理器)的inflate方法[1-2]。由于LayoutInflater类中有四个重载的方法[3],学生在使用Recyclerview控件或者ListView控件时经常会遇到由于获取不到相应布局参数,或者由于参数设置错误导致给已经有父布局的子View对象再次添加父布局,在项目运行时就会直接崩溃。为了让学生熟悉LayoutInflater的作用,理解inflate各参数的用法,正确使用inflate方法,本文对LayoutInflater类的实例化方式及inflate方法的参数做了详细的介绍,并提供了相应的案例。
2 inflate方法介绍
2.1 LayoutInflater类的作用
在Android开发中,LayoutInflater类的实例对象不能使用new关键字生成,常使用LayoutInflater.from(this)语句获取实例化对象。LayoutInflater类的主要作用是将解析生成的XML布局文件创建生成一个View对象,将该View对象动态加载至指定的布局,常见的使用场景有以下几种方式:
1) 在Activity场景中的用法
LayoutInflater tflat2 = getLayoutInflater();
View tv2 = tflat2.inflate(R.layout.inflater2, null);
2) 在Fragment场景中的用法
View tv2 = tfat2.inflate(R.layout.item2, pt, false);
3) 在Adapter数据适配器场景中的用法
public View getView(int p1, View cv, ViewGroup pRoot) {
LayoutInflater tfat=LayoutInflater.from(pCont);
View tv = tfat.inflate(R.layout.pt, pRoot, false);
return tv; }
为了提高解析XML文件的速度,所有解析的XML文件都需要在构建阶段(Build)进行预处理,LayoutInflater不能加载没有编译的XML文件,只能加载通过XmlPullParser类解析的R文件资源,如Fragment场景中的用法,给inflate方法传递的第一个参数是R.layout.item2,LayoutInflater的作用将布局资源转化生成实际的Android的对象。
2.2 inflate方法重载与调用链
inflate方法有四个重载的方法,也就是说有四种表现形式,四种定义如下:
1) View inflate(int res2, ViewGroup pRoot)
2) View inflate(int res2, ViewGroup pRoot, boolean attRoot)
3) View inflate(XmlPullParser xmlParser, ViewGroup pRoot)
4) View inflate(XmlPullParser xmlParser, ViewGroup pRoot, boolean attRoot)
这4个重载方法的作用是将指定的布局文件按照传递的参数返回相应的View对象。在开发过程中,经常使用方法2,通过查阅源码可知,方法1、2 、3最终都调用方法4生成指定的View对象。
有时候,也会使用View.inflate()方法,View.infl查看该方法的源码可知,View.inflate()方法调用了LayoutInflater的inflate()重载方法中两个参数的方法。调用链如图1所示。
在开发过程中,最常用的方法就是inflate(int res2, ViewGroup pRoot, boolean attRoot)有三个参数的方法,而三个参数的方法中res2是必须传递的,不需要过多介绍。所以我们只需考虑pRoot是否为null,attRoot为 true或者false即可,通过程序实例来说明这两个参数的用法。
3 程序案例
新建TestInflate项目,InflaterActivity使用的布局文件是inflater.xml,示例代码:
<LinearLayout
android:id="@+id/ft2"
android:layout_width="300dp"
android:layout_height="800dp">
</LinearLayout>
inflater.xml布局文件只添加了一个空的LinearLayout控件,运行时就是一个空白界面。
再建立一个布局文件,命名为add_button.xml,示例代码:
<Button
android:layout_width="320dp"
android:layout_height="90dp"
android:text="测试按钮" >
</Button>
这个布局文件中只放置了一个Button按钮。分四种情况使用inflater方法将add_button.xml布局文件中的按钮动态添加到主布局文件(inflater.xml)的LinearLayout中。
3.1 将pRoot参数设置为null
InflaterActivity中的示例代码如下所示:
protected void onCreate(Bundle state) {
super.onCreate(state);
setContentView(R.layout.inflater);
LinearLayout pt2 = findViewById(R.id. ft2);
LayoutInflater tfat2 = LayoutInflater.from(this);
View bt2 = tfat2.inflate(R.layout. add_button, null);
pt2.addView(bt2);
}
项目在夜神模拟器中运行结果如图2所示。
由运行结果可知,如果pRoot设置为null,参数attRoot将失去作用,创建的View对象是一个独立的个体,将创建的View对象使用addView方法动态添加到相应的布局文件中。由按钮大小可知,在button_layout.xml中设置的Button的宽度和高度属性值没有起作用,在布局文件中Button的layout_width设置为320dp,layout_height设置为90dp,但这两个属性值没有起任何作用。平时设置View控件大小都会使用layout_width和layout_height,也会正常显示,用户就会默认这两个属性的用是设置View控件的大小,查阅资料可知,这两个属性是用于设置View控件在一个布局中的大小,换句话说,View必须添加至一个布局容器中[4]。也就是说将layout_width的属性值设置为match_parent,就表示该View控件的宽度将会填充满布局容器;如果将layout_width的属性值设置为wrap_content,则表示让View控件的显示宽度刚好包含其内容;如果将layout_width设置为具体的数值,则View控件的显示的宽度会变成相应的数值,这就是Android工程师将View控件的两个属性命名为layout_height和layout_width的原因。
3.2 pRoot!=null&& attRoot =true
修改InflaterActivity中的代码:
protected void onCreate(Bundle state) {
super.onCreate(state);
setContentView(R.layout.inflater);
LinearLayout pt2 = findViewById(R.id. ft2);
LayoutInflater tfat2 = LayoutInflater.from(this);
View butt2 = tfat2.inflate(R.layout. add_button,
pt2, true); }
项目在夜神模拟器中运行结果如图3所示。
从运行结果可以看出,创建的View对象的属性值(params)会依托于pRoot构建[5],此时的xml布局文件中根布局属性有效,由于创建的View对象在父布局中,所以Button的layout_width和layout_height属性值是有效的。虽然没有调用addView()方法,但已经将创建好的View对象添加到父布局root中。查阅源码可以看到,编译器使用root.addView(temp,params)将创建的View对象自动添加到指定的父布局root中,所以在编程时不能再次使用addView()方法,否则就会出现迭代异常错误,如图4所示。在ListView控件中,要使用BaseAdapter适配器添加数据,需要将getView()方法返回的View对象提供给GridView使用,inflate方法的attachRoot参数不能设置为true。
3.3 pRoot!=null&&attRoot=false
修改InflaterActivity中的代码:
protected void onCreate(Bundle state) {
super.onCreate(state);