layoutinflater.inflate源码详解

2020-06-11  本文已影响0人  wcwmyzy

加载布局的两种方式
View.inflate(context,R.layout.xxx,viewGroup);
LayoutInflater.from(this).inflate(R.layout.xxx,viewGroup,true);
View.infalte内部也是调用LayoutInflater.inflate

public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
        //通过Context获取了一个 LayoutInflater对象
        LayoutInflater factory = LayoutInflater.from(context);
        //调用LayoutInflater.inflate方法
        return factory.inflate(resource, root);
    }

那么 LayoutInflater.inflate是怎么加载布局的呢
首先 我们先看一下LayoutInflater.inflate重载 主要是两个要注意的
public View inflate(XmlPullParser parser, @Nullable ViewGroup root)
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root)
其他都是后面两个参数不一样而已
我们看到一个是XmlPullParser对象 和我们的布局id两个重载方法,但是在 LayoutInflater 内部其实都是调的同一个方法
比如我们如果传的是布局id 那么LayoutInflater会给我们自动创建一个XmlPullParser对象

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        if (DEBUG) {
            Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                    + Integer.toHexString(resource) + ")");
        }
        //=========================在这里通过我们传进来的布局id(resource) 初始化了一个XmlResourceParser=========================
        final XmlResourceParser parser = res.getLayout(resource);
        try {
            //=================其他的方法最终都会调用这个方法加载布局==========================
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }

接下来我们一行一行分析源码 日志打印之类的跳过

该方法声明
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)

首先开启了线程锁 不知道的自行搜索
synchronized (mConstructorArgs)

            //获取Context 
            final Context inflaterContext = mContext;
            //获取布局的属性 比如高度 宽度 然后在createViewFromTag()方法中对View的属性初始化
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            //不知道有啥用 知道的可以告知一下
            Context lastContext = (Context) mConstructorArgs[0];
            mConstructorArgs[0] = inflaterContext;
            //声明一个 View 默认将传进来的ViewGroup赋值给 result 后面作为返回值返回
            View result = root;

主要是判断布局是不是空的 能不能解析 细节不用太关心

                // 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!");
                }

这句比较重要 获取根布局的名称 主要是后面判断根布局是不是merge

final String name = parser.getName();

判断根布局是不是merge标签 传进来的ViewGroup为空 或者attachToRoot为false的话抛异常

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(XmlPullParser parser, View parent, Context context,AttributeSet attrs, boolean finishInflate)
    //解析布局并创建View 感兴趣的可以自己看一下
    rInflate(parser, root, inflaterContext, attrs, false);
}

如果根布局不是merge

            else {
                    //创建一个临时的View
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);
                    //声明LayoutParams
                    ViewGroup.LayoutParams params = null;
                    
                    //判断ViewGroup是否等于空
                    if (root != null) {
                        //通过generateLayoutParams(attrs)初始化LayoutParams
                        //方法内部其实是通过解析自定义属性一样 解析了attrs中的高度和宽度
                        //TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
                        params = root.generateLayoutParams(attrs);
                        if (!attachToRoot) {
                            
                            //如果attachToRoot为false 就不会将创建的View(---->temp)添加到
                            //我们传进来的ViewGroup(---->root)中 只设置View 的LayoutParams
                            temp.setLayoutParams(params);
                        }
                    }

                    //初始化所有View(--->temp)下的所有子View 并将解析的子View添加到View(---->temp)中
                    rInflateChildren(parser, temp, attrs, true);
                    
                    //=====================================================================  
                    //如果传进来的ViewGroup不为空 并且 attachToRoot为true 就自动将View(--->temp)添加到
                    //我们传进来的ViewGroup(---->root)中
                    //=====================================================================
                    if (root != null && attachToRoot) {
                        root.addView(temp, params);
                    }
                    //如果ViewGroup为空 或者 attachToRoot为空 将创建的View(--->temp)赋值给 result变量
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }
//返回创建好的View对象
return result;

完毕

上一篇下一篇

猜你喜欢

热点阅读