一、View
View组件是一个空白的矩形区域,并负责这块区域的界面绘制和事件处理。
Android中的所有UI组件类都继承了View类,扩展出具有各种形状和功能的UI组件。
View还有一个重要的子类:ViewGroup,通常作为其他组件的容器来使用,这些组件可以是View组件也可以是ViewGroup组件,当然,ViewGroup也可以作为一个View来使用。
二、LayoutInflater
当我们想要向一个ViewGroup里动态地添加View或者ViewGroup时,要使用LayoutInflater。
首先要获取LayoutInflater的实例,有两种方式:
LayoutInflater layoutInflater = LayoutInflater.from(context);
LayoutInflater layoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
其实第一种就是第二种封装后的简单写法。
然后,我们用这个实例调用inflater()方法去加载布局:
layoutInflater.inflate(int resource,ViewGroup root)
第一个参数是要加载布局的id,第二个参数指给该布局的外部再嵌套一层父布局。
从源码的角度来说,在这个Inflater()方法的内部,采用pull解析的方式去解析布局文件。调用createViewFromTag()这个方法返回传入的resource对应加载布局的最外层布局的View对象,然后在rInflater()这个方法遍历这个最外层布局下的子元素,同样通过createViewFromTag()这个方法创建这些子元素的View对象,并通过addView()添加到最外层布局中,最终返回这个resouse对应加载布局的View对象。如果这个root不为空,那么还会将这个这个加载布局的View对象add到root布局中再返回。
inflater()方法还有接收三个参数的方法重载
inflate(int resource, ViewGroup root, boolean attachToRoot)
如果root为空,attachToRoot将失去作用,设置任何值都没有意义。
如果root不为空,attachToRoot为true,跟上面接收两个参数的inflate()方法中root参数不为空的情况一致。
如果root不为空,attachToRoot为false,不会为加载布局外部嵌套一层父布局,但是会对加载布局的最外层的layout属性预设置,当这个加载布局的View对象被add到另一个View时,这些layout属性会立即生效。这是什么意思呢?直接看源码:
View temp = createViewFromTag(name, attrs);
if (root != null) {
//Create layout params that match root,if supplied
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
//Set the layout params for temp if we are not attaching
temp.setLayoutParams(params);
}
}
先来简单了解一下LayoutParams,我们都知道LayoutParams are used by views to tell their parents how they want to be laid out,即LayoutParams是由ParentView创建出来给ChildView用的,ChildView设置了LayoutParams后在ParentView addView的时候把这个LayoutParams传回给ParentVIew告诉它自己想要怎么设置。
通过上述代码,我们可以很清楚地看到,attrs里的layout属性通过root转化为LayoutParams对象,然后temp对它进行了提前的设置。看到这里,有的朋友可能会有一个疑问,这里LayoutParams是root这个布局的类型对应的,当我要把这个inflate方法返回的View对象add到另一个不同类型的布局的时候,比如,root是RelativeLayout类型,而调用addVIew()方法的是一个LinearLayout类型,这样不会报错吗?其实,在addView()这个方法内部有一个checkLayoutParam()方法会检查被添加的View的LayoutParam是否是调用addView()的View的布局类型对应的LayoutParams,如果不是,则会转化为后者对应的LayoutParams 。
我们都知道,View组件都必须存在一个布局中才可以设置一些layout类的属性,比如layout_height,layout_width等,如果没有存在于一个布局中,那么这些属性都将失效。在这里总结一下inflate()方法怎么用layout属性才会生效。
这里分两种情况,第一种情况,View组件在一个布局中,那么无论用哪一种inflate()方法,这个View组件的layout属性都可以生效。
第二种情况,View组件没有在一个布局中,那么root不为空的时候,无论用哪一种inflate()方法,addView()后这个View组件的layout属性都可以生效。