1、获取LayoutInflater实例方法
(1)LayoutInflater inflater=LayoutInflater.from(context);
注:方法一只是对方法二的一个封装
(2)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; }
LayoutInflater inflater=(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
2、引入布局方法
(1)
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
</pre><pre>
(2)
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)
注:该方法最终调用方法(4),源码片段如下
final XmlResourceParser parser = res.getLayout(resource); try { return inflate(parser, root, attachToRoot); } finally { parser.close(); }
public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
return inflate(parser, root, root != null);
}
</pre><pre>
(4)
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)
3、源码解析
(1)首先将传入的XmlPullParser进行解析
(2)如果节点名和TAG_MERGE相同,则不创建新的View对象
(3)如果节点名与TAG_MERGE不同则调用createViewFromTag()根据节点名创建一个新的View对象temp。之后首先判断若(root!=null),则将params的值设为root.generateLayoutParams(attrs)。之后判断attachToRoot是否为真,若为真则直接root.addView(temp,params),并且返回root;若为假则将params参数传入temp中,并且返回temp。若(root==null)则直接返回不带params参数的temp。if (TAG_MERGE.equals(name)) { if (root == null || !attachToRoot) { throw new InflateException("<merge /> can be used only with a valid " + "ViewGroup root and attachToRoot=true"); } rInflate(parser, root, inflaterContext, attrs, false); }
View result = root;
final View temp = createViewFromTag(root, name, inflaterContext, attrs); ViewGroup.LayoutParams params = null; if (root != null) { if (DEBUG) { System.out.println("Creating params from root: " + root); } // 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. (If we are, we use addView, below) temp.setLayoutParams(params); } } if (DEBUG) { System.out.println("-----> start inflating children"); } // Inflate all children under temp against its context. rInflateChildren(parser, temp, attrs, true); if (DEBUG) { System.out.println("-----> done inflating children"); } // We are supposed to attach all the views we found (int temp) // to root. Do that now. if (root != null && attachToRoot) { root.addView(temp, params); } // Decide whether to return the root that was passed in or the // top view found in xml. if (root == null || !attachToRoot) { result = temp; } } } catch (XmlPullParserException e) { InflateException ex = new InflateException(e.getMessage()); ex.initCause(e); throw ex; } catch (Exception e) { InflateException ex = new InflateException( parser.getPositionDescription() + ": " + e.getMessage()); ex.initCause(e); throw ex; } finally { // Don't retain static reference on context. mConstructorArgs[0] = lastContext; mConstructorArgs[1] = null; } Trace.traceEnd(Trace.TRACE_TAG_VIEW); return result;
(4)rInflaterChidren()循环遍历该视图下的子视图。同样通过创建新的View对象,并且直接将这些子View对象加入该视图中。
在学习郭神关于LayoutInflater解析的博客的时候受益匪浅,不过却发现一个问题。郭神在博客中举了一个例子,就是用LayoutInflater引入一个按钮布局并且调用addView()将按钮置于mainlayout中。此时修改Button的layout_width和layout_height不起作用,郭神给的解释是Button不存在于任何布局当中,所以这两个属性不能起作用。之后郭神在Button的XML文件中加了一个RelativeLayout包住了Button,之后layout_width和layout_height起作用了。将整个流程看下来,发现原因应该不是这样的。下面是原博中的代码片段:final View view = createViewFromTag(parent, name, context, attrs); final ViewGroup viewGroup = (ViewGroup) parent; final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs); rInflateChildren(parser, view, attrs, true); viewGroup.addView(view, params);
setContentView(R.layout.activity_main);
mainLayout = (LinearLayout) findViewById(R.id.main_layout);
LayoutInflater layoutInflater = LayoutInflater.from(this);
View buttonLayout = layoutInflater.inflate(R.layout.button_layout, null);
mainLayout.addView(buttonLayout);
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="300dp"
android:layout_height="80dp"
android:text="Button" >
</Button>
上面为代码片段1,此时修改大小不能生效。
</pre><pre name="code" class="java" style="background-color: inherit;"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:layout_width="300dp"
android:layout_height="80dp"
android:text="Button" >
</Button>
</RelativeLayout>
上面为代码片段2,此时修改大小生效。
我的理解:在片段1中,引入button_layout时root布局为null,因此如前文所说将直接返回一个不带params参数信息的temp视图,因此设置layout_width和layoutout_height并不会传入新建的视图中,而是在addView()时由于没有传入LayoutParams参数便由其内部的generateDefaultLayoutParams()方法生成一个默认尺寸。而在片段二中,加入一个RelativeLayout之后,首先会建立RelativeLayout的视图。然后在rInflaterChildren()中遍历RelativeLayout的子视图并创建它们,Button就是在这时创建的,而在rInflateChidren()方法中调用的是addView(view,params)将Button加入RelativeLayout所以带有XML文件中的尺寸信息,因此调整Button的大小可以生效。
参考网址:
郭神关于LayoutInflater原理解析博客地址:
http://blog.youkuaiyun.com/guolin_blog/article/details/12921889
Android-Reference-LayoutInflater:
http://developer.android.com/intl/zh-cn/reference/android/view/LayoutInflater.html