zoukankan      html  css  js  c++  java
  • Android之View绘制流程开胃菜---setContentView(...)详细分析

    版权声明:本文出自汪磊的博客,转载请务必注明出处。

    1 为什么要分析setContentView方法

    作为安卓开发者相信大部分都有意或者无意看过如下图示:PhoneWindow,DecorView这些究竟都是些神马玩意?图示的层级关系是怎么来的?我们自己所写的布局是怎么加载到窗体上的?以及在上一篇《Android事件传递机制详解及最新源码分析——Activity篇》中提到过我们在调用setContentView设置布局的时候其实都是被放置在id为content的FrameLayout 布局中的,这里又是什么鬼?带着这些问题我们一起探讨下setContentView方法究竟做了些什么。

    2 分析setContentView方法(API23)

    我们平时调用setContentView,例如:setContentView(R.layout.xxx);点进源码都是先调用Activity中的setContentView方法,我们就从Activity中的setContentView方法开始分析。

    Activity的源码中有三个重载的setContentView方法,如下:

     1 public void setContentView(@LayoutRes int layoutResID) {
     2         getWindow().setContentView(layoutResID);
     3         initWindowDecorActionBar();
     4     }   
     5 
     6 public void setContentView(View view) {
     7         getWindow().setContentView(view);
     8         initWindowDecorActionBar();
     9     }
    10 
    11 public void setContentView(View view, ViewGroup.LayoutParams params) {
    12         getWindow().setContentView(view, params);
    13         initWindowDecorActionBar();
    14     } 

    可以看到三个方法都是又调用了getWindow().setContentView(...);在上一篇文章中分析过getWindow()返回mWindow对象,mWindow定义是Windo类型,实际初始化的时候初始化为PhoneWindow,源码如下:

    private Window mWindow;
    
    
    mWindow = new PhoneWindow(this);

    这里说明一下:Window 是抽象类,主要提供一些绘制窗口的一些公用方法,PhoneWindow是Window的具体继承实现类。

    我们看看Window类中setContentView方法,源码如下:

    public abstract void setContentView(@LayoutRes int layoutResID);
    
    public abstract void setContentView(View view);
    
    public abstract void setContentView(View view, ViewGroup.LayoutParams params);

    看到了吧,这里只是三个抽象方法而已,具体逻辑需要子类自己去实现。

    接下来,我们就就去PhoneWindow中找一下吧,源码如下:

        @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();
            }
        }
    
        @Override
        public void setContentView(View view) {
            setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
    
        @Override
        public void setContentView(View view, ViewGroup.LayoutParams params) {
            // 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)) {
                view.setLayoutParams(params);
                final Scene newScene = new Scene(mContentParent, view);
                transitionTo(newScene);
            } else {
                mContentParent.addView(view, params);
            }
            mContentParent.requestApplyInsets();
            final Callback cb = getCallback();
            if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
            }
        }

     看到了吧,在子类PhoneWindow中有具体实现,并且setContentView(View view)实际上也是调用的setContentView(View view, ViewGroup.LayoutParams params),只不过params参数默认传入为MATCH_PARENT。并且setContentView(int layoutResID)与setContentView(View view, ViewGroup.LayoutParams params)方法代码逻辑是一样的,这里我们选取setContentView(int layoutResID)方法加以分析即可。

    到这里我们明白平时调用的setContentView(R.layout.xxx)方法实际上调用的是PhoneWindow中的setContentView(int layoutResID)方法,接下来我们着重分析此方法。

    3 分析PhoneWindow中的setContentView(int layoutResID)方法(API23)

    源码如下:

     1  @Override
     2     public void setContentView(int layoutResID) {
     3         // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
     4         // decor, when theme attributes and the like are crystalized. Do not check the feature
     5         // before this happens.
     6         if (mContentParent == null) {
     7             installDecor();
     8         } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
     9             mContentParent.removeAllViews();
    10         }
    11 
    12         if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
    13             final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
    14                     getContext());
    15             transitionTo(newScene);
    16         } else {
    17             mLayoutInflater.inflate(layoutResID, mContentParent);
    18         }
    19         mContentParent.requestApplyInsets();
    20         final Callback cb = getCallback();
    21         if (cb != null && !isDestroyed()) {
    22             cb.onContentChanged();
    23         }
    24     }

     第6行代码判断mContentParent 是否为空,mContentParent PhoneWindow中定义的一个ViewGroup类型实例。第一次运行的时候mContentParent 为null,则进入判断执行第7行代码

    installDecor(),我们看看installDecor()方法都做了什么源码如下:这里只列出主要代码

     1 private void installDecor() {
     2         if (mDecor == null) {
     3             mDecor = generateDecor();
     4             ...
     5         }
     6         if (mContentParent == null) {
     7             mContentParent = generateLayout(mDecor);
     8             ...
     9         }
    10         ....
    11 }        

    第2行代码判断mDecor是否为null,为null则执行generateDecor()代码并对mDecor赋值,mDecor是DecorView的一个实例,DecorView是PhoneWindow的内部类,定义如下:

    private final class DecorView extends FrameLayout implements RootViewSurfaceTaker

     看到了吧,DecorView其实就是FrameLayout 的子类,对FrameLayout 进行装饰,增强其某些功能。

    我们继续看generateDecor()源码:

    1    protected DecorView generateDecor() {
    2         return new DecorView(getContext(), -1);
    3     }

    很简单吧就是生成DecorView对象并且返回,这里没什么要多说的。

    返回installDecor()方法我们继续向下分析。

    第6行代码又是判断mContentParent 是否为null,是则执行generateLayout(mDecor)方法并将返回值赋值给mContentParent

    那我们就继续看generateLayout(mDecor)源码:

      1 protected ViewGroup generateLayout(DecorView decor) {
      2         // Apply data from current theme.
      3 
      4         TypedArray a = getWindowStyle();
      5 
      6         if (false) {
      7             System.out.println("From style:");
      8             String s = "Attrs:";
      9             for (int i = 0; i < R.styleable.Window.length; i++) {
     10                 s = s + " " + Integer.toHexString(R.styleable.Window[i]) + "="
     11                         + a.getString(i);
     12             }
     13             System.out.println(s);
     14         }
     15 
     16         mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
     17         int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
     18                 & (~getForcedWindowFlags());
     19         if (mIsFloating) {
     20             setLayout(WRAP_CONTENT, WRAP_CONTENT);
     21             setFlags(0, flagsToUpdate);
     22         } else {
     23             setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
     24         }
     25 
     26         if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
     27             requestFeature(FEATURE_NO_TITLE);
     28         } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
     29             // Don't allow an action bar if there is no title.
     30             requestFeature(FEATURE_ACTION_BAR);
     31         }
     32 
     33         if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {
     34             requestFeature(FEATURE_ACTION_BAR_OVERLAY);
     35         }
     36 
     37         if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) {
     38             requestFeature(FEATURE_ACTION_MODE_OVERLAY);
     39         }
     40 
     41         if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) {
     42             requestFeature(FEATURE_SWIPE_TO_DISMISS);
     43         }
     44 
     45         if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
     46             setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
     47         }
     48 
     49         if (a.getBoolean(R.styleable.Window_windowTranslucentStatus,
     50                 false)) {
     51             setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS
     52                     & (~getForcedWindowFlags()));
     53         }
     54 
     55         if (a.getBoolean(R.styleable.Window_windowTranslucentNavigation,
     56                 false)) {
     57             setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION
     58                     & (~getForcedWindowFlags()));
     59         }
     60 
     61         if (a.getBoolean(R.styleable.Window_windowOverscan, false)) {
     62             setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForcedWindowFlags()));
     63         }
     64 
     65         if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) {
     66             setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));
     67         }
     68 
     69         if (a.getBoolean(R.styleable.Window_windowEnableSplitTouch,
     70                 getContext().getApplicationInfo().targetSdkVersion
     71                         >= android.os.Build.VERSION_CODES.HONEYCOMB)) {
     72             setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));
     73         }
     74 
     75         a.getValue(R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);
     76         a.getValue(R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);
     77         if (a.hasValue(R.styleable.Window_windowFixedWidthMajor)) {
     78             if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();
     79             a.getValue(R.styleable.Window_windowFixedWidthMajor,
     80                     mFixedWidthMajor);
     81         }
     82         if (a.hasValue(R.styleable.Window_windowFixedWidthMinor)) {
     83             if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();
     84             a.getValue(R.styleable.Window_windowFixedWidthMinor,
     85                     mFixedWidthMinor);
     86         }
     87         if (a.hasValue(R.styleable.Window_windowFixedHeightMajor)) {
     88             if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();
     89             a.getValue(R.styleable.Window_windowFixedHeightMajor,
     90                     mFixedHeightMajor);
     91         }
     92         if (a.hasValue(R.styleable.Window_windowFixedHeightMinor)) {
     93             if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();
     94             a.getValue(R.styleable.Window_windowFixedHeightMinor,
     95                     mFixedHeightMinor);
     96         }
     97         if (a.getBoolean(R.styleable.Window_windowContentTransitions, false)) {
     98             requestFeature(FEATURE_CONTENT_TRANSITIONS);
     99         }
    100         if (a.getBoolean(R.styleable.Window_windowActivityTransitions, false)) {
    101             requestFeature(FEATURE_ACTIVITY_TRANSITIONS);
    102         }
    103 
    104         final Context context = getContext();
    105         final int targetSdk = context.getApplicationInfo().targetSdkVersion;
    106         final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
    107         final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
    108         final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;
    109         final boolean targetHcNeedsOptions = context.getResources().getBoolean(
    110                 R.bool.target_honeycomb_needs_options_menu);
    111         final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);
    112 
    113         if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {
    114             setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE);
    115         } else {
    116             setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_FALSE);
    117         }
    118 
    119         // Non-floating windows on high end devices must put up decor beneath the system bars and
    120         // therefore must know about visibility changes of those.
    121         if (!mIsFloating && ActivityManager.isHighEndGfx()) {
    122             if (!targetPreL && a.getBoolean(
    123                     R.styleable.Window_windowDrawsSystemBarBackgrounds,
    124                     false)) {
    125                 setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
    126                         FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags());
    127             }
    128         }
    129         if (!mForcedStatusBarColor) {
    130             mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);
    131         }
    132         if (!mForcedNavigationBarColor) {
    133             mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);
    134         }
    135         if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {
    136             decor.setSystemUiVisibility(
    137                     decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
    138         }
    139 
    140         if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion
    141                 >= android.os.Build.VERSION_CODES.HONEYCOMB) {
    142             if (a.getBoolean(
    143                     R.styleable.Window_windowCloseOnTouchOutside,
    144                     false)) {
    145                 setCloseOnTouchOutsideIfNotSet(true);
    146             }
    147         }
    148 
    149         WindowManager.LayoutParams params = getAttributes();
    150 
    151         if (!hasSoftInputMode()) {
    152             params.softInputMode = a.getInt(
    153                     R.styleable.Window_windowSoftInputMode,
    154                     params.softInputMode);
    155         }
    156 
    157         if (a.getBoolean(R.styleable.Window_backgroundDimEnabled,
    158                 mIsFloating)) {
    159             /* All dialogs should have the window dimmed */
    160             if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) {
    161                 params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
    162             }
    163             if (!haveDimAmount()) {
    164                 params.dimAmount = a.getFloat(
    165                         android.R.styleable.Window_backgroundDimAmount, 0.5f);
    166             }
    167         }
    168 
    169         if (params.windowAnimations == 0) {
    170             params.windowAnimations = a.getResourceId(
    171                     R.styleable.Window_windowAnimationStyle, 0);
    172         }
    173 
    174         // The rest are only done if this window is not embedded; otherwise,
    175         // the values are inherited from our container.
    176         if (getContainer() == null) {
    177             if (mBackgroundDrawable == null) {
    178                 if (mBackgroundResource == 0) {
    179                     mBackgroundResource = a.getResourceId(
    180                             R.styleable.Window_windowBackground, 0);
    181                 }
    182                 if (mFrameResource == 0) {
    183                     mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0);
    184                 }
    185                 mBackgroundFallbackResource = a.getResourceId(
    186                         R.styleable.Window_windowBackgroundFallback, 0);
    187                 if (false) {
    188                     System.out.println("Background: "
    189                             + Integer.toHexString(mBackgroundResource) + " Frame: "
    190                             + Integer.toHexString(mFrameResource));
    191                 }
    192             }
    193             mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);
    194             mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);
    195             mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT);
    196         }
    197 
    198         // Inflate the window decor.
    199 
    200         int layoutResource;
    201         int features = getLocalFeatures();
    202         // System.out.println("Features: 0x" + Integer.toHexString(features));
    203         if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
    204             layoutResource = R.layout.screen_swipe_dismiss;
    205         } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
    206             if (mIsFloating) {
    207                 TypedValue res = new TypedValue();
    208                 getContext().getTheme().resolveAttribute(
    209                         R.attr.dialogTitleIconsDecorLayout, res, true);
    210                 layoutResource = res.resourceId;
    211             } else {
    212                 layoutResource = R.layout.screen_title_icons;
    213             }
    214             // XXX Remove this once action bar supports these features.
    215             removeFeature(FEATURE_ACTION_BAR);
    216             // System.out.println("Title Icons!");
    217         } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
    218                 && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
    219             // Special case for a window with only a progress bar (and title).
    220             // XXX Need to have a no-title version of embedded windows.
    221             layoutResource = R.layout.screen_progress;
    222             // System.out.println("Progress!");
    223         } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
    224             // Special case for a window with a custom title.
    225             // If the window is floating, we need a dialog layout
    226             if (mIsFloating) {
    227                 TypedValue res = new TypedValue();
    228                 getContext().getTheme().resolveAttribute(
    229                         R.attr.dialogCustomTitleDecorLayout, res, true);
    230                 layoutResource = res.resourceId;
    231             } else {
    232                 layoutResource = R.layout.screen_custom_title;
    233             }
    234             // XXX Remove this once action bar supports these features.
    235             removeFeature(FEATURE_ACTION_BAR);
    236         } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
    237             // If no other features and not embedded, only need a title.
    238             // If the window is floating, we need a dialog layout
    239             if (mIsFloating) {
    240                 TypedValue res = new TypedValue();
    241                 getContext().getTheme().resolveAttribute(
    242                         R.attr.dialogTitleDecorLayout, res, true);
    243                 layoutResource = res.resourceId;
    244             } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
    245                 layoutResource = a.getResourceId(
    246                         R.styleable.Window_windowActionBarFullscreenDecorLayout,
    247                         R.layout.screen_action_bar);
    248             } else {
    249                 layoutResource = R.layout.screen_title;
    250             }
    251             // System.out.println("Title!");
    252         } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
    253             layoutResource = R.layout.screen_simple_overlay_action_mode;
    254         } else {
    255             // Embedded, so no decoration is needed.
    256             layoutResource = R.layout.screen_simple;
    257             // System.out.println("Simple!");
    258         }
    259 
    260         mDecor.startChanging();
    261 
    262         View in = mLayoutInflater.inflate(layoutResource, null);
    263         decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    264         mContentRoot = (ViewGroup) in;
    265 
    266         ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    267         if (contentParent == null) {
    268             throw new RuntimeException("Window couldn't find content container view");
    269         }
    270 
    271         if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
    272             ProgressBar progress = getCircularProgressBar(false);
    273             if (progress != null) {
    274                 progress.setIndeterminate(true);
    275             }
    276         }
    277 
    278         if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
    279             registerSwipeCallbacks();
    280         }
    281 
    282         // Remaining setup -- of background and title -- that only applies
    283         // to top-level windows.
    284         if (getContainer() == null) {
    285             final Drawable background;
    286             if (mBackgroundResource != 0) {
    287                 background = getContext().getDrawable(mBackgroundResource);
    288             } else {
    289                 background = mBackgroundDrawable;
    290             }
    291             mDecor.setWindowBackground(background);
    292 
    293             final Drawable frame;
    294             if (mFrameResource != 0) {
    295                 frame = getContext().getDrawable(mFrameResource);
    296             } else {
    297                 frame = null;
    298             }
    299             mDecor.setWindowFrame(frame);
    300 
    301             mDecor.setElevation(mElevation);
    302             mDecor.setClipToOutline(mClipToOutline);
    303 
    304             if (mTitle != null) {
    305                 setTitle(mTitle);
    306             }
    307 
    308             if (mTitleColor == 0) {
    309                 mTitleColor = mTextColor;
    310             }
    311             setTitleColor(mTitleColor);
    312         }
    313 
    314         mDecor.finishChanging();
    315 
    316         return contentParent;
    317     }

     我勒个去,这方法太挺长,不过别担心,总体逻辑不复杂。

     第4行代码getWindowStyle()是什么鬼呢?这里就直接说了,我们在manifest文件配置的Activity的时候有时会指定theme,如:android:theme="@style/AppTheme",getWindowStyle()就是获取我们配置的theme信息。

    接着6-199行代码都是根据我们通过getWindowStyle()获取的theme配置信息进行相应设置。

    200行代码,定义layoutResource变量。

    201调用getLocalFeatures()方法又是干什么呢?我们有时会通过代码对Activity设置一些Feature,如:requestWindowFeature(Window.FEATURE_NO_TITLE);这里getLocalFeatures()方法就是获取通过requestWindowFeature设置的一些值。

    202-258根据获取的features不同对layoutResource进行不同的赋值,layoutResource主要纪录不同的布局文件。如果什么也没设置,也就是说Activity没有任何修饰,那么就赋值为

    R.layout.screen_simple,我们看一下R.layout.screen_simple布局源码:

     1 <?xml version="1.0" encoding="utf-8"?>
     2 
     3 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     4     android:layout_width="match_parent"
     5     android:layout_height="match_parent"
     6     android:fitsSystemWindows="true"
     7     android:orientation="vertical">
     8     <ViewStub android:id="@+id/action_mode_bar_stub"
     9               android:inflatedId="@+id/action_mode_bar"
    10               android:layout="@layout/action_mode_bar"
    11               android:layout_width="match_parent"
    12               android:layout_height="wrap_content"
    13               android:theme="?attr/actionBarTheme" />
    14     <FrameLayout
    15          android:id="@android:id/content"
    16          android:layout_width="match_parent"
    17          android:layout_height="match_parent"
    18          android:foregroundInsidePadding="false"
    19          android:foregroundGravity="fill_horizontal|top"
    20          android:foreground="?android:attr/windowContentOverlay" />
    21 </LinearLayout>

     看到了吧,很简单,就包括一个actiob_Bar,还有一个id为content的FrameLayout,并且action_Bar部分使用了布局优化ViewStub 。

    继续向下分析262行将layoutResource记录的布局转化为View。

    263行代码将262行生成的view添加到decor中,这个decor就是我们上面分析过的mDecor。

    264行将262行生成的View赋值给mContentRoot,用以纪录。

    接下来266行通过findViewById找到ID为ID_ANDROID_CONTENT的View,这个ID_ANDROID_CONTENT又是什么鬼?通过查找最终在父类Window中找到,源码如下:

        public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;

     看到了吧,就是id为content的View,以R.layout.screen_simple布局为例,最终找的就是id为content的FrameLayout。赋值给名为contentParent的ViewGroup。

    最终在316行将contentParent作为generateLayout方法的返回值返回。到此generateLayout想要探讨的就都探讨完了。

    我们马上回看上面分析的installDecor()方法第7行。将generateLayout方法返回值赋值给mContentParent,到这里,你应该知道mContentParent就是DecorView中布局为content的部分。

    我们在回看一开始分析的setContentView方法,之前分析到第7行,继续向下看直到第17行,调用mLayoutInflater.inflate(layoutResID, mContentParent),至于inflate方法内部逻辑这里就不分析了,不是本文重点,直接说结论:mLayoutInflater.inflate(layoutResID, mContentParent)就是将layoutResID布局转化为View添加到mContentParent中。还记得mContentParent吗?它就是DecorView中id为content的View。到这里就知道了原来我们自己定义的布局最终都是加载到这里了。

    4总结

    经过上面分析相信你已经有了一些眉目,我们赶紧总结一下。

    我们平时在Activity中调用的setContentView方法其实都是调用的PhoneWindow中的setContentView方法,其首先会判断mContentParent是否为null,如果为null,则执行installDecor()方法,在installDecor()方法中会对mDecor进行判断是否为null,为null则进行初始化,mDecor为DecorView类型,DecorView继承自FrameLayout。接下来继续判断mContentParent是否为null,为null则执行generateLayout方法,在generateLayout方法中最重要的逻辑就是根据我们设置的不同feature找到对应布局文件,并且inflate为View,通过addView方法加入到mDecor中,然后找到布局文件中ID为content的View作为generateLayout方法最终返回值返回。接下来回到installDecor方法将generateLayout返回值赋值给mContentParent,最后回到setContentView,将我们自己的布局文件layoutResID加载到mContentParent中。

    相信经过上述分析你应该对本文一开始的那张图会有更深刻的认识。

  • 相关阅读:
    批量插入SQL(适用于数据量十万级以上的快速大量插入)
    python爬虫BUG(爬取航班信息)
    python爬虫实例(爬取航班信息)
    python入门基础
    C#生成Guid的几种方式
    微信小程序echart图
    替换SQL字段中的换行符,回车符
    ASP.NET目录
    增删改查程序
    C++中explicit关键字的使用
  • 原文地址:https://www.cnblogs.com/leipDao/p/7509222.html
Copyright © 2011-2022 走看看