关于View的创建以及ViewGroup的遍历

2020-04-24  本文已影响0人  remax1

关于view的加载

请移步我的上篇设置LayoutParams失效

关于view的创建

  // Temp is the root view that was found in the xml
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);

上篇博文提到了在解析xml时,会调用inflate方法,其中这个temp就是我们要创建的view对象。来看看这两个个参数分别代表什么意思。

final AttributeSet attrs = Xml.asAttributeSet(parser);
 final String name = parser.getName();

不难看出,都时xml解析根视图得来的属性。接着去看看createViewFromTag()方法干了啥事。

View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
        if (name.equals("view")) {
            name = attrs.getAttributeValue(null, "class");
        }
···
        try {
            View view = tryCreateView(parent, name, context, attrs);
          //一般情况下,view的创建走的是这一段代码
          //自定义控件需要全类名
            if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    if (-1 == name.indexOf('.')) {//系统控件
                        view = onCreateView(context, parent, name, attrs);
                    } else {
                        //自定义控件
                        view = createView(context, name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }

            return view;
        }  catch (Exception e) {
            ····
    }

接下来去看看onCreateView()做了啥事,最终还是回调了createView()方法

public final View createView(@NonNull Context viewContext, @NonNull String name,
            @Nullable String prefix, @Nullable AttributeSet attrs)
            throws ClassNotFoundException, InflateException {
        Objects.requireNonNull(viewContext);
        Objects.requireNonNull(name);
       //从map中读取构造函数
        Constructor<? extends View> constructor = sConstructorMap.get(name);
        if (constructor != null && !verifyClassLoader(constructor)) {
            constructor = null;
            sConstructorMap.remove(name);
        }
        Class<? extends View> clazz = null;

        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);
            //map中没有,就通过完整包名历用反射来加载
            if (constructor == null) {
                // Class not found in the cache, see if it's real, and try to add it
                clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
                        mContext.getClassLoader()).asSubclass(View.class);

                if (mFilter != null && clazz != null) {
                    boolean allowed = mFilter.onLoadClass(clazz);
                    if (!allowed) {
                        failNotAllowed(name, prefix, viewContext, attrs);
                    }
                }
                constructor = clazz.getConstructor(mConstructorSignature);
                constructor.setAccessible(true);
                //缓存
                sConstructorMap.put(name, constructor);
            } else {
                ···
            }

            Object lastContext = mConstructorArgs[0];
            mConstructorArgs[0] = viewContext;
            Object[] args = mConstructorArgs;
            args[1] = attrs;

            try {
              //view对象此时已经被创建了
                final View view = constructor.newInstance(args);
                if (view instanceof ViewStub) {
                    // Use the same context when inflating ViewStub later.
                    final ViewStub viewStub = (ViewStub) view;
                    viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
                }
                return view;
            } finally {
                mConstructorArgs[0] = lastContext;
            }
        } catch (NoSuchMethodException e) {
          ···

        } catch (ClassCastException e) {
          ···
        } catch (ClassNotFoundException e) {
           ···
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

整个流程是如果是第一次加载,就把加载的view对象放进sConstructorMap中。这样就可以知道view是通过反射来加载到内存中。当然这里是只加载了根视图,其它的子view在何时加载。也是在xml解析时有个

 //加载子控件
                   rInflate(parser, root, inflaterContext, attrs, false);

去看看里面到底干了啥。

void rInflate(XmlPullParser parser, View parent, Context context,
            AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
      //获取深度
        final int depth = parser.getDepth();
        int type;
        boolean pendingRequestFocus = false;

        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)) {
                pendingRequestFocus = true;
                consumeChildElements(parser);
            } else if (TAG_TAG.equals(name)) {
                parseViewTag(parser, parent, attrs);
            } else if (TAG_INCLUDE.equals(name)) {//include标签
                if (parser.getDepth() == 0) {
                    throw new InflateException("<include /> cannot be the root element");
                }
                parseInclude(parser, context, parent, attrs);
            } else if (TAG_MERGE.equals(name)) {//merge标签
                throw new InflateException("<merge /> must be the root element");
            } else {
              //前面分析过,创建view对象的。
                final View view = createViewFromTag(parent, name, context, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
              //递归来了。再次会调用rinflate(),深度优先
                rInflateChildren(parser, view, attrs, true);
                //解析的view添加进parent中
                viewGroup.addView(view, params);
            }
        }

        if (pendingRequestFocus) {
            parent.restoreDefaultFocus();
        }

        if (finishInflate) {
            parent.onFinishInflate();
        }
    }

总结

view对象的创建和viewGroup的遍历已经大致看完了,view对象是通过反射创建,viewgroup的遍历是按深度优先的算法遍历,遍历完成后添加进parent中。

上一篇下一篇

猜你喜欢

热点阅读