Android编程中的inflate方法探究
2022-08-29陈天超
陈天超
(兰州职业技术学院电子信息工程系,甘肃兰州730070)
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所示。
图1 inflate方法调用链
在开发过程中,最常用的方法就是inflate(int res2, View‐Group pRoot,boolean attRoot)有三个参数的方法,而三个参数的方法中res2是必须传递的,不需要过多介绍。所以我们只需考虑pRoot 是否为null,attRoot 为true 或者false 即可,通过程序实例来说明这两个参数的用法。
3 程序案例
新建TestInflate 项目,InflaterActivity 使用的布局文件是in‐flater.xml,示例代码:
android:id="@+id/ft2" android:layout_width="300dp" android:layout_height="800dp">
inflater.xml布局文件只添加了一个空的LinearLayout控件,运行时就是一个空白界面。
再建立一个布局文件,命名为add_button.xml,示例代码:
这个布局文件中只放置了一个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所示。
图2 将pRoot设置为null
由运行结果可知,如果pRoot设置为null,参数attRoot 将失去作用,创建的View对象是一个独立的个体,将创建的View对象使用addView方法动态添加到相应的布局文件中。由按钮大小可知,在button_layout.xml中设置的Button的宽度和高度属性值没有起作用,在布局文件中Button 的layout_width 设置为320dp,layout_height 设置为90dp,但这两个属性值没有起任何作用。平时设置View 控件大小都会使用layout_width 和lay‐out_height,也会正常显示,用户就会默认这两个属性的用是设置View 控件的大小,查阅资料可知,这两个属性是用于设置View控件在一个布局中的大小,换句话说,View必须添加至一个布局容器中[4]。也就是说将layout_width 的属性值设置为match_parent,就表示该View控件的宽度将会填充满布局容器;如果将layout_width 的属性值设置为wrap_content,则表示让View 控件的显示宽度刚好包含其内容;如果将layout_width 设置为具体的数值,则View 控件的显示的宽度会变成相应的数值,这就是Android 工程师将View 控件的两个属性命名为lay‐out_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所示。
图3 pRoot!=null&&attRoot=true
从运行结果可以看出,创建的View 对象的属性值(params)会依托于pRoot 构建[5],此时的xml 布局文件中根布局属性有效,由于创建的View 对象在父布局中,所以Button 的lay‐out_width和layout_height属性值是有效的。虽然没有调用add‐View()方法,但已经将创建好的View对象添加到父布局root中。查阅源码可以看到,编译器使用root.addView(temp,params)将创建的View对象自动添加到指定的父布局root中,所以在编程时不能再次使用addView()方法,否则就会出现迭代异常错误,如图4所示。在ListView 控件中,要使用BaseAdapter 适配器添加数据,需要将getView()方法返回的View对象提供给GridView使用,inflate方法的attachRoot参数不能设置为true。
图4 迭代异常错误
3.3 pRoot!=null&&attRoot=false
修改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,false);
pt2.addView(butt2); }
项目在夜神模拟器中运行结果如图4所示。
运行结果与图5 相同,创建的View 对象的属性值(params)会依托于pRoot 构建,所以此时的xml 布局文件中根布局属性有效。由于编译器没有把创建的View对象添加到指定的父布局pRoot中,在编程时需要使用addView()方法将生成的View对象添加至指定的父布局文件中。这种重载形式在编程中使用频率比较高。
图5 pRoot!=null&&attRoot=false
3.4 省略attRoot参数
在编程中,如果省略了attRoot 参数,pRoot 值不为null 时,则attRoot 参数默认为true,与第二种使用方式相同,就不再赘述。
4 结束语
在Android 编程中,LayoutInflater 类实例化对象不能使用new关键字,Android 中提供了三种实例化LayoutInflater 类对象的方式,在这三种方式中最常用的是LayoutInflater.from(this)方式。LayoutInflater类的inflate方法的基本作用是使用解析生成的XML 布局资源文件创建生成一个View 对象,程序员可以将该View 对象动态添加至布局文件pRoot 中,inflate 方法有四种重载形式,经常使用方法2 即View inflate(int res2, ViewGroup pRoot, boolean attRoot),传递的参数是pRoot!=null&&attRoot=false,返回的View 对象不会自动添加至父布局root 中,所以在ListView 控件和RecyclerView 控件的数据适配器,getView 方法在使用inflate方法时需要将第三个参数设置为false。