本系列文章循序渐进的学习Android View的使用和核心源码分析。
Android View (1) View的树形结构和坐标计算
Android View (2) View的加载过程
Android View (3) View LayoutInflater 源码分析
Android View (4) View的绘制过程
LayoutInflater 布局加载
android LayoutInflater 的最常见的使用是在代码中动态的加载布局,比如ListView、RecyclerView等,在我上一篇介绍Activity View加载过程中,也是通过LayoutInflater来加载XML布局的,今天就从源码的角度,分析他的加载过程。
LayoutInflater初始化
LayoutInflater的初始化有两种方式
1.LayoutInflater layoutInflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
通过Context来获取
2.LayoutInflater layoutInflater = LayoutInflater.from(context);
通过自身的from方法获取,这种方式只是对上面的代码进行了封装而已,from源码如下:
225 * Obtains the LayoutInflater from the given context.
226 */
227 public static LayoutInflater from(Context context) {
228 LayoutInflater LayoutInflater =
229 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
230 if (LayoutInflater == null) {
231 throw new AssertionError("LayoutInflater not found.");
232 }
233 return LayoutInflater;
234 }
LayoutInflater的使用
LayoutInflater加载布局的方法是layotInflater.inflate(),该方法有四个重载方式:
inflate(@LayoutRes int resource, @Nullable ViewGroup root)
inflate(XmlPullParser parser, @Nullable ViewGroup root)
inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)
inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)
最终都是走的最后一个方法,所以我们只需要分析最后一个方法的源码:
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
452 synchronized (mConstructorArgs) {
453 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
454
455 final Context inflaterContext = mContext;
456 final AttributeSet attrs = Xml.asAttributeSet(parser);
457 Context lastContext = (Context) mConstructorArgs[0];
458 mConstructorArgs[0] = inflaterContext;
//传入父View
459 View result = root;
460
461 try {
462 // Look for the root node.
463 int type;
//确定根节点
464 while ((type = parser.next()) != XmlPullParser.START_TAG &&
465 type != XmlPullParser.END_DOCUMENT) {
466 // Empty
467 }
468 //验证XML是否正确
469 if (type != XmlPullParser.START_TAG) {
470 throw new InflateException(parser.getPositionDescription()
471 + ": No start tag found!");
472 }
473 //获取布局的名称
474 final String name = parser.getName();
475
476 if (DEBUG) {
477 System.out.println("**************************");
478 System.out.println("Creating root view: "
479 + name);
480 System.out.println("**************************");
481 }
482
483 if (TAG_MERGE.equals(name)) {
//处理merge相关的操作
484 if (root == null || !attachToRoot) {
485 throw new InflateException(" can be used only with a valid "
486 + "ViewGroup root and attachToRoot=true");
487 }
488 //递归inflate方法
489 rInflate(parser, root, inflaterContext, attrs, false);
490 } else {
//通过createViewFromTag传入节点和参数名创建view实例(过程是在createViewFromTag()方法的内部又会去调用createView()方法,然后使用反射的方式创建出View的实例并返回)
491 // Temp is the root view that was found in the xml
492 final View temp = createViewFromTag(root, name, inflaterContext, attrs);
493
494 ViewGroup.LayoutParams params = null;
495
496 if (root != null) {
497 if (DEBUG) {
498 System.out.println("Creating params from root: " +
499 root);
500 }
501 // Create layout params that match root, if supplied
//根据root创建LayoutParams
502 params = root.generateLayoutParams(attrs);
503 if (!attachToRoot) {
504 // Set the layout params for temp if we are not
505 // attaching. (If we are, we use addView, below)
506 temp.setLayoutParams(params);
507 }
508 }
509
510 if (DEBUG) {
511 System.out.println("-----> start inflating children");
512 }
513
514 // Inflate all children under temp against its context.
//递归inflate剩下的chrildren,最终调用rInflate下面会分析
515 rInflateChildren(parser, temp, attrs, true);
516
517 if (DEBUG) {
518 System.out.println("-----> done inflating children");
519 }
520
521 // We are supposed to attach all the views we found (int temp)
522 // to root. Do that now.
//root非空且attachToRoot=true则将xml文件加载到root
523 if (root != null && attachToRoot) {
524 root.addView(temp, params);
525 }
526
527 // Decide whether to return the root that was passed in or the
528 // top view found in xml.
//将布局文件最外层的所有layout属性进行设置,当该view被添加到父view当中时,这些layout属性会自动生效
529 if (root == null || !attachToRoot) {
530 result = temp;
531 }
532 }
533
534 } catch (XmlPullParserException e) {
535 final InflateException ie = new InflateException(e.getMessage(), e);
536 ie.setStackTrace(EMPTY_STACK_TRACE);
537 throw ie;
538 } catch (Exception e) {
539 final InflateException ie = new InflateException(parser.getPositionDescription()
540 + ": " + e.getMessage(), e);
541 ie.setStackTrace(EMPTY_STACK_TRACE);
542 throw ie;
543 } finally {
544 // Don't retain static reference on context.
545 mConstructorArgs[0] = lastContext;
546 mConstructorArgs[1] = null;
547
548 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
549 }
550 //返回参数root或xml文件里的root
551 return result;
552 }
553 }
接下来分析rInflate 递归inflate的代码:
/**
828 * Recursive method used to descend down the xml hierarchy and instantiate
829 * views, instantiate their children, and then call onFinishInflate().
830 *
831 * Note: Default visibility so the BridgeInflater can
832 * override it.
833 */
834 void rInflate(XmlPullParser parser, View parent, Context context,
835 AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
836
837 final int depth = parser.getDepth();
838 int type;
839 boolean pendingRequestFocus = false;
840 //android 自带Pull解析XML模式开启
841 while (((type = parser.next()) != XmlPullParser.END_TAG ||
842 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
843 //找到start_tag节点
844 if (type != XmlPullParser.START_TAG) {
845 continue;
846 }
847 //获取Name标记
848 final String name = parser.getName();
849 //处理requestFocus
850 if (TAG_REQUEST_FOCUS.equals(name)) {
851 pendingRequestFocus = true;
852 consumeChildElements(parser);
//处理tag
853 } else if (TAG_TAG.equals(name)) {
854 parseViewTag(parser, parent, attrs);
//处理include
855 } else if (TAG_INCLUDE.equals(name)) {
856 if (parser.getDepth() == 0) {
857 throw new InflateException(" cannot be the root element");
858 }
859 parseInclude(parser, context, parent, attrs);
//处理merge merge需要是xml中的根节点
860 } else if (TAG_MERGE.equals(name)) {
861 throw new InflateException(" must be the root element");
862 } else {
863 final View view = createViewFromTag(parent, name, context, attrs);
864 final ViewGroup viewGroup = (ViewGroup) parent;
865 final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
866 rInflateChildren(parser, view, attrs, true);
867 viewGroup.addView(view, params);
868 }
869 }
870
871 if (pendingRequestFocus) {
872 parent.restoreDefaultFocus();
873 }
874 //处理完成,回调onFinishInflate
875 if (finishInflate) {
876 parent.onFinishInflate();
877 }
878 }
分析完成后,LayoutInflater其实就是使用Android提供的pull解析方式来解析布局文件的,通过递归的方式一层层的解析。
参考
1.郭霖:http://blog.csdn.net/guolin_blog/article/details/12921889
2.:http://www.jianshu.com/p/a66da642b3e2