本网站(662p.com)打包出售,且带程序代码数据,662p.com域名,程序内核采用TP框架开发,需要联系扣扣:2360248666 /wx:lianweikj
精品域名一口价出售:1y1m.com(350元) ,6b7b.com(400元) , 5k5j.com(380元) , yayj.com(1800元), jiongzhun.com(1000元) , niuzen.com(2800元) , zennei.com(5000元)
需要联系扣扣:2360248666 /wx:lianweikj
Activity的OnCreate方法中的setContentView方法具体做了哪些事?
mylove136 · 595浏览 · 发布于2019-09-19 +关注

这个方法,其实会最终调用PhoneWindow的setContentView方法,下面看看PhoneWindow的源码:(PhoneWindow.java)


@Override
    public void setContentView(int layoutResID) {
//当mContentParent为null时,则执行installDecor()
        if (mContentParent == null) {
            installDecor();  //   1
        } 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
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }



从这个方法可以看出,第一次在Activity的onCreate方法中执行setContentView时,mContentParent是为null的,这是会执行installDecor()方法,下面我们看看,installDecor方法的源码:(PhoneWindow.java)

private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            mDecor = generateDecor(-1);// 3
            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); // 4
        // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
            mDecor.makeOptionalFitsSystemWindows();
    //省略部分代码
    // ...
        }
    }
    protected DecorView generateDecor(int featureId) {
        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()); // 5
    }


从这个方法可以看出,这个方法中,通过generateDecor(-1)方法创建一个mDecor对象,generateDecor方法中,是通过new DecorView(context, featureId, this, getAttributes())的方式来创建DecorView对象的。这里注意一下第三个参数this,这个this,就是代表了当前PhoneWindow,所以,DecorView对象内部是持有PhoneWindow的引用的。看完generateDecor(-1)方法后,我们继续看注释4处,由于第一次加载布局,所以,

mContentParent肯定是为null的,这样就会执行generateLayout(mDecor)方法,下面跟进到这个方法内部,看看具体实现:(PhoneWindow.java)

protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.
        TypedArray a = getWindowStyle();
//  省略部分代码,这些代码都是根据window的样式来进行一些设置
// ... 
//下面这部分代码是加载window的decor
        // Inflate the window decor.
        int layoutResource;
        int features = getLocalFeatures();
//下面代码是根据features,来确定相应的布局文件被添加到DecorView中
        // 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);
        // 6 
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); 
        // 7
        if (contentParent == null) {
       throw new RuntimeException("Window couldn't find content container view");
        }
 
        mDecor.finishChanging();
        return contentParent;
    }


generateLayout这个方法内部,是根据widow的一些样式来进行一些设置,根据features来确定添加到DecorView的布局。确定完要被添加的布局文件的id后, 就通过DecorView的onResourcesLoaded方法来将布局文件的view添加到DecorView中。下面来看看DecorView的onResourcesLoaded(LayoutInflater inflater, int layoutResource)

方法的源码:(DecorView.java)

void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
        if (mBackdropFrameRenderer != null) {
            loadBackgroundDrawablesIfNeeded();
            mBackdropFrameRenderer.onResourcesLoaded(
            this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
            mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
            getCurrentColor(mNavigationColorViewState));
        }
        mDecorCaptionView = createDecorCaptionView(inflater);
        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();
    }


这个方法内部会先创建一个mDecorCaptionView,在将传入布局文件加载成view,

接着判断,如果mDecorCaptionView不为null,并且mDecorCaptionView的父控件也不为null,则将mDecorCaptionView添加到其父控件中,并且,将传入的布局文件的view添加到

mDecorCaptionView中,如果mDecorCaptionView为null,则直接将传入的布局文件的view,直接添加到DecorView中。说的这里,大家可能会奇怪,DecorView的addView方法是怎么添加view的,其实大家也不必奇怪,因为DecorView其实是继承FrameLayout的,它也是一个ViewGroup,所以,它的添加view的方法其实就是使用的ViewGroup的添加view的方法。下面是DecorView的类的继承关系:

public class DecorView extends FrameLayout implements RootViewSurfaceTaker,WindowCallbacks{
//省略代码
}


完成了将传入的布局文件的view添加到DecorView中后,将这个布局文件的viwe赋值给了mContentRoot变量。回到PhoneWindow类的generateLayout方法中,看看注释7的位置,将布局文件的view添加到DecorView中以后,通过findViewById的方法将这个ViewGroup赋值给了contentParent,ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT)

最后,返回这个contentParent。到这里,PhoneWindow的installDecor()方法就分析完了,

