Android 源码分析 - 深入理解 setContentView 方法

1,351 阅读4分钟

版权声明:本文为博主原创文章,未经博主允许不得转载。

一般我们都是这样使用setContentView:

    @Override
    protected  onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

但setContentView是怎么把我们的布局加载显示到界面上的呢?我们进去setContentView看一下:

    public  setContentView( layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

这里的getWindow返回一个Window对象mWindow,然后调用其setContentView方法,不过Window是一个抽象类,因此这个mWindow一定是Window的某个子类对象(你可以打印它的Class对象直接看是什么类型),它在Activity的attach方法(这个方法在ActivityThread启动Activity时会被调用)中被初始化:

final  attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
        
        mWindow  PolicyManagermakeNewWindow(this);   
        

再看PolicyManager的makeNewWindow方法:

    public static Window makeNewWindow(Context context) {
        return sPolicy.makeNewWindow(context);
    }

这个sPolicy是通过反射创建的,是一个com.android.internal.policy.impl.Policy对象,它的makeNewWindow方法:

    public Window makeNewWindow(Context context) {
        return  PhoneWindow(context);
    }

到这里就可以确定Activity的getWindow()返回的其实是一个PhoneWindow对象,注意PhoneWindow所在的包为com.android.internal.policy.impl。也就是说getWindow().setContentView(layoutResID);调用的就是PhoneWindow的setContentView方法:

    @Override
    public  setContentView( layoutResID) {
         (mContentParent == ) {
            installDecor();
        }  {
            mContentParent.removeAllViews();
        }
        //把我们的布局添加到mContentParent中
        mLayoutInflater.inflate(layoutResID, mContentParent);
        //这个cb其实就是我们的Activity对象
        final Callback cb = getCallback();
         (cb !=  && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

代码逻辑并不复杂,就是把我们的布局添加到mContentParent(ViewGroup对象)中。它的赋值是在installDecor中进行的:

private  installDecor() {
         (mDecor  ) {
            mDecor  generateDecor();
            
        }
         (mContentParent  ) {
            mContentParent  generateLayout(mDecor);
            
        }
    }

先看mDecor是一个什么东西:

    protected DecorView generateDecor() {
        return  DecorView(getContext(), -);
    }

原来是一个DecorView对象,DecorView类继承自FrameLayout,它是PhoneWindow的内部类。
再来看generateLayout方法:

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

        TypedArray a = getWindowStyle();

         (false) {
            System..println("From style:");
            String s = "Attrs:";
             ( i = ; i < com.android.internal.R.styleable.Window.length; i++) {
                s = s +  + Integer.toHexString(com.android.internal.R.styleable.Window[i]) + 
                        + a.getString(i);
            }
            System..println(s);
        }

        mIsFloating = a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false);
         flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
                & (~getForcedWindowFlags());
         (mIsFloating) {
            setLayout(WRAP_CONTENT, WRAP_CONTENT);
            setFlags(, flagsToUpdate);
        }  {
            setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
        }

         (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {
            requestFeature(FEATURE_NO_TITLE);
        }   (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) {
            // Don't allow an action bar if there is no title.
            requestFeature(FEATURE_ACTION_BAR);
        }

         (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) {
            requestFeature(FEATURE_ACTION_BAR_OVERLAY);
        }

         (a.getBoolean(com.android.internal.R.styleable.Window_windowActionModeOverlay, false)) {
            requestFeature(FEATURE_ACTION_MODE_OVERLAY);
        }

         (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) {
            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
        }

         (a.getBoolean(com.android.internal.R.styleable.Window_windowTranslucentStatus,
                false)) {
            setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS
                    & (~getForcedWindowFlags()));
        }

         (a.getBoolean(com.android.internal.R.styleable.Window_windowTranslucentNavigation,
                false)) {
            setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION
                    & (~getForcedWindowFlags()));
        }

         (a.getBoolean(com.android.internal.R.styleable.Window_windowOverscan, false)) {
            setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForcedWindowFlags()));
        }

         (a.getBoolean(com.android.internal.R.styleable.Window_windowShowWallpaper, false)) {
            setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));
        }

         (a.getBoolean(com.android.internal.R.styleable.Window_windowEnableSplitTouch,
                getContext().getApplicationInfo().targetSdkVersion
                        >= android.os.Build.VERSION_CODES.HONEYCOMB)) {
            setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));
        }

        a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);
        a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);
         (a.hasValue(com.android.internal.R.styleable.Window_windowFixedWidthMajor)) {
             (mFixedWidthMajor == ) mFixedWidthMajor =  TypedValue();
            a.getValue(com.android.internal.R.styleable.Window_windowFixedWidthMajor,
                    mFixedWidthMajor);
        }
         (a.hasValue(com.android.internal.R.styleable.Window_windowFixedWidthMinor)) {
             (mFixedWidthMinor == ) mFixedWidthMinor =  TypedValue();
            a.getValue(com.android.internal.R.styleable.Window_windowFixedWidthMinor,
                    mFixedWidthMinor);
        }
         (a.hasValue(com.android.internal.R.styleable.Window_windowFixedHeightMajor)) {
             (mFixedHeightMajor == ) mFixedHeightMajor =  TypedValue();
            a.getValue(com.android.internal.R.styleable.Window_windowFixedHeightMajor,
                    mFixedHeightMajor);
        }
         (a.hasValue(com.android.internal.R.styleable.Window_windowFixedHeightMinor)) {
             (mFixedHeightMinor == ) mFixedHeightMinor =  TypedValue();
            a.getValue(com.android.internal.R.styleable.Window_windowFixedHeightMinor,
                    mFixedHeightMinor);
        }

        final Context context = getContext();
        final  targetSdk = context.getApplicationInfo().targetSdkVersion;
        final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
        final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
        final boolean targetHcNeedsOptions = context.getResources().getBoolean(
                com.android.internal.R..target_honeycomb_needs_options_menu);
        final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);

         (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {
            addFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY);
        }  {
            clearFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY);
        }

         (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion
                >= android.os.Build.VERSION_CODES.HONEYCOMB) {
             (a.getBoolean(
                    com.android.internal.R.styleable.Window_windowCloseOnTouchOutside,
                    false)) {
                setCloseOnTouchOutsideIfNotSet();
            }
        }

        WindowManager.LayoutParams params = getAttributes();

         (!hasSoftInputMode()) {
            params.softInputMode = a.getInt(
                    com.android.internal.R.styleable.Window_windowSoftInputMode,
                    params.softInputMode);
        }

         (a.getBoolean(com.android.internal.R.styleable.Window_backgroundDimEnabled,
                mIsFloating)) {
            /* All dialogs should have the window dimmed */
             ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == ) {
                params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
            }
             (!haveDimAmount()) {
                params.dimAmount = a.getFloat(
                        android.R.styleable.Window_backgroundDimAmount, f);
            }
        }

         (params.windowAnimations == ) {
            params.windowAnimations = a.getResourceId(
                    com.android.internal.R.styleable.Window_windowAnimationStyle, );
        }

        // The rest are only done if this window is not embedded; otherwise,
        // the values are inherited from our container.
         (getContainer() == ) {
             (mBackgroundDrawable == ) {
                 (mBackgroundResource == ) {
                    mBackgroundResource = a.getResourceId(
                            com.android.internal.R.styleable.Window_windowBackground, );
                }
                 (mFrameResource == ) {
                    mFrameResource = a.getResourceId(com.android.internal.R.styleable.Window_windowFrame, );
                }
                 (false) {
                    System..println("Background: "
                            + Integer.toHexString(mBackgroundResource) + " Frame: "
                            + Integer.toHexString(mFrameResource));
                }
            }
            mTextColor = a.getColor(com.android.internal.R.styleable.Window_textColor, 0xFF000000);
        }

        // Inflate the window decor.

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

        mDecor.startChanging();

        View  = mLayoutInflater.inflate(layoutResource, );
        decor.addView(,  ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));

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

         ((features & ( << FEATURE_INDETERMINATE_PROGRESS)) != ) {
            ProgressBar progress = getCircularProgressBar(false);
             (progress != ) {
                progress.setIndeterminate();
            }
        }

        // Remaining setup -- of background and title -- that only applies
        // to top-level windows.
         (getContainer() == ) {
            Drawable drawable = mBackgroundDrawable;
             (mBackgroundResource != ) {
                drawable = getContext().getResources().getDrawable(mBackgroundResource);
            }
            mDecor.setWindowBackground(drawable);
            drawable = ;
             (mFrameResource != ) {
                drawable = getContext().getResources().getDrawable(mFrameResource);
            }
            mDecor.setWindowFrame(drawable);

            // System.out.println("Text=" + Integer.toHexString(mTextColor) +
            // " Sel=" + Integer.toHexString(mTextSelectedColor) +
            // " Title=" + Integer.toHexString(mTitleColor));

             (mTitleColor == ) {
                mTitleColor = mTextColor;
            }

             (mTitle != ) {
                setTitle(mTitle);
            }
            setTitleColor(mTitleColor);
        }

        mDecor.finishChanging();

        return contentParent;
    }

这个方法主要做两件事:
1.设置当前窗口的主题样式
2.查找适当的布局作为mDecor的子View,Android中定义了许多这种布局,在~\sdk\platforms\android-19\data\res\layout目录下可以找到,比如一般使用的screen_simple.xml:

<LinearLayout xmlns:android="http://schemas.android.com///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" />
    <!-- 这个就是mContentParent,我们的布局添加做为它的子View -->
    <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>

因此平时我们调用setContentView设置显示我们的布局,实际的布局会是这样子:

这里写图片描述

实际的xml布局文件(采用screen_simple.xml为例):

<com.android.internal.policy.impl.PhoneWindow$DecorView
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- screen_simple.xml -->
    <LinearLayout
        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" />
        <!-- mContentParent实际就是这个FrameLayout -->
        <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">
             <!-- 我们的activity_main.xml -->
             <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">
                <TextView 
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="hello world"/>    
            </LinearLayout>          
        </FrameLayout>
    </LinearLayout> 
</com.android.internal.policy.impl.PhoneWindow>

setContentView时序图:

这里写图片描述

转载请注明出处:blog.csdn.net/u012619640/…[2]