Activity的setContentView渲染的原理

通过源码得知Activity、PhoneWindow和Window之间的关系(API25)


PhoneWindow是唯一实现Window的具体实现类,在PhoneWindow中有一个DecorView对象

DecorView对象是所有应用窗口(即Activity界面)的根View,DecorView是FrameLayout的子类,对FrameLayout进行装饰或渲染,是所有应用窗口的根View


setContentView

Activity中setContentView直接调用getWindow().setContentView(),PhoneWindow又是Window的实现类,所以就来具体看PhoneWindow的setContentView方法

setContentView(int layoutResID)源码

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) { //mContentParent是放置窗口内容(Activity界面)的视图,如果mContentParent为空
            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;
    }

上面知道,如果mContentParent为空时,就调用installDecor来生成mContentParent的View(即是Activity界面的根视图)

加载完布局视图后,最后onContentChanged方法,发现在PhoneWindow并没重写Window的这个方法,onContentChange方法在Window中是空方法

public void onContentChanged();
当Activity的布局改动时,即setContentView()或者addContentView()方法执行完毕时就会调用该方法。


private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {   //mDecor是个顶层视图
            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) {
            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 = (TextView) 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);
                    }
                }
            }

           .......
          
            }
        }
    }

上面知道,如果mContentParent 为空,通过generateLayout(mDecor)进行赋值,参数mDecor是Activity顶层布局(也可以理解为一个ViewGroup)视图

protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.

        TypedArray a = getWindowStyle();   //mCurrentParent(ViewGroup)装饰的风格

        .....
        

        WindowManager.LayoutParams params = getAttributes();   //设置布局相关属性

        // Non-floating windows on high end devices must put up decor beneath the system bars and
        // therefore must know about visibility changes of those.  设置相关设备非浮动窗口的显示情况
        if (!mIsFloating && ActivityManager.isHighEndGfx()) {
            if (!targetPreL && a.getBoolean(
                    R.styleable.Window_windowDrawsSystemBarBackgrounds,
                    false)) {
                setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
                        FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags());
            }
            if (mDecor.mForceWindowDrawsStatusBarBackground) {
                params.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
            }
        }
       .....

        

        // Inflate the window decor.

        int layoutResource;   //布局xml资源
        int features = getLocalFeatures(); //得到此窗口正常工作的特性。给出了requestfeature()集,被处理的只有这个窗口本身,而不它的容器
        ......
        mDecor.startChanging(); //标记顶层DecorView开始改变
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);  //得到上面设置的xml Layout资源,然后添加到DecorView根视图上

        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }

       	....... //DecorView相关属性设置
        mDecor.finishChanging();

        return contentParent;
    }

上面主要根据窗口风格类型选择不窗口的根布局文件,mDecor做为根视图将该窗口根布局添加进去,然后获取id为ID_ANDROID_CONTENT,然后返回给mContentParent对象,从上面就知道installDecor方法实质就是产生mDecor和mContentParent对象


在Activity中设置标题状态(feature)或者相关主题(theme),有两种配置方式

1、通过xml形式android:theme属性,通过这里的getWindowStyle()获取的

2、通过代码形式requestWindowFeature(),在这里通过getLocalFeature()获取的

设置Activity的属性时必须在setContentView方法之前调用requestFeature()方法的原因


setContentView方法工作流程:

Activity的setContentView主要是把Activity布局文件(xml或代码形式)添加到顶层视图上

1、先创建一个DecorView对象mDecor作为所有应用窗口(即Activity界面)的根视图

2、根据不同Feature等风格选择不同窗口修饰布局文件

3、将Activity布局文件添加到mContentParent上

public void setContentView(int layoutResID) {
        ......
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
       ......
    }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值