下面接着看PhoneWindow的setContentView方法的注释2处,mLayoutInflater.inflate(layoutResID, mContentParent); 这里将我们在Activity中的布局文件的id加载到了mContentParent中。这样就完成了将我们自己在Activity的setContetView

方法中设置的布局文件添加到了mContentParent这个ViewGroup中。由于mContentParent是DecorView的子控件,所以,相当于是添加到DecorView中了。


前面分析PhoneWindow的generateLayout方法的注释7处,可能有些人会有疑问,怎么DecorView将一个布局文件作为自己的子view后,怎么就可以通过findViewById(ID_ANDROID_CONTENT)就能找到contentParent呢?这个findViewById方法中传入的id为什么就是ID_ANDROID_CONTENT这个常量呢?我们回到上面的PhoneWindow的ViewGroup generateLayout(DecorView decor) 这个方法内部看看,layoutResource这个变量被赋值的情况,可以看到这个layoutResource可能有如下几种赋值:

R.layout.screen_swipe_dismiss


R.layout.screen_title_icons;


R.layout.screen_progress;


R.layout.screen_custom_title


R.layout.screen_action_bar


R.layout.screen_title


R.layout.screen_simple_overlay_action_mode


R.layout.screen_simple;

这些布局文件位于 frameworks/base/core/res/res/layout/ (参照9.0源码查看)

下面介绍一下常见的系统提供的布局文件


R.layout.screen_title
<!--  
This is an optimized layout for a screen, with the minimum set of features  
enabled.  
-->  
  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:orientation="vertical"  
    android:fitsSystemWindows="true">  
    <FrameLayout  
        android:layout_width="match_parent"   
        android:layout_height="?android:attr/windowTitleSize"  
        style="?android:attr/windowTitleBackgroundStyle">  
        <TextView android:id="@android:id/title"   
            style="?android:attr/windowTitleStyle"  
            android:background="@null"  
            android:fadingEdge="horizontal"  
            android:gravity="center_vertical"  
            android:layout_width="match_parent"  
            android:layout_height="match_parent" />  
    </FrameLayout>  
    <FrameLayout android:id="@android:id/content"  
        android:layout_width="match_parent"   
        android:layout_height="0dip"  
        android:layout_weight="1"  
        android:foregroundGravity="fill_horizontal|top"  
        android:foreground="?android:attr/windowContentOverlay" />  
</LinearLayout>


R.layout.screen_simple(全屏窗口布局文件)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>


其它布局文件,大家可以看看源码,这些布局文件中,都有一个id为 @android:id/content 的FrameLayout,我们在PhoneWindow的generateLayout的注释7处的ID_ANDROID_CONTENT其实就是指的 @android:id/content这个id,所以,能够通过ID_ANDROID_CONTENT来查找其代表的控件FrameLayout。到这里,也可以明白,为什么这个ID_ANDROID_CONTENT的控件取名叫mContentParent了,因为它就是我们传入的布局的父控件,我们传入的

布局都是被添加到它里面的。


到这里,其实只是完成了Decorview的初始化,并将我们自己的布局添加到了Decorview的mContentParent中,但是DecorView还未显示,也不能接收外界的输入,在完成ActivityThread的handleResumeActivity方法的调用后,回调了Activity的onReusme方法后,在接着调用Activity的makeVisible()方法才将DecorView添加到WindowManager,并将DecorView显示。


通过上面整个流程的分析,我们可以总结一下几点:

1.要去除标题栏是,要在setContentView方法前调用requestWindowFeature(Window.FEATURE_NO_TITLE);这是因为,我们DecorView添加布局,是根据feautres来决定添加的布局文件的id的。只有先设置了feature,DecorView才知道去添加哪个布局作为自己的子view。


2.DecorView中,只有一个直接的子view,这个子view要么是DecorCaptionView要么是根据feature拿到的layoutResource,比如R.layout.screen_title 或 R.layout.screen_simple等,上面列出的几种布局中的一种。


系统的状态栏和底部导航栏是不包含在DecorView中,他们是SystemUI的一部分

相关推荐

android下vulkan与opengles纹理互通

talkchan · 1179浏览 · 2020-11-23 10:37:39
Android 使用RecyclerView实现轮播图

奔跑的男人 · 2175浏览 · 2019-05-09 17:11:13
微软发布新命令行工具 Windows Terminal

吴振华 · 869浏览 · 2019-05-09 17:15:04
Facebook 停止屏蔽部分区块链广告

· 754浏览 · 2019-05-09 17:20:08
加载中

0评论

评论
分类专栏
小鸟云服务器
扫码进入手机网页