(2)LayoutInflater.from(context)源码分析

一、概述

我们平常创建view一般是使用View view = LayoutInflate.from(context).inflate(resId, parent)来创建。通过阅读源码可以让我们了解view的创建过程。

二、源码解析

1.先来看from方法:

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;
    }

这里我们知道通过LayoutInflate其实是系统的一项服务。

2.看下inflate方法:

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");

            final Context inflaterContext = mContext;
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            Context lastContext = (Context) mConstructorArgs[0];
            mConstructorArgs[0] = inflaterContext;
            View result = root;
//解析XML的根标签。 
            try {
                // Look for the root node.
                int type;
                while ((type = parser.next()) != XmlPullParser.START_TAG &&
                        type != XmlPullParser.END_DOCUMENT) {
                    // Empty
                }

                if (type != XmlPullParser.START_TAG) {
                    throw new InflateException(parser.getPositionDescription()
                            + ": No start tag found!");
                }

                final String name = parser.getName();
                
                if (DEBUG) {
                    System.out.println("**************************");
                    System.out.println("Creating root view: "
                            + name);
                    System.out.println("**************************");
                }
//如果是merge,调用rInflate进行解析。它会把merge所有子view添加到根标签中。
                if (TAG_MERGE.equals(name)) {
                    if (root == null || !attachToRoot) {
                        throw new InflateException(" can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }

                    rInflate(parser, root, inflaterContext, attrs, false);
                } else {
//如果是普通标签,调用createViewFromTag进行解析。 
                    // Temp is the root view that was found in the xml
                    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");
                    }
//用rInflate解析temp根元素下的子view。并添加到temp中。 
                    // 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) {
                final InflateException ie = new InflateException(e.getMessage(), e);
                ie.setStackTrace(EMPTY_STACK_TRACE);
                throw ie;
            } catch (Exception e) {
                final InflateException ie = new InflateException(parser.getPositionDescription()
                        + ": " + e.getMessage(), e);
                ie.setStackTrace(EMPTY_STACK_TRACE);
                throw ie;
            } finally {
                // Don't retain static reference on context.
                mConstructorArgs[0] = lastContext;
                mConstructorArgs[1] = null;

                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
//最后返回root
            return result;
        }
    }

这里我们知道解析xml使用的是pull解析,Android其实一共有三种解析方式:dom、sax、pull解析,Android中解析xml使用pull,解析textview.fromHtml()中的html使用sax中的tagSoup。

3.其实我们主要看下createViewFromTag()这个方法就可以:

View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
//此处省略一些代码,减少眼疲劳
............
            try {
            View view;
//前方高能!高能!高能!
            if (mFactory2 != null) {
                view = mFactory2.onCreateView(parent, name, context, attrs);
            } else if (mFactory != null) {
                view = mFactory.onCreateView(name, context, attrs);
            } else {
                view = null;
            }

            if (view == null && mPrivateFactory != null) {
                view = mPrivateFactory.onCreateView(parent, name, context, attrs);
            }

            if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    if (-1 == name.indexOf('.')) {
                        view = onCreateView(parent, name, attrs);
                    } else {
                        view = createView(name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }

            return view;
        } catch (InflateException e) {
            throw e;

        } catch (ClassNotFoundException e) {
..........
        }
    }

4.我们知道了view的创建都是通过Factory来创建的。我们来看下factory是如何创建的:

        @Override
        public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
            View view = null;
            if (view == null) {
                view = createViewOrFailQuietly(name,context,attrs);
            }
            return view;
        }
  private View createViewOrFailQuietly(String name, Context context,
                                             AttributeSet attrs) {
            //1.自定义控件标签名称带点,所以创建时不需要前缀
            if (name.contains(".")) {
                createViewOrFailQuietly(name, null, context, attrs);
            }
            //2.系统视图需要加上前缀
            for (String prefix : sClassPrefix) {
                View view = createViewOrFailQuietly(name, prefix, context, attrs);
                if (view != null) {
                    return view;
                }
            }
            return null;
        }

createViewOrFailQuietly会判断是不是自定义布局,有点的是自定义布局,没有点的需要改成全路径的形式。

5.最后其实是通过inflate的createView来创建view。

 private View createViewOrFailQuietly(String name, String prefix, Context context,
                                             AttributeSet attrs) {
            try {
                //通过系统的inflater创建视图,读取系统的属性
                return inflater.createView(name, prefix, attrs);
            } catch (Exception e) {
                return null;
            }
        }

到此源码分析完毕!!!

三、总结

inflate通过pull解析的方式获取到每个view的标签,之后通过factory将标签转换成view。整个流程还是比较简单的。

四、反思

阅读源码之后我们知道:其实可以自定义factory来创建view,自定义factory中可以对view做一些额外的处理。

你可能感兴趣的:((2)LayoutInflater.from(context)源码分析)