Android LayoutInflater源码解析(3)

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。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/33826b4eed08541e5783b5a5da1bc4b4.html