美文网首页
setcontentview流程

setcontentview流程

作者: nich | 来源:发表于2022-07-28 09:48 被阅读0次

    1.activity的setcontent流程

     public void setContentView(@LayoutRes int layoutResID) {
            getWindow().setContentView(layoutResID);
            initWindowDecorActionBar();
        }
    

    这里面是phonewindow调用setcontentview

    @Override
        public void setContentView(int layoutResID) {
            // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
            // decor, when theme attributes and the like are crystalized. Do not check the feature
            // before this happens.
            if (mContentParent == null) {
                installDecor();//1.创建 DecorView 拿到 mContentParent
            } 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);2.将自己的xml布局加载进mContentParent里面
            }
            mContentParent.requestApplyInsets();
            final Callback cb = getCallback();
            if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
            }
            mContentParentExplicitlySet = true;
        }
    
    
    
     private void installDecor() {
            mForceDecorInstall = false;
            if (mDecor == null) {
                mDecor = generateDecor(-1);//生成Decorview
                mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
                mDecor.setIsRootNamespace(true);
                if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                    mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
                }
            } else {
                mDecor.setWindow(this);
            }
            if (mContentParent == null) {
                mContentParent = generateLayout(mDecor);返回contentParent,一些activity的系统主题在里面设置比如screen_custom_title ,R.layout.screen_simple
    
                // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
    

    2.AppCompatActivity的setcontentview流程

    getDelegate().setContentView(layoutResID);
    
    @Override
        public void setContentView(View v) {
            ensureSubDecor();
            ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
            contentParent.removeAllViews();
            contentParent.addView(v);
            mAppCompatWindowCallback.getWrapped().onContentChanged();
        }
    
    
     mSubDecor = createSubDecor();
    
    
    createSubDecor()方法里面主要看
     // Now let's make sure that the Window has installed its decor by retrieving it
            ensureWindow();//拿到phonewindow
            mWindow.getDecorView();//也就是调用installDecor
    
    
    final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
            if (windowContentView != null) {
                // There might be Views already added to the Window's content view so we need to
                // migrate them to our content view
                while (windowContentView.getChildCount() > 0) {
                    final View child = windowContentView.getChildAt(0);
                    windowContentView.removeViewAt(0);
                    contentView.addView(child);
                }
    
                // Change our content FrameLayout to use the android.R.id.content id.
                // Useful for fragments.
                windowContentView.setId(View.NO_ID);
    //修改contentview的id变成android.R.id.content            contentView.setId(android.R.id.content);
    
                // The decorContent may have a foreground drawable set (windowContentOverlay).
                // Remove this as we handle it ourselves
                if (windowContentView instanceof FrameLayout) {
                    ((FrameLayout) windowContentView).setForeground(null);
                }
            }
    
            // Now set the Window's content view with the decor
            mWindow.setContentView(subDecor);
    

    总结

    1.activity中setcontentview流程

    1.installDecor()->mDecor = generateDecor(-1)生成DecorView->mContentParent =generateLayout(mDecor);加载系统布局到Decorview,mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
    --> ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);里面的@android:id/content赋值给mContentParent

    2.mLayoutInflater.inflate(layoutResID, mContentParent);

    2.AppCompatActivity的setcontentview流程

    1.AppCompatDelegate.setContentView ->ensureSubDecor()->createSubDecor里面调用ensureWindow(); // 从Activity 那PhoneWindow,mWindow.getDecorView()里面调用installDecor()
    -> final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
    -> windowContentView.setId(View.NO_ID); // 将原始的 content id 置为 NO_ID
    -> contentView.setId(android.R.id.content); // subDecerView R.id.action_bar_activity_content -> 置为 content
    -> mWindow.setContentView(subDecor); //
    -> ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);

    接下来分析mLayoutInflater.inflate(layoutResID, mContentParent);

    public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
            synchronized (mConstructorArgs) {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
    
                final Context inflaterContext = mContext;
                final AttributeSet attrs = Xml.asAttributeSet(parser);
                Context lastContext = (Context) mConstructorArgs[0];
                mConstructorArgs[0] = inflaterContext;
                View result = root;
    
                try {
                    advanceToRootNode(parser);
                    final String name = parser.getName();
    
                    if (DEBUG) {
                        System.out.println("**************************");
                        System.out.println("Creating root view: "
                                + name);
                        System.out.println("**************************");
                    }
                   //如果标签是MERGE到这段代码里
                    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, inflaterContext, attrs, false);
                    } else {
                        // Temp is the root view that was found in the xml
                  //主要看这句话
                        final View temp = createViewFromTag(root, name, inflaterContext, 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)
                                temp.setLayoutParams(params);
                            }
                        }
    
                        if (DEBUG) {
                            System.out.println("-----> start inflating children");
                        }
    
                        // Inflate all children under temp against its context.创建子布局,然后再调用createViewFromTag
                        rInflateChildren(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) {
                    final InflateException ie = new InflateException(e.getMessage(), e);
                    ie.setStackTrace(EMPTY_STACK_TRACE);
                    throw ie;
                } catch (Exception e) {
                    final InflateException ie = new InflateException(
                            getParserStateDescription(inflaterContext, attrs)
                            + ": " + e.getMessage(), e);
                    ie.setStackTrace(EMPTY_STACK_TRACE);
                    throw ie;
                } finally {
                    // Don't retain static reference on context.
                    mConstructorArgs[0] = lastContext;
                    mConstructorArgs[1] = null;
    
                    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
                }
    
                return result;
            }
        }
    
    
    查看主要看
    
    
     if (view == null) {
                    final Object lastContext = mConstructorArgs[0];
                    mConstructorArgs[0] = context;
                    try {
                        if (-1 == name.indexOf('.')) {
                            //如果是线性相对等没有.的布局走这个
                            view = onCreateView(context, parent, name, attrs);
                        } else {
                           //比如androidx.constraintlayout.widget.ConstraintLayout
                            view = createView(context, name, null, attrs);
                        }
                    } finally {
                        mConstructorArgs[0] = lastContext;
                    }
                }
    
    点击进去最后都会调用到LayoutInflater的createView方法
    
    clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
    mContext.getClassLoader()).asSubclass(View.class);
    constructor = clazz.getConstructor(mConstructorSignature);
    final View view = constructor.newInstance(args);
    通过反射生成view
    

    丢两张布局图


    image.png image.png

    inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) 解释
    第一个是加载的资源文件
    第二个父控件view
    第三个是否attachToRoot里面带有addview功能,就是是否想让其处于某一个容器中

    如果父控件设置为null就会使加载的子控件的父控件设置的长宽失效,子控件的大小由子控件里面的子view决定

    ViewStub跟include差不多,懒加载隐藏布局,他就是一个宽高都为0的view,这里需要注意的一点是,当ViewStub被inflate到parent时,ViewStub就被remove掉了,即当前view hierarchy中不再存在ViewStub,而是使用对应的layout视图代替。

    相关文章

      网友评论

          本文标题:setcontentview流程

          本文链接:https://www.haomeiwen.com/subject/clszirtx.html