LayoutInflater 是怎么把xml添加到decorview?

问题

主要问题:

LayoutInflater 是怎么把xml添加到decorview?

衍生问题:

include 为什么不能作为xml资源布局的根节点?

merge 为什么作为xml资源布局的根节点?

先来看setContentView里的 mLayoutInflater.inflate(layoutResID, mContentParent)方法

@Override
    public void setContentView(int layoutResID) {

        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

点inflate进去是以下代码,是把我们自定义的xml和ContentParent传进去

public View inflate(int resource, ViewGroup root) {
        return inflate(resource, root, root != null);
    }

继续往里进入inflate方法,resource是我们自定义的xml,root是我们的ContentParent,第三个参数从命名上看意思是,是否添加倒root布局里,如果root传的是null则为false,反之则为true。往下看重要的方法是–>利用res.getLayout(resource)方法得到xml解析器,把xml文件解析出来,之后是inflate(parser, root, attachToRoot),

 public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        if (DEBUG) {
            Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                    + Integer.toHexString(resource) + ")");
        }

        final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }

我们进入inflate(parser, root, attachToRoot)这个方法继续探索,方法如下

public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
            *//先拿xml的各种属性 *   
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            Context lastContext = (Context)mConstructorArgs[0];
            mConstructorArgs[0] = mContext;
            **//把root赋值给result**
            View result = root;

            try {
                // Look for the root node. 先去找根节点
                int type;
                while ((type = parser.next()) != XmlPullParser.START_TAG &&
                        type != XmlPullParser.END_DOCUMENT) {
                    // Empty
                }

                。。。省略若干行

                //首先判断根节点是不是merge标签
                if (TAG_MERGE.equals(name)) {
                    if (root == null || !attachToRoot) {
                    //如果是merge标签,但是 root == null || !attachToRoot就抛出一个异常,因为如果使用merge标签,肯定有得有一个顶层的ViewGroup标签来放这个加载的layout

                        throw new InflateException("<merge /> can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }
                    如果root不为空就用以下方法把逐层解析添加到root(ViewGroup)里面,这里传的false
                    rInflate(parser, root, attrs, false, false);
                } else {
                    //当是一些常用的标签的时候,首先会利用createViewFromTag方法来生成我们自定义的根节点,比如LinearLayout标签
                    // Temp is the root view that was found in the xml

                    final View temp = createViewFromTag(root, name, attrs, false);

                    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
                    //利用父节点的root.generateLayoutParams方法生成子节点的属性(这里的父节点就是id为content的Framelayout)
                        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()方法,把父节点解析出来的属性设置到子节点上;
                            temp.setLayoutParams(params);
                        }
                    }

                    if (DEBUG) {
                        System.out.println("-----> start inflating children");
                    }
                    // Inflate all children under temp
                    //加载所有子节点 这里传的true
                    rInflate(parser, temp, attrs, true, 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 (IOException 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;
        }
    }

再看rInflat方法

void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
            boolean finishInflate, boolean inheritContext) throws XmlPullParserException,
            IOException {

        final int depth = parser.getDepth();
        int type;

        //while循环,通过遍历循环来解析child节点,当不是最后一个标签的时候会走以下方法逐层解析,并添加到parent,如果只加载一个View,拿肯定不会走这个方法了

        while (((type = parser.next()) != XmlPullParser.END_TAG ||
                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {

            if (type != XmlPullParser.START_TAG) {
                continue;
            }

            final String name = parser.getName();

            if (TAG_REQUEST_FOCUS.equals(name)) {
                parseRequestFocus(parser, parent);
            } else if (TAG_TAG.equals(name)) {
                parseViewTag(parser, parent, attrs);
            } 
            else if (TAG_INCLUDE.equals(name)) {
            //这里是判断include标签,当该布局里面没有东西的时候,会抛出一个异常 inclide标签不能作为根节点
                if (parser.getDepth() == 0) {
                    throw new InflateException("<include /> cannot be the root element");
                }
                parseInclude(parser, parent, attrs, inheritContext);
            } else if (TAG_MERGE.equals(name)) {
            //merge只能作为根节点,如果出现在子节点中会抛出异常。

                throw new InflateException("<merge /> must be the root element");
            } else {
            //一层一层遍历解析
                final View view = createViewFromTag(parent, name, attrs, inheritContext);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                rInflate(parser, view, attrs, true, true);
                viewGroup.addView(view, params);
            }
        }
        //当为merge标签时候 不走这个方法,为普通标签时会走这个方法
        //控制回调函数,具体以后在讲
        if (finishInflate) parent.onFinishInflate();
    }

小结:每一个Activity都有一个关联的Window对象,用来描述应用程序窗口。
每一个窗口内部又包含了一个DecorView对象,Decorview对象用来描述窗口的视图–xml布局,以上大概记录了xml布局的加载过程,

上述是创建DecorView的过程

附上图片更易理解
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值