网站/小程序/APP个性化定制开发,二开,改版等服务,加扣:8582-36016

    这个方法,其实会最终调用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的一部分

    评论 0

    暂无评论
    0
    0
    0
    立即
    投稿
    发表
    评论
    返回
    顶部