LayoutInflater 是怎么把xml添加到decorview?

来源:互联网 发布:kad网络连接不上 编辑:程序博客网 时间:2024/06/06 04:45

问题

主要问题:

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