Android View (3) View LayoutInflater源码分析

本系列文章循序渐进的学习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

你可能感兴趣的:(Android View (3) View LayoutInflater源码分析)