美文网首页Android技术知识
从源码角度分析Activity#setContentView

从源码角度分析Activity#setContentView

作者: Joker_Wan | 来源:发表于2019-12-09 12:53 被阅读0次

    从源码角度分析Activity#setContentView

    我们平时在开发的过程中将一个layout绘制出来是通过Activity#setContentView

    先来看看源码执行的流程图


    image.png

    有了上面的流程图我们对就setContentView源码调用有了个大概的框架,再来分析源码就不会小鹿乱撞了,哈哈,现在从setContentView入手来看整个绘制流程源码

    public class MainActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    }
    

    我们点进去setContentView看看源码

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

    这里的getWindow()返回的是Window的唯一实现类PhoneWindow,继续跟进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();
            } 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);
            }
            mContentParent.requestApplyInsets();
            final Callback cb = getCallback();
            if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
            }
            mContentParentExplicitlySet = true;
        }
    

    这里面先调用installDecor(),然后判断有无转场动画,有转场动画执行专场动画,最终都通过LayoutInflater解析传进来的layoutResID到mContentParent,继续跟进installDecor()

    private void installDecor() {
            mForceDecorInstall = false;
            if (mDecor == null) {
                // 注释1
                mDecor = generateDecor(-1);
                mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
                mDecor.setIsRootNamespace(true);
                if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                    mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
                }
            } else {
                mDecor.setWindow(this);
            }
            if (mContentParent == null) {
                // 注释1
                mContentParent = generateLayout(mDecor);
    
                // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
                mDecor.makeOptionalFitsSystemWindows();
    
                final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
                        R.id.decor_content_parent);
    
                if (decorContentParent != null) {
                    mDecorContentParent = decorContentParent;
                    mDecorContentParent.setWindowCallback(getCallback());
                    if (mDecorContentParent.getTitle() == null) {
                        mDecorContentParent.setWindowTitle(mTitle);
                    }
    
                    final int localFeatures = getLocalFeatures();
                    for (int i = 0; i < FEATURE_MAX; i++) {
                        if ((localFeatures & (1 << i)) != 0) {
                            mDecorContentParent.initFeature(i);
                        }
                    }
    
                    mDecorContentParent.setUiOptions(mUiOptions);
    
                    if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 ||
                            (mIconRes != 0 && !mDecorContentParent.hasIcon())) {
                        mDecorContentParent.setIcon(mIconRes);
                    } else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 &&
                            mIconRes == 0 && !mDecorContentParent.hasIcon()) {
                        mDecorContentParent.setIcon(
                                getContext().getPackageManager().getDefaultActivityIcon());
                        mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;
                    }
                    if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 ||
                            (mLogoRes != 0 && !mDecorContentParent.hasLogo())) {
                        mDecorContentParent.setLogo(mLogoRes);
                    }
    
                    // Invalidate if the panel menu hasn't been created before this.
                    // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
                    // being called in the middle of onCreate or similar.
                    // A pending invalidation will typically be resolved before the posted message
                    // would run normally in order to satisfy instance state restoration.
                    PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
                    if (!isDestroyed() && (st == null || st.menu == null) && !mIsStartingWindow) {
                        invalidatePanelMenu(FEATURE_ACTION_BAR);
                    }
                } else {
                    mTitleView = findViewById(R.id.title);
                    if (mTitleView != null) {
                        if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
                            final View titleContainer = findViewById(R.id.title_container);
                            if (titleContainer != null) {
                                titleContainer.setVisibility(View.GONE);
                            } else {
                                mTitleView.setVisibility(View.GONE);
                            }
                            mContentParent.setForeground(null);
                        } else {
                            mTitleView.setText(mTitle);
                        }
                    }
                }
    
                if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
                    mDecor.setBackgroundFallback(mBackgroundFallbackResource);
                }
    
                // Only inflate or create a new TransitionManager if the caller hasn't
                // already set a custom one.
                if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) {
                    if (mTransitionManager == null) {
                        final int transitionRes = getWindowStyle().getResourceId(
                                R.styleable.Window_windowContentTransitionManager,
                                0);
                        if (transitionRes != 0) {
                            final TransitionInflater inflater = TransitionInflater.from(getContext());
                            mTransitionManager = inflater.inflateTransitionManager(transitionRes,
                                    mContentParent);
                        } else {
                            mTransitionManager = new TransitionManager();
                        }
                    }
    
                    mEnterTransition = getTransition(mEnterTransition, null,
                            R.styleable.Window_windowEnterTransition);
                    mReturnTransition = getTransition(mReturnTransition, USE_DEFAULT_TRANSITION,
                            R.styleable.Window_windowReturnTransition);
                    mExitTransition = getTransition(mExitTransition, null,
                            R.styleable.Window_windowExitTransition);
                    mReenterTransition = getTransition(mReenterTransition, USE_DEFAULT_TRANSITION,
                            R.styleable.Window_windowReenterTransition);
                    mSharedElementEnterTransition = getTransition(mSharedElementEnterTransition, null,
                            R.styleable.Window_windowSharedElementEnterTransition);
                    mSharedElementReturnTransition = getTransition(mSharedElementReturnTransition,
                            USE_DEFAULT_TRANSITION,
                            R.styleable.Window_windowSharedElementReturnTransition);
                    mSharedElementExitTransition = getTransition(mSharedElementExitTransition, null,
                            R.styleable.Window_windowSharedElementExitTransition);
                    mSharedElementReenterTransition = getTransition(mSharedElementReenterTransition,
                            USE_DEFAULT_TRANSITION,
                            R.styleable.Window_windowSharedElementReenterTransition);
                    if (mAllowEnterTransitionOverlap == null) {
                        mAllowEnterTransitionOverlap = getWindowStyle().getBoolean(
                                R.styleable.Window_windowAllowEnterTransitionOverlap, true);
                    }
                    if (mAllowReturnTransitionOverlap == null) {
                        mAllowReturnTransitionOverlap = getWindowStyle().getBoolean(
                                R.styleable.Window_windowAllowReturnTransitionOverlap, true);
                    }
                    if (mBackgroundFadeDurationMillis < 0) {
                        mBackgroundFadeDurationMillis = getWindowStyle().getInteger(
                                R.styleable.Window_windowTransitionBackgroundFadeDuration,
                                DEFAULT_BACKGROUND_FADE_DURATION_MS);
                    }
                    if (mSharedElementsUseOverlay == null) {
                        mSharedElementsUseOverlay = getWindowStyle().getBoolean(
                                R.styleable.Window_windowSharedElementsUseOverlay, true);
                    }
                }
            }
        }
    

    我们先来看看注释1的地方,mDecor就是DecorView,是顶层布局容器并继承FrameLayout,通过generateDecor(-1)来生成,继续跟进generateDecor(-1)

    protected DecorView generateDecor(int featureId) {
            // System process doesn't have application context and in that case we need to directly use
            // the context we have. Otherwise we want the application context, so we don't cling to the
            // activity.
            Context context;
            if (mUseDecorContext) {
                Context applicationContext = getContext().getApplicationContext();
                if (applicationContext == null) {
                    context = getContext();
                } else {
                    context = new DecorContext(applicationContext, getContext());
                    if (mTheme != -1) {
                        context.setTheme(mTheme);
                    }
                }
            } else {
                context = getContext();
            }
            return new DecorView(context, featureId, this, getAttributes());
        }
    

    可以看到在这里创建了上下文Context对象,最终通过DecorView的构造函数创建DecorView对象并返回。
    再回到installDecor()方法中,看到注释2的位置,如果mContentParent为null,则调用generateLayout(mDecor)并将返回值赋值给mContentParent,跟进generateLayout(mDecor),由于代码太多,我们只截取关键代码讲解

    protected ViewGroup generateLayout(DecorView decor) {
    
            ...
    
            // Inflate the window decor.
    
            int layoutResource;
            int features = getLocalFeatures();
            // System.out.println("Features: 0x" + Integer.toHexString(features));
            if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
                layoutResource = R.layout.screen_swipe_dismiss;
                setCloseOnSwipeEnabled(true);
            } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
                if (mIsFloating) {
                    TypedValue res = new TypedValue();
                    getContext().getTheme().resolveAttribute(
                            R.attr.dialogTitleIconsDecorLayout, res, true);
                    layoutResource = res.resourceId;
                } else {
                    layoutResource = R.layout.screen_title_icons;
                }
                // XXX Remove this once action bar supports these features.
                removeFeature(FEATURE_ACTION_BAR);
                // System.out.println("Title Icons!");
            } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
                    && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
                // Special case for a window with only a progress bar (and title).
                // XXX Need to have a no-title version of embedded windows.
                layoutResource = R.layout.screen_progress;
                // System.out.println("Progress!");
            } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
                // Special case for a window with a custom title.
                // If the window is floating, we need a dialog layout
                if (mIsFloating) {
                    TypedValue res = new TypedValue();
                    getContext().getTheme().resolveAttribute(
                            R.attr.dialogCustomTitleDecorLayout, res, true);
                    layoutResource = res.resourceId;
                } else {
                    layoutResource = R.layout.screen_custom_title;
                }
                // XXX Remove this once action bar supports these features.
                removeFeature(FEATURE_ACTION_BAR);
            } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
                // If no other features and not embedded, only need a title.
                // If the window is floating, we need a dialog layout
                if (mIsFloating) {
                    TypedValue res = new TypedValue();
                    getContext().getTheme().resolveAttribute(
                            R.attr.dialogTitleDecorLayout, res, true);
                    layoutResource = res.resourceId;
                } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
                    layoutResource = a.getResourceId(
                            R.styleable.Window_windowActionBarFullscreenDecorLayout,
                            R.layout.screen_action_bar);
                } else {
                    layoutResource = R.layout.screen_title;
                }
                // System.out.println("Title!");
            } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
                layoutResource = R.layout.screen_simple_overlay_action_mode;
            } else {
                // Embedded, so no decoration is needed.
                layoutResource = R.layout.screen_simple;
                // System.out.println("Simple!");
            }
            
            mDecor.startChanging();
            mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
            
            ...
    }
    

    这段代码根据应用不同的主题将对应的布局文件id赋值给layoutResource,并将mLayoutInflater和layoutResource传入DecorView#onResourcesLoaded,跟进onResourcesLoaded()

    void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
            if (mBackdropFrameRenderer != null) {
                loadBackgroundDrawablesIfNeeded();
                mBackdropFrameRenderer.onResourcesLoaded(
                        this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
                        mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
                        getCurrentColor(mNavigationColorViewState));
            }
    
            mDecorCaptionView = createDecorCaptionView(inflater);
            // 注释3
            final View root = inflater.inflate(layoutResource, null);
            if (mDecorCaptionView != null) {
                if (mDecorCaptionView.getParent() == null) {
                    addView(mDecorCaptionView,
                            new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
                }
                mDecorCaptionView.addView(root,
                        new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
            } else {
                // Put it below the color views.
                addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
            }
            mContentRoot = (ViewGroup) root;
            initializeElevation();
        }
    

    可以看到注释3,通过解析layoutResource创建一个root对象,最后通过addView()将root对象添加到DecorView中,了解onResourcesLoaded()做的事情后,继续回到PhoneWindow#generateLayout,截取其中部分代码

    protected ViewGroup generateLayout(DecorView decor) {
    
            ...
    
            mDecor.startChanging();
            mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
            //  注释4
            ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
            if (contentParent == null) {
                throw new RuntimeException("Window couldn't find content container view");
            }
            
            ...
            
            return contentParent;
    }
    

    可以看到注释4出的代码,通过findViewById得到contentParent,ID_ANDROID_CONTENT常量值为com.android.internal.R.id.content,这个id是主容器的id并且是一定存在的,最后返回contentParent,好,总结下generateLayout方法做的事情:根据不同的主题获取了不同的layoutResource,通过onResourcesLoaded方法将layoutResource解析,并将解析得到的View添加到DecorView,最后通过View的id com.android.internal.R.id.content得到contentParent并将其返回

    回到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();
            } 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);
            }
            mContentParent.requestApplyInsets();
            final Callback cb = getCallback();
            if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
            }
            mContentParentExplicitlySet = true;
        }
    

    通过上面的源码分析我们可以发现,installDecor()里面调用generateLayout()给mContentParent赋值,即mContentParent就是id为com.android.internal.R.id.content容器,最后将我们传入setContentView()中的layoutResID渲染出来放到mContentParent容器中

    视图结构图如下:

    image.png

    screen_simple.xml代码如下:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    25    android:layout_width="match_parent"
    26    android:layout_height="match_parent"
    27    android:fitsSystemWindows="true"
    28    android:orientation="vertical">
    29    <ViewStub android:id="@+id/action_mode_bar_stub"
    30              android:inflatedId="@+id/action_mode_bar"
    31              android:layout="@layout/action_mode_bar"
    32              android:layout_width="match_parent"
    33              android:layout_height="wrap_content"
    34              android:theme="?attr/actionBarTheme" />
    35    <FrameLayout
    36         android:id="@android:id/content"
    37         android:layout_width="match_parent"
    38         android:layout_height="match_parent"
    39         android:foregroundInsidePadding="false"
    40         android:foregroundGravity="fill_horizontal|top"
    41         android:foreground="?android:attr/windowContentOverlay" />
    42</LinearLayout>
    

    相关文章

      网友评论

        本文标题:从源码角度分析Activity#setContentView

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