加入收藏 | 设为首页 | 会员中心 | 我要投稿 应用网_丽江站长网 (http://www.0888zz.com/)- 科技、建站、数据工具、云上网络、机器学习!
当前位置: 首页 > 站长学院 > PHP教程 > 正文

Android LayoutInflater源码介绍

发布时间:2021-12-10 19:50:34 所属栏目:PHP教程 来源:互联网
导读:Android使用LayoutInflater来进行布局加载,通常获取方式有两种: 第一种: LayoutInflater layoutInflater = LayoutInflater.from(context); 第二种: LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER

Android使用LayoutInflater来进行布局加载,通常获取方式有两种:
 
第一种:
 
LayoutInflater layoutInflater = LayoutInflater.from(context);
 
第二种:
 
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 
从源码中可以看出第一种是第二种的封装简化,便于使用:
 
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;
    }
 
我们通过调用inflate方法便可以完成对布局的加载:
 
layoutInflater.inflate(resource, root, true);
 
LayoutInflater中的inflate方法有若干种重载方式,最终都调用了如下代码:
 
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            //获取xml中属性信息
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            Context lastContext = (Context)mConstructorArgs[0];
            mConstructorArgs[0] = mContext;
            View result = root;
 
            try {
                // 查找根节点.
                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标签,必须保证父节点不为null且attachToRoot为true
                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(parser, root, attrs, false);
                } else {
                    //代表布局文件中根节点的view
                    View temp;
                    if (TAG_1995.equals(name)) {
                        temp = new BlinkLayout(mContext, attrs);
                    } else {
                        //利用反射,通过root名称创建view
                        temp = createViewFromTag(root, name, 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)
                            //当不把当前view附加到父容器中,则设置获取到的布局参数
                          //否则使用下面的addView方法设置
                            temp.setLayoutParams(params);
                        }
                    }
 
                    if (DEBUG) {
                        System.out.println("-----> start inflating children");
                    }
                    // Inflate all children under temp
                  //递归调用此方法加载子布局
                    rInflate(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) {
                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;
            }
 
            return result;
        }
    }
 
这里,Android使用了PULL来解析xml布局文件,并通过反射来创建出当前view:
 
temp = createViewFromTag(root, name, attrs);
 
我们查看一下源码:
 
View createViewFromTag(View parent, String name, AttributeSet attrs) {
        if (name.equals("view")) {
            name = attrs.getAttributeValue(null, "class");
        }
 
        if (DEBUG) System.out.println("******** Creating view: " + name);
 
        try {
            View view;
            if (mFactory2 != null) view = mFactory2.onCreateView(parent, name, mContext, attrs);
            else if (mFactory != null) view = mFactory.onCreateView(name, mContext, attrs);
            else view = null;
 
            if (view == null && mPrivateFactory != null) {
                view = mPrivateFactory.onCreateView(parent, name, mContext, attrs);
            }
           
            if (view == null) {
                if (-1 == name.indexOf('.')) {
                    view = onCreateView(parent, name, attrs);
                } else {
                    view = createView(name, null, attrs);
                }
            }
 
            if (DEBUG) System.out.println("Created view is: " + view);
            return view;
 
        } catch (InflateException e) {
            throw e;
 
        } catch (ClassNotFoundException e) {
            InflateException ie = new InflateException(attrs.getPositionDescription()
                    + ": Error inflating class " + name);
            ie.initCause(e);
            throw ie;
 
        } catch (Exception e) {
            InflateException ie = new InflateException(attrs.getPositionDescription()
                    + ": Error inflating class " + name);
            ie.initCause(e);
            throw ie;
        }
    }
 
里面根据不同情况,调用了onCreateView方法,利用反射来创建view。其中可以使用指定的factory来创建view,这样的钩子设计使得inflate方法变得十分灵活。
 
然后调用rInflate(parser, temp, attrs, true)方法来递归查找temp中的子view,并添加到上层view中:
 
void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
            boolean finishInflate) throws XmlPullParserException, IOException {
 
        final int depth = parser.getDepth();
        int type;
 
        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_INCLUDE.equals(name)) {
                if (parser.getDepth() == 0) {
                    throw new InflateException("<include /> cannot be the root element");
                }
                parseInclude(parser, parent, attrs);
            } else if (TAG_MERGE.equals(name)) {
                throw new InflateException("<merge /> must be the root element");
            } else if (TAG_1995.equals(name)) {
                final View view = new BlinkLayout(mContext, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                rInflate(parser, view, attrs, true);
                viewGroup.addView(view, params);               
            } else {
                final View view = createViewFromTag(parent, name, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                rInflate(parser, view, attrs, true);
                viewGroup.addView(view, params);
            }
        }
 
        if (finishInflate) parent.onFinishInflate();
    }
 
里面也用到onCreateView方法创建子view,然后将其加入到父view中返回。
 
通过查看上面的源码,我们可以发现inflate方法中的三个参数int resource, ViewGroup root, boolean attachToRoot的作用如下:
 
resource指定了要加载的view,root作为view外面一层的父容器,attachToRoot表示是否将view加入到父容器。
 
当指定了父容器,并且attachToRoot为true,则将view加入到父容器中。
 
如果指定了父容器,却将attachToRoot设置为false,那么只是从父容器中生成了view布局的参数并设置给view
 
当未指定父容器时,直接返回view本身。
 
总结
 
通过研究LayoutInflater源码的设计,我们了解到代码的执行细节的同时,也可以发现:
 
LayoutInflater创建view对象时候使用了简单工厂模式,并通过加入钩子方法,利用抽象工厂模式让coder可以使用自定义的工厂方法来创建view。

(编辑:应用网_丽江站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读