• setContentView源码解析


    一、引言

            本文将解析activity加载布局文件的setContentView源码,由于会涉及到部分activity的启动流程,这里附上一张activity启动流程图:   

            关于 setContentView 源码分两种情况,因为我们的 activity 有两个:

    一种是继承自 Activity(来自sdk: android.app);另一种是继承自AppCompatActivity (来自support库:如androidx.appcompat.app),而这种的本质也是在 Activity 基础上的扩展,所以先看 Activity。这里的activity指的是根activity,而不是我们通常使用的应用activity。

    二. Activity 的 setContentView

    1.整体流程        

    在图中我们看到第8步:ActivityThread 调用 handleLaunchActivity() 方法,执行 Activity 的oncreate() 方法,而我们的 xml布局文件就是在oncreate()中通过:setContentView(R.layout.activity_main); 来加载的。下面就去看一下其内部如何实现的。

    (看源码的时候,我们习惯倒着或向上或向内部一步步去追踪代码,这里我们反着以正向来分析)

            首先看 ActivityThread 的 handleLaunchActivity()  方法:

    1. /**
    2. * Extended implementation of activity launch. Used when server requests a launch or relaunch.
    3. */
    4. @Override
    5. public Activity handleLaunchActivity(ActivityClientRecord r,
    6. PendingTransactionActions pendingActions, Intent customIntent) {
    7. // If we are getting ready to gc after going to the background, well
    8. // we are back active so skip it.
    9. unscheduleGcIdler();
    10. mSomeActivitiesChanged = true;
    11. if (r.profilerInfo != null) {
    12. mProfiler.setProfiler(r.profilerInfo);
    13. mProfiler.startProfiling();
    14. }
    15. // Make sure we are running with the most recent config.
    16. handleConfigurationChanged(null, null);
    17. if (localLOGV) Slog.v(
    18. TAG, "Handling launch of " + r);
    19. // Initialize before creating the activity
    20. if (!ThreadedRenderer.sRendererDisabled) {
    21. GraphicsEnvironment.earlyInitEGL();
    22. }
    23. WindowManagerGlobal.initialize();
    24. final Activity a = performLaunchActivity(r, customIntent);
    25. if (a != null) {
    26. r.createdConfig = new Configuration(mConfiguration);
    27. reportSizeConfigurations(r);
    28. if (!r.activity.mFinished && pendingActions != null) {
    29. pendingActions.setOldState(r.state);
    30. pendingActions.setRestoreInstanceState(true);
    31. pendingActions.setCallOnPostCreate(true);
    32. }
    33. } else {
    34. // If there was an error, for any reason, tell the activity manager to stop us.
    35. try {
    36. ActivityManager.getService()
    37. .finishActivity(r.token, Activity.RESULT_CANCELED, null,
    38. Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
    39. } catch (RemoteException ex) {
    40. throw ex.rethrowFromSystemServer();
    41. }
    42. }
    43. return a;
    44. }

            代码简单来说,就是通过  performLaunchActivity()创建了一个activity,然后返回。去看看他如何创建activity:

    1. /** Core implementation of activity launch. */
    2. private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    3. ActivityInfo aInfo = r.activityInfo;
    4. if (r.packageInfo == null) {
    5. r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
    6. Context.CONTEXT_INCLUDE_CODE);
    7. }
    8. ComponentName component = r.intent.getComponent();
    9. if (component == null) {
    10. component = r.intent.resolveActivity(
    11. mInitialApplication.getPackageManager());
    12. r.intent.setComponent(component);
    13. }
    14. if (r.activityInfo.targetActivity != null) {
    15. component = new ComponentName(r.activityInfo.packageName,
    16. r.activityInfo.targetActivity);
    17. }
    18. ContextImpl appContext = createBaseContextForActivity(r);
    19. Activity activity = null;
    20. try {
    21. java.lang.ClassLoader cl = appContext.getClassLoader();
    22. activity = mInstrumentation.newActivity(
    23. cl, component.getClassName(), r.intent);
    24. StrictMode.incrementExpectedActivityCount(activity.getClass());
    25. r.intent.setExtrasClassLoader(cl);
    26. r.intent.prepareToEnterProcess();
    27. if (r.state != null) {
    28. r.state.setClassLoader(cl);
    29. }
    30. } catch (Exception e) {
    31. if (!mInstrumentation.onException(activity, e)) {
    32. throw new RuntimeException(
    33. "Unable to instantiate activity " + component
    34. + ": " + e.toString(), e);
    35. }
    36. }
    37. try {
    38. Application app = r.packageInfo.makeApplication(false, mInstrumentation);
    39. if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
    40. if (localLOGV) Slog.v(
    41. TAG, r + ": app=" + app
    42. + ", appName=" + app.getPackageName()
    43. + ", pkg=" + r.packageInfo.getPackageName()
    44. + ", comp=" + r.intent.getComponent().toShortString()
    45. + ", dir=" + r.packageInfo.getAppDir());
    46. if (activity != null) {
    47. CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
    48. Configuration config = new Configuration(mCompatConfiguration);
    49. if (r.overrideConfig != null) {
    50. config.updateFrom(r.overrideConfig);
    51. }
    52. if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
    53. + r.activityInfo.name + " with config " + config);
    54. Window window = null;
    55. if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
    56. window = r.mPendingRemoveWindow;
    57. r.mPendingRemoveWindow = null;
    58. r.mPendingRemoveWindowManager = null;
    59. }
    60. appContext.setOuterContext(activity);
    61. activity.attach(appContext, this, getInstrumentation(), r.token,
    62. r.ident, app, r.intent, r.activityInfo, title, r.parent,
    63. r.embeddedID, r.lastNonConfigurationInstances, config,
    64. r.referrer, r.voiceInteractor, window, r.configCallback);
    65. if (customIntent != null) {
    66. activity.mIntent = customIntent;
    67. }
    68. r.lastNonConfigurationInstances = null;
    69. checkAndBlockForNetworkAccess();
    70. activity.mStartedActivity = false;
    71. int theme = r.activityInfo.getThemeResource();
    72. if (theme != 0) {
    73. activity.setTheme(theme);
    74. }
    75. activity.mCalled = false;
    76. if (r.isPersistable()) {
    77. mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
    78. } else {
    79. mInstrumentation.callActivityOnCreate(activity, r.state);
    80. }
    81. if (!activity.mCalled) {
    82. throw new SuperNotCalledException(
    83. "Activity " + r.intent.getComponent().toShortString() +
    84. " did not call through to super.onCreate()");
    85. }
    86. r.activity = activity;
    87. }
    88. r.setState(ON_CREATE);
    89. mActivities.put(r.token, r);
    90. } catch (SuperNotCalledException e) {
    91. throw e;
    92. } catch (Exception e) {
    93. if (!mInstrumentation.onException(activity, e)) {
    94. throw new RuntimeException(
    95. "Unable to start activity " + component
    96. + ": " + e.toString(), e);
    97. }
    98. }
    99. return activity;
    100. }

            总的来说就是通过反射创建了activity,然后执行了activity的attach方法(在attach方法中初始化了 PhoneWindow ),再然后回调activity的onCreate方法,在oncreate方法中再通过PhoneWindow 的 setContentView 方法加载布局。

    2. activity的attach方法

            内部初始化了 mWindow :

    1. final void attach(Context context, ActivityThread aThread,
    2. Instrumentation instr, IBinder token, int ident,
    3. Application application, Intent intent, ActivityInfo info,
    4. CharSequence title, Activity parent, String id,
    5. NonConfigurationInstances lastNonConfigurationInstances,
    6. Configuration config, String referrer, IVoiceInteractor voiceInteractor,
    7. Window window, ActivityConfigCallback activityConfigCallback) {
    8. attachBaseContext(context);
    9. mFragments.attachHost(null /*parent*/);
    10. // 初始化 mWindow
    11. mWindow = new PhoneWindow(this, window, activityConfigCallback);
    12. mWindow.setWindowControllerCallback(this);
    13. mWindow.setCallback(this);
    14. mWindow.setOnWindowDismissedCallback(this);
    15. mWindow.getLayoutInflater().setPrivateFactory(this);
    16. if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
    17. mWindow.setSoftInputMode(info.softInputMode);
    18. }
    19. if (info.uiOptions != 0) {
    20. mWindow.setUiOptions(info.uiOptions);
    21. }
    22. mUiThread = Thread.currentThread();
    23. mMainThread = aThread;
    24. mInstrumentation = instr;
    25. mToken = token;
    26. mIdent = ident;
    27. mApplication = application;
    28. mIntent = intent;
    29. mReferrer = referrer;
    30. mComponent = intent.getComponent();
    31. mActivityInfo = info;
    32. mTitle = title;
    33. mParent = parent;
    34. mEmbeddedID = id;
    35. mLastNonConfigurationInstances = lastNonConfigurationInstances;
    36. if (voiceInteractor != null) {
    37. if (lastNonConfigurationInstances != null) {
    38. mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
    39. } else {
    40. mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
    41. Looper.myLooper());
    42. }
    43. }
    44. mWindow.setWindowManager(
    45. (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
    46. mToken, mComponent.flattenToString(),
    47. (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    48. if (mParent != null) {
    49. mWindow.setContainer(mParent.getWindow());
    50. }
    51. mWindowManager = mWindow.getWindowManager();
    52. mCurrentConfig = config;
    53. mWindow.setColorMode(info.colorMode);
    54. setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled());
    55. enableAutofillCompatibilityIfNeeded();
    56. }

    performLaunchActivity()中回调activity的onCreate方法

    1. if (r.isPersistable()) {
    2. mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
    3. } else {
    4. mInstrumentation.callActivityOnCreate(activity, r.state);
    5. }

    mInstrumentation的callActivityOnCreate方法(关于mInstrumentation单独去学习):

    1. /**
    2. * Perform calling of an activity's {@link Activity#onCreate}
    3. * method. The default implementation simply calls through to that method.
    4. * @param activity The activity being created.
    5. * @param icicle The previously frozen state (or null) to pass through to
    6. * @param persistentState The previously persisted state (or null)
    7. */
    8. public void callActivityOnCreate(Activity activity, Bundle icicle,
    9. PersistableBundle persistentState) {
    10. prePerformCreate(activity);
    11. activity.performCreate(icicle, persistentState);
    12. postPerformCreate(activity);
    13. }

    callActivityOnCreate 中的 performCreate 方法:

    1. final void performCreate(Bundle icicle) {
    2. performCreate(icicle, null);
    3. }
    4. final void performCreate(Bundle icicle, PersistableBundle persistentState) {
    5. mCanEnterPictureInPicture = true;
    6. restoreHasCurrentPermissionRequest(icicle);
    7. if (persistentState != null) {
    8. onCreate(icicle, persistentState);
    9. } else {
    10. onCreate(icicle);
    11. }
    12. writeEventLog(LOG_AM_ON_CREATE_CALLED, "performCreate");
    13. mActivityTransitionState.readState(icicle);
    14. mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
    15. com.android.internal.R.styleable.Window_windowNoDisplay, false);
    16. mFragments.dispatchActivityCreated();
    17. mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
    18. }

    然后在activity的oncreate方法中去调用 setContentView方法,这是 onCreate 的注释:

    Called when the activity is starting.  This is where most initialization
    should go: calling {@link #setContentView(int)} to inflate the
    activity's UI, using {@link #findViewById} to programmatically interact
    with widgets in the UI, calling
    {@link #managedQuery(android.net.Uri , String[], String, String[], String)} to retrievecursors for data being displayed, etc.

    3. 去看 activity 的 setContentView 方法(注意我们看的是参数为int的方法):

    1. /**
    2. * Set the activity content from a layout resource. The resource will be
    3. * inflated, adding all top-level views to the activity.
    4. *
    5. * @param layoutResID Resource ID to be inflated.
    6. *
    7. * @see #setContentView(android.view.View)
    8. * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
    9. */
    10. public void setContentView(@LayoutRes int layoutResID) {
    11. getWindow().setContentView(layoutResID);
    12. initWindowDecorActionBar();
    13. }
    14. /**
    15. * Set the activity content to an explicit view. This view is placed
    16. * directly into the activity's view hierarchy. It can itself be a complex
    17. * view hierarchy. When calling this method, the layout parameters of the
    18. * specified view are ignored. Both the width and the height of the view are
    19. * set by default to {@link ViewGroup.LayoutParams#MATCH_PARENT}. To use
    20. * your own layout parameters, invoke
    21. * {@link #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}
    22. * instead.
    23. *
    24. * @param view The desired content to display.
    25. *
    26. * @see #setContentView(int)
    27. * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
    28. */
    29. public void setContentView(View view) {
    30. getWindow().setContentView(view);
    31. initWindowDecorActionBar();
    32. }
    33. /**
    34. * Set the activity content to an explicit view. This view is placed
    35. * directly into the activity's view hierarchy. It can itself be a complex
    36. * view hierarchy.
    37. *
    38. * @param view The desired content to display.
    39. * @param params Layout parameters for the view.
    40. *
    41. * @see #setContentView(android.view.View)
    42. * @see #setContentView(int)
    43. */
    44. public void setContentView(View view, ViewGroup.LayoutParams params) {
    45. getWindow().setContentView(view, params);
    46. initWindowDecorActionBar();
    47. }

            这个 getWindow() 就是在attach方法中初始化的PhoneWindow对象,那去PhoneWindow看下他的 setContentView 方法:

    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. if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
    12. final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
    13. getContext());
    14. transitionTo(newScene);
    15. } else {
    16. mLayoutInflater.inflate(layoutResID, mContentParent);
    17. }
    18. mContentParent.requestApplyInsets();
    19. final Callback cb = getCallback();
    20. if (cb != null && !isDestroyed()) {
    21. cb.onContentChanged();
    22. }
    23. mContentParentExplicitlySet = true;
    24. }

    他内部主要做了这几件事:

    1. 创建 DecorView,拿到 mContentParent: installDecor();   

    2. 将我们的界面xml渲染到mContentParent :

                    mLayoutInflater.inflate(layoutResID, mContentParent);

    3.将mContentParentExplicitlySet 置为 true(这个重要,下面会有解释)

    3.1 下面一个一个进去看看:首先是  installDecor();   

    1. private void installDecor() {
    2. mForceDecorInstall = false;
    3. if (mDecor == null) {
    4. mDecor = generateDecor(-1);
    5. mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
    6. mDecor.setIsRootNamespace(true);
    7. if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
    8. mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
    9. }
    10. } else {
    11. mDecor.setWindow(this);
    12. }
    13. if (mContentParent == null) {
    14. mContentParent = generateLayout(mDecor);
    15. // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
    16. mDecor.makeOptionalFitsSystemWindows();
    17. final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
    18. R.id.decor_content_parent);
    19. if (decorContentParent != null) {
    20. mDecorContentParent = decorContentParent;
    21. mDecorContentParent.setWindowCallback(getCallback());
    22. if (mDecorContentParent.getTitle() == null) {
    23. mDecorContentParent.setWindowTitle(mTitle);
    24. }
    25. final int localFeatures = getLocalFeatures();
    26. for (int i = 0; i < FEATURE_MAX; i++) {
    27. if ((localFeatures & (1 << i)) != 0) {
    28. mDecorContentParent.initFeature(i);
    29. }
    30. }
    31. mDecorContentParent.setUiOptions(mUiOptions);
    32. if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 ||
    33. (mIconRes != 0 && !mDecorContentParent.hasIcon())) {
    34. mDecorContentParent.setIcon(mIconRes);
    35. } else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 &&
    36. mIconRes == 0 && !mDecorContentParent.hasIcon()) {
    37. mDecorContentParent.setIcon(
    38. getContext().getPackageManager().getDefaultActivityIcon());
    39. mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;
    40. }
    41. if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 ||
    42. (mLogoRes != 0 && !mDecorContentParent.hasLogo())) {
    43. mDecorContentParent.setLogo(mLogoRes);
    44. }
    45. // Invalidate if the panel menu hasn't been created before this.
    46. // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
    47. // being called in the middle of onCreate or similar.
    48. // A pending invalidation will typically be resolved before the posted message
    49. // would run normally in order to satisfy instance state restoration.
    50. PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
    51. if (!isDestroyed() && (st == null || st.menu == null) && !mIsStartingWindow) {
    52. invalidatePanelMenu(FEATURE_ACTION_BAR);
    53. }
    54. } else {
    55. mTitleView = findViewById(R.id.title);
    56. if (mTitleView != null) {
    57. if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
    58. final View titleContainer = findViewById(R.id.title_container);
    59. if (titleContainer != null) {
    60. titleContainer.setVisibility(View.GONE);
    61. } else {
    62. mTitleView.setVisibility(View.GONE);
    63. }
    64. mContentParent.setForeground(null);
    65. } else {
    66. mTitleView.setText(mTitle);
    67. }
    68. }
    69. }
    70. if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
    71. mDecor.setBackgroundFallback(mBackgroundFallbackResource);
    72. }
    73. // Only inflate or create a new TransitionManager if the caller hasn't
    74. // already set a custom one.
    75. if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) {
    76. if (mTransitionManager == null) {
    77. final int transitionRes = getWindowStyle().getResourceId(
    78. R.styleable.Window_windowContentTransitionManager,
    79. 0);
    80. if (transitionRes != 0) {
    81. final TransitionInflater inflater = TransitionInflater.from(getContext());
    82. mTransitionManager = inflater.inflateTransitionManager(transitionRes,
    83. mContentParent);
    84. } else {
    85. mTransitionManager = new TransitionManager();
    86. }
    87. }
    88. mEnterTransition = getTransition(mEnterTransition, null,
    89. R.styleable.Window_windowEnterTransition);
    90. mReturnTransition = getTransition(mReturnTransition, USE_DEFAULT_TRANSITION,
    91. R.styleable.Window_windowReturnTransition);
    92. mExitTransition = getTransition(mExitTransition, null,
    93. R.styleable.Window_windowExitTransition);
    94. mReenterTransition = getTransition(mReenterTransition, USE_DEFAULT_TRANSITION,
    95. R.styleable.Window_windowReenterTransition);
    96. mSharedElementEnterTransition = getTransition(mSharedElementEnterTransition, null,
    97. R.styleable.Window_windowSharedElementEnterTransition);
    98. mSharedElementReturnTransition = getTransition(mSharedElementReturnTransition,
    99. USE_DEFAULT_TRANSITION,
    100. R.styleable.Window_windowSharedElementReturnTransition);
    101. mSharedElementExitTransition = getTransition(mSharedElementExitTransition, null,
    102. R.styleable.Window_windowSharedElementExitTransition);
    103. mSharedElementReenterTransition = getTransition(mSharedElementReenterTransition,
    104. USE_DEFAULT_TRANSITION,
    105. R.styleable.Window_windowSharedElementReenterTransition);
    106. if (mAllowEnterTransitionOverlap == null) {
    107. mAllowEnterTransitionOverlap = getWindowStyle().getBoolean(
    108. R.styleable.Window_windowAllowEnterTransitionOverlap, true);
    109. }
    110. if (mAllowReturnTransitionOverlap == null) {
    111. mAllowReturnTransitionOverlap = getWindowStyle().getBoolean(
    112. R.styleable.Window_windowAllowReturnTransitionOverlap, true);
    113. }
    114. if (mBackgroundFadeDurationMillis < 0) {
    115. mBackgroundFadeDurationMillis = getWindowStyle().getInteger(
    116. R.styleable.Window_windowTransitionBackgroundFadeDuration,
    117. DEFAULT_BACKGROUND_FADE_DURATION_MS);
    118. }
    119. if (mSharedElementsUseOverlay == null) {
    120. mSharedElementsUseOverlay = getWindowStyle().getBoolean(
    121. R.styleable.Window_windowSharedElementsUseOverlay, true);
    122. }
    123. }
    124. }
    125. }

            里面重点看两个方法: 

    mDecor = generateDecor(-1);  和  mContentParent = generateLayout(mDecor);

    1. protected DecorView generateDecor(int featureId) {
    2. // System process doesn't have application context and in that case we need to directly use
    3. // the context we have. Otherwise we want the application context, so we don't cling to the
    4. // activity.
    5. Context context;
    6. if (mUseDecorContext) {
    7. Context applicationContext = getContext().getApplicationContext();
    8. if (applicationContext == null) {
    9. context = getContext();
    10. } else {
    11. context = new DecorContext(applicationContext, getContext());
    12. if (mTheme != -1) {
    13. context.setTheme(mTheme);
    14. }
    15. }
    16. } else {
    17. context = getContext();
    18. }
    19. return new DecorView(context, featureId, this, getAttributes());
    20. }

    创建了DecorView,然后返回。

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

    代码长了点,实际也简单,

    1. 和我们自定义view有些相似,通过获取自定义属性来设置 mDecor 的状态。 像我们常设置的 “FEATURE_NO_TITLE” 就在这里,这也是我们要把这个属性设置在setContentView之前的原因:
                setContentView 在加载布局时会通过 requestFeature() 加载我们设置的属性。
    2. 把我们的xml布局文件添加到 decorView (decorView本质是一个FrameLayout):
                        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

    3.2  将界面的xml布局加载到mContentParent

                    mLayoutInflater.inflate(layoutResID, mContentParent);

    去看这个inflate方法:

    1. public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
    2. return inflate(resource, root, root != null);
    3. }

    继续看:

    1. public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
    2. final Resources res = getContext().getResources();
    3. if (DEBUG) {
    4. Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
    5. + Integer.toHexString(resource) + ")");
    6. }
    7. final XmlResourceParser parser = res.getLayout(resource);
    8. try {
    9. return inflate(parser, root, attachToRoot);
    10. } finally {
    11. parser.close();
    12. }
    13. }

    这里看到时xml解析器:XmlResourceParser ,再进去 inflate:

    1. public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
    2. synchronized (mConstructorArgs) {
    3. Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
    4. final Context inflaterContext = mContext;
    5. final AttributeSet attrs = Xml.asAttributeSet(parser);
    6. Context lastContext = (Context) mConstructorArgs[0];
    7. mConstructorArgs[0] = inflaterContext;
    8. View result = root;
    9. try {
    10. // Look for the root node.
    11. int type;
    12. while ((type = parser.next()) != XmlPullParser.START_TAG &&
    13. type != XmlPullParser.END_DOCUMENT) {
    14. // Empty
    15. }
    16. if (type != XmlPullParser.START_TAG) {
    17. throw new InflateException(parser.getPositionDescription()
    18. + ": No start tag found!");
    19. }
    20. final String name = parser.getName();
    21. if (DEBUG) {
    22. System.out.println("**************************");
    23. System.out.println("Creating root view: "
    24. + name);
    25. System.out.println("**************************");
    26. }
    27. if (TAG_MERGE.equals(name)) {
    28. if (root == null || !attachToRoot) {
    29. throw new InflateException(" can be used only with a valid "
    30. + "ViewGroup root and attachToRoot=true");
    31. }
    32. rInflate(parser, root, inflaterContext, attrs, false);
    33. } else {
    34. // Temp is the root view that was found in the xml
    35. final View temp = createViewFromTag(root, name, inflaterContext, attrs);
    36. ViewGroup.LayoutParams params = null;
    37. if (root != null) {
    38. if (DEBUG) {
    39. System.out.println("Creating params from root: " +
    40. root);
    41. }
    42. // Create layout params that match root, if supplied
    43. params = root.generateLayoutParams(attrs);
    44. if (!attachToRoot) {
    45. // Set the layout params for temp if we are not
    46. // attaching. (If we are, we use addView, below)
    47. temp.setLayoutParams(params);
    48. }
    49. }
    50. if (DEBUG) {
    51. System.out.println("-----> start inflating children");
    52. }
    53. // Inflate all children under temp against its context.
    54. rInflateChildren(parser, temp, attrs, true);
    55. if (DEBUG) {
    56. System.out.println("-----> done inflating children");
    57. }
    58. // We are supposed to attach all the views we found (int temp)
    59. // to root. Do that now.
    60. if (root != null && attachToRoot) {
    61. root.addView(temp, params);
    62. }
    63. // Decide whether to return the root that was passed in or the
    64. // top view found in xml.
    65. if (root == null || !attachToRoot) {
    66. result = temp;
    67. }
    68. }
    69. } catch (XmlPullParserException e) {
    70. final InflateException ie = new InflateException(e.getMessage(), e);
    71. ie.setStackTrace(EMPTY_STACK_TRACE);
    72. throw ie;
    73. } catch (Exception e) {
    74. final InflateException ie = new InflateException(parser.getPositionDescription()
    75. + ": " + e.getMessage(), e);
    76. ie.setStackTrace(EMPTY_STACK_TRACE);
    77. throw ie;
    78. } finally {
    79. // Don't retain static reference on context.
    80. mConstructorArgs[0] = lastContext;
    81. mConstructorArgs[1] = null;
    82. Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    83. }
    84. return result;
    85. }
    86. }

            其中是 reateViewFromTag 创建rootView,rInflateChildren是创建子view。

    3.2.1  下面看创建rootView:reateViewFromTag

    1. // Temp is the root view that was found in the xml
    2. final View temp = createViewFromTag(root, name, inflaterContext, attrs);
    3. // Inflate all children under temp against its context.
    4. rInflateChildren(parser, temp, attrs, true);

            这个就是创建view(这里的view是指rootView)的核心代码:

    1. View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
    2. boolean ignoreThemeAttr) {
    3. if (name.equals("view")) {
    4. name = attrs.getAttributeValue(null, "class");
    5. }
    6. // Apply a theme wrapper, if allowed and one is specified.
    7. if (!ignoreThemeAttr) {
    8. final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
    9. final int themeResId = ta.getResourceId(0, 0);
    10. if (themeResId != 0) {
    11. context = new ContextThemeWrapper(context, themeResId);
    12. }
    13. ta.recycle();
    14. }
    15. if (name.equals(TAG_1995)) {
    16. // Let's party like it's 1995!
    17. return new BlinkLayout(context, attrs);
    18. }
    19. try {
    20. View view;
    21. if (mFactory2 != null) {
    22. view = mFactory2.onCreateView(parent, name, context, attrs);
    23. } else if (mFactory != null) {
    24. view = mFactory.onCreateView(name, context, attrs);
    25. } else {
    26. view = null;
    27. }
    28. if (view == null && mPrivateFactory != null) {
    29. view = mPrivateFactory.onCreateView(parent, name, context, attrs);
    30. }
    31. if (view == null) {
    32. final Object lastContext = mConstructorArgs[0];
    33. mConstructorArgs[0] = context;
    34. try {
    35. if (-1 == name.indexOf('.')) {
    36. view = onCreateView(parent, name, attrs);
    37. } else {
    38. view = createView(name, null, attrs);
    39. }
    40. } finally {
    41. mConstructorArgs[0] = lastContext;
    42. }
    43. }
    44. return view;
    45. } catch (InflateException e) {
    46. throw e;
    47. } catch (ClassNotFoundException e) {
    48. final InflateException ie = new InflateException(attrs.getPositionDescription()
    49. + ": Error inflating class " + name, e);
    50. ie.setStackTrace(EMPTY_STACK_TRACE);
    51. throw ie;
    52. } catch (Exception e) {
    53. final InflateException ie = new InflateException(attrs.getPositionDescription()
    54. + ": Error inflating class " + name, e);
    55. ie.setStackTrace(EMPTY_STACK_TRACE);
    56. throw ie;
    57. }
    58. }

    其中主要的:

    1. if (-1 == name.indexOf('.')) {
    2. view = onCreateView(parent, name, attrs);
    3. } else {
    4. view = createView(name, null, attrs);
    5. }

            之所以判断是否有“.”,是用来区分是否是sdk的view:如果带点的话,就是sddk的view;否则为自定义的view(这里的自定义view包含support包里view,比如:android.support.v7.widget.AppCompatTextView)。

            下面分别看下  onCreateView 和createView:

    1. protected View onCreateView(View parent, String name, AttributeSet attrs)
    2. throws ClassNotFoundException {
    3. return onCreateView(name, attrs);
    4. }

            继续进去:

    1. protected View onCreateView(String name, AttributeSet attrs)
    2. throws ClassNotFoundException {
    3. return createView(name, "android.view.", attrs);
    4. }

            这里就错了,他不是直接进入这个 onCreateView 方法,由于 LayoutInflater是一个抽象类,

    public abstract class LayoutInflater

    所以这里进入的是他的实现类 { PhoneLayoutInflater } 的两个参数的方法中:

    1. /** Override onCreateView to instantiate names that correspond to the
    2. widgets known to the Widget factory. If we don't find a match,
    3. call through to our super class.
    4. */
    5. @Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
    6. for (String prefix : sClassPrefixList) {
    7. try {
    8. View view = createView(name, prefix, attrs);
    9. if (view != null) {
    10. return view;
    11. }
    12. } catch (ClassNotFoundException e) {
    13. // In this case we want to let the base class take a crack
    14. // at it.
    15. }
    16. }
    17. return super.onCreateView(name, attrs);
    18. }

            这里的 sClassPrefixList 实际上是view的全类名:

    1. private static final String[] sClassPrefixList = {
    2. "android.widget.",
    3. "android.webkit.",
    4. "android.app."
    5. };
    6. // 另外还应该加一个:android.view

            实际他最终还是调用了createView,进去:

    1. public final View createView(String name, String prefix, AttributeSet attrs)
    2. throws ClassNotFoundException, InflateException {
    3. Constructorextends View> constructor = sConstructorMap.get(name);
    4. if (constructor != null && !verifyClassLoader(constructor)) {
    5. constructor = null;
    6. sConstructorMap.remove(name);
    7. }
    8. Classextends View> clazz = null;
    9. try {
    10. Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);
    11. if (constructor == null) {
    12. // Class not found in the cache, see if it's real, and try to add it
    13. clazz = mContext.getClassLoader().loadClass(
    14. prefix != null ? (prefix + name) : name).asSubclass(View.class);
    15. if (mFilter != null && clazz != null) {
    16. boolean allowed = mFilter.onLoadClass(clazz);
    17. if (!allowed) {
    18. failNotAllowed(name, prefix, attrs);
    19. }
    20. }
    21. constructor = clazz.getConstructor(mConstructorSignature);
    22. constructor.setAccessible(true);
    23. sConstructorMap.put(name, constructor);
    24. } else {
    25. // If we have a filter, apply it to cached constructor
    26. if (mFilter != null) {
    27. // Have we seen this name before?
    28. Boolean allowedState = mFilterMap.get(name);
    29. if (allowedState == null) {
    30. // New class -- remember whether it is allowed
    31. clazz = mContext.getClassLoader().loadClass(
    32. prefix != null ? (prefix + name) : name).asSubclass(View.class);
    33. boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
    34. mFilterMap.put(name, allowed);
    35. if (!allowed) {
    36. failNotAllowed(name, prefix, attrs);
    37. }
    38. } else if (allowedState.equals(Boolean.FALSE)) {
    39. failNotAllowed(name, prefix, attrs);
    40. }
    41. }
    42. }
    43. Object lastContext = mConstructorArgs[0];
    44. if (mConstructorArgs[0] == null) {
    45. // Fill in the context if not already within inflation.
    46. mConstructorArgs[0] = mContext;
    47. }
    48. Object[] args = mConstructorArgs;
    49. args[1] = attrs;
    50. final View view = constructor.newInstance(args);
    51. if (view instanceof ViewStub) {
    52. // Use the same context when inflating ViewStub later.
    53. final ViewStub viewStub = (ViewStub) view;
    54. viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
    55. }
    56. mConstructorArgs[0] = lastContext;
    57. return view;
    58. } catch (NoSuchMethodException e) {
    59. final InflateException ie = new InflateException(attrs.getPositionDescription()
    60. + ": Error inflating class " + (prefix != null ? (prefix + name) : name), e);
    61. ie.setStackTrace(EMPTY_STACK_TRACE);
    62. throw ie;
    63. } catch (ClassCastException e) {
    64. // If loaded class is not a View subclass
    65. final InflateException ie = new InflateException(attrs.getPositionDescription()
    66. + ": Class is not a View " + (prefix != null ? (prefix + name) : name), e);
    67. ie.setStackTrace(EMPTY_STACK_TRACE);
    68. throw ie;
    69. } catch (ClassNotFoundException e) {
    70. // If loadClass fails, we should propagate the exception.
    71. throw e;
    72. } catch (Exception e) {
    73. final InflateException ie = new InflateException(
    74. attrs.getPositionDescription() + ": Error inflating class "
    75. + (clazz == null ? "" : clazz.getName()), e);
    76. ie.setStackTrace(EMPTY_STACK_TRACE);
    77. throw ie;
    78. } finally {
    79. Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    80. }
    81. }

            内部实际上就是通过反射创建对应的view实例,然后返回。

    3.2.2  然后看rInflateChildren:创建子view

    1. final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
    2. boolean finishInflate) throws XmlPullParserException, IOException {
    3. rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
    4. }
    5. /**
    6. * Recursive method used to descend down the xml hierarchy and instantiate
    7. * views, instantiate their children, and then call onFinishInflate().
    8. *

    9. * Note: Default visibility so the BridgeInflater can
    10. * override it.
    11. */
    12. void rInflate(XmlPullParser parser, View parent, Context context,
    13. AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
    14. final int depth = parser.getDepth();
    15. int type;
    16. boolean pendingRequestFocus = false;
    17. while (((type = parser.next()) != XmlPullParser.END_TAG ||
    18. parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
    19. if (type != XmlPullParser.START_TAG) {
    20. continue;
    21. }
    22. final String name = parser.getName();
    23. if (TAG_REQUEST_FOCUS.equals(name)) {
    24. pendingRequestFocus = true;
    25. consumeChildElements(parser);
    26. } else if (TAG_TAG.equals(name)) {
    27. parseViewTag(parser, parent, attrs);
    28. } else if (TAG_INCLUDE.equals(name)) {
    29. if (parser.getDepth() == 0) {
    30. throw new InflateException(" cannot be the root element");
    31. }
    32. parseInclude(parser, context, parent, attrs);
    33. } else if (TAG_MERGE.equals(name)) {
    34. throw new InflateException(" must be the root element");
    35. } else {
    36. final View view = createViewFromTag(parent, name, context, attrs);
    37. final ViewGroup viewGroup = (ViewGroup) parent;
    38. final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
    39. rInflateChildren(parser, view, attrs, true);
    40. viewGroup.addView(view, params);
    41. }
    42. }
    43. if (pendingRequestFocus) {
    44. parent.restoreDefaultFocus();
    45. }
    46. if (finishInflate) {
    47. parent.onFinishInflate();
    48. }
    49. }

    它内部通过while循环不断地通过 XmlPullParser 判断类型然后,不断去创建view。

    3.3  将mContentParentExplicitlySet 置为 true


            注意这个标记:mContentParentExplicitlySet 是“不能设置decorView属性”的一个标记,在 setContentView 完成后会把这个变量标记为true,之后再通过 requestFeature() 设置 window的属性会报错:requestFeature() must be called before adding content 。 如下:

    1. if (mContentParentExplicitlySet) {
    2.          throw new AndroidRuntimeException("requestFeature() must be called before adding content");
    3.  } 

       他内部对标记:mContentParentExplicitlySet 做了一个判断。

            所以我们平时想要隐藏标题栏需要将:

    getWindow().requestFeature(Window.FEATURE_NO_TITLE);

    设置到  setContentView()之前的原因。

    4.下面图示一下setContentView的整个流程:

    三. AppCompatActivity的 setContentView

            他和activity有一些相似的地方,所有这里就用正常读源码的方式,倒着去看吧。

            首先进入AppCompatActivity的setContentView方法:

    1. @Override
    2. public void setContentView(@LayoutRes int layoutResID) {
    3. getDelegate().setContentView(layoutResID);
    4. }

            getDelegate()就是获得一个 { AppCompatDelegate }类型的变量,关于这个类的描述:

    This class represents a delegate which you can use to extend AppCompat's support to any activity.

             

    1. public AppCompatDelegate getDelegate() {
    2. if (mDelegate == null) {
    3. mDelegate = AppCompatDelegate.create(this, this);
    4. }
    5. return mDelegate;
    6. }

              而 AppCompatDelegate 的 create 方法,实际上是实例化了一个他的实现类:AppCompatDeletateImpl:

    1. /**
    2. * Create a {@link androidx.appcompat.app.AppCompatDelegate} to use with {@code activity}.
    3. *
    4. * @param callback An optional callback for AppCompat specific events
    5. */
    6. public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
    7. return new AppCompatDelegateImpl(activity, activity.getWindow(), callback);
    8. }

            然后进到 AppCompatDelegateImpl 去看 setContentView 方法:

    1. @Override
    2. public void setContentView(int resId) {
    3. ensureSubDecor();
    4. ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
    5. contentParent.removeAllViews();
    6. LayoutInflater.from(mContext).inflate(resId, contentParent);
    7. mOriginalWindowCallback.onContentChanged();
    8. }

            里面几个主要的方法:

    1. ensureSubDecor(); 

    2. ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);

    3. LayoutInflater.from(mContext).inflate(resId, contentParent); 

            这很类似于activity的setContentView方法:

    首先,activity通过getWindow获取Window的实现类PhoneWindow,然后调用PhoneWindow的setContentView方法;而AppCompatActivity是通过getDelegate()获取AppCompatDelegate的实现类AppCompatDeletateImpl,然后调用AppCompatDeletateImpl的setContentView方法;

    其次在setContentView方法中:

    PhoneWindow做了这几件事:

    1. 创建 DecorView,拿到 mContentParent: installDecor();   

    2. 将我们的界面xml渲染到mContentParent :

                    mLayoutInflater.inflate(layoutResID, mContentParent);

    3.将mContentParentExplicitlySet 置为 true;

    而AppCompatDeletateImpl也做了类似的事:

    1.创建SubDecor: ensureSubDecor(); 

    2.通过 findViewById 拿到 content:

        ViewGroup contentParent = (ViewGroup)mSubDecor.findViewById(android.R.id.content);

    3.渲染布局到content:LayoutInflater.from(mContext).inflate(resId, contentParent); 

            下面分别看着几步:

    1.ensureSubDecor();

    1. private void ensureSubDecor() {
    2. if (!mSubDecorInstalled) {
    3. mSubDecor = createSubDecor();
    4. // If a title was set before we installed the decor, propagate it now
    5. CharSequence title = getTitle();
    6. if (!TextUtils.isEmpty(title)) {
    7. if (mDecorContentParent != null) {
    8. mDecorContentParent.setWindowTitle(title);
    9. } else if (peekSupportActionBar() != null) {
    10. peekSupportActionBar().setWindowTitle(title);
    11. } else if (mTitleView != null) {
    12. mTitleView.setText(title);
    13. }
    14. }
    15. applyFixedSizeWindow();
    16. onSubDecorInstalled(mSubDecor);
    17. mSubDecorInstalled = true;
    18. // Invalidate if the panel menu hasn't been created before this.
    19. // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
    20. // being called in the middle of onCreate or similar.
    21. // A pending invalidation will typically be resolved before the posted message
    22. // would run normally in order to satisfy instance state restoration.
    23. PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
    24. if (!mIsDestroyed && (st == null || st.menu == null)) {
    25. invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
    26. }
    27. }
    28. }

    其中, createSubDecor 代码:

    1. private ViewGroup createSubDecor() {
    2. TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
    3. if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
    4. a.recycle();
    5. throw new IllegalStateException(
    6. "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
    7. }
    8. if (a.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false)) {
    9. requestWindowFeature(Window.FEATURE_NO_TITLE);
    10. } else if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBar, false)) {
    11. // Don't allow an action bar if there is no title.
    12. requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR);
    13. }
    14. if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBarOverlay, false)) {
    15. requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
    16. }
    17. if (a.getBoolean(R.styleable.AppCompatTheme_windowActionModeOverlay, false)) {
    18. requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY);
    19. }
    20. mIsFloating = a.getBoolean(R.styleable.AppCompatTheme_android_windowIsFloating, false);
    21. a.recycle();
    22. // Now let's make sure that the Window has installed its decor by retrieving it
    23. mWindow.getDecorView();
    24. final LayoutInflater inflater = LayoutInflater.from(mContext);
    25. ViewGroup subDecor = null;
    26. if (!mWindowNoTitle) {
    27. if (mIsFloating) {
    28. // If we're floating, inflate the dialog title decor
    29. subDecor = (ViewGroup) inflater.inflate(
    30. R.layout.abc_dialog_title_material, null);
    31. // Floating windows can never have an action bar, reset the flags
    32. mHasActionBar = mOverlayActionBar = false;
    33. } else if (mHasActionBar) {
    34. /**
    35. * This needs some explanation. As we can not use the android:theme attribute
    36. * pre-L, we emulate it by manually creating a LayoutInflater using a
    37. * ContextThemeWrapper pointing to actionBarTheme.
    38. */
    39. TypedValue outValue = new TypedValue();
    40. mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);
    41. Context themedContext;
    42. if (outValue.resourceId != 0) {
    43. themedContext = new ContextThemeWrapper(mContext, outValue.resourceId);
    44. } else {
    45. themedContext = mContext;
    46. }
    47. // Now inflate the view using the themed context and set it as the content view
    48. subDecor = (ViewGroup) LayoutInflater.from(themedContext)
    49. .inflate(R.layout.abc_screen_toolbar, null);
    50. mDecorContentParent = (DecorContentParent) subDecor
    51. .findViewById(R.id.decor_content_parent);
    52. mDecorContentParent.setWindowCallback(getWindowCallback());
    53. /**
    54. * Propagate features to DecorContentParent
    55. */
    56. if (mOverlayActionBar) {
    57. mDecorContentParent.initFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
    58. }
    59. if (mFeatureProgress) {
    60. mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);
    61. }
    62. if (mFeatureIndeterminateProgress) {
    63. mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
    64. }
    65. }
    66. } else {
    67. if (mOverlayActionMode) {
    68. subDecor = (ViewGroup) inflater.inflate(
    69. R.layout.abc_screen_simple_overlay_action_mode, null);
    70. } else {
    71. subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
    72. }
    73. if (Build.VERSION.SDK_INT >= 21) {
    74. // If we're running on L or above, we can rely on ViewCompat's
    75. // setOnApplyWindowInsetsListener
    76. ViewCompat.setOnApplyWindowInsetsListener(subDecor,
    77. new OnApplyWindowInsetsListener() {
    78. @Override
    79. public WindowInsetsCompat onApplyWindowInsets(View v,
    80. WindowInsetsCompat insets) {
    81. final int top = insets.getSystemWindowInsetTop();
    82. final int newTop = updateStatusGuard(top);
    83. if (top != newTop) {
    84. insets = insets.replaceSystemWindowInsets(
    85. insets.getSystemWindowInsetLeft(),
    86. newTop,
    87. insets.getSystemWindowInsetRight(),
    88. insets.getSystemWindowInsetBottom());
    89. }
    90. // Now apply the insets on our view
    91. return ViewCompat.onApplyWindowInsets(v, insets);
    92. }
    93. });
    94. } else {
    95. // Else, we need to use our own FitWindowsViewGroup handling
    96. ((FitWindowsViewGroup) subDecor).setOnFitSystemWindowsListener(
    97. new FitWindowsViewGroup.OnFitSystemWindowsListener() {
    98. @Override
    99. public void onFitSystemWindows(Rect insets) {
    100. insets.top = updateStatusGuard(insets.top);
    101. }
    102. });
    103. }
    104. }
    105. if (subDecor == null) {
    106. throw new IllegalArgumentException(
    107. "AppCompat does not support the current theme features: { "
    108. + "windowActionBar: " + mHasActionBar
    109. + ", windowActionBarOverlay: "+ mOverlayActionBar
    110. + ", android:windowIsFloating: " + mIsFloating
    111. + ", windowActionModeOverlay: " + mOverlayActionMode
    112. + ", windowNoTitle: " + mWindowNoTitle
    113. + " }");
    114. }
    115. if (mDecorContentParent == null) {
    116. mTitleView = (TextView) subDecor.findViewById(R.id.title);
    117. }
    118. // Make the decor optionally fit system windows, like the window's decor
    119. ViewUtils.makeOptionalFitsSystemWindows(subDecor);
    120. final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
    121. R.id.action_bar_activity_content);
    122. final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
    123. if (windowContentView != null) {
    124. // There might be Views already added to the Window's content view so we need to
    125. // migrate them to our content view
    126. while (windowContentView.getChildCount() > 0) {
    127. final View child = windowContentView.getChildAt(0);
    128. windowContentView.removeViewAt(0);
    129. contentView.addView(child);
    130. }
    131. // Change our content FrameLayout to use the android.R.id.content id.
    132. // Useful for fragments.
    133. windowContentView.setId(View.NO_ID);
    134. contentView.setId(android.R.id.content);
    135. // The decorContent may have a foreground drawable set (windowContentOverlay).
    136. // Remove this as we handle it ourselves
    137. if (windowContentView instanceof FrameLayout) {
    138. ((FrameLayout) windowContentView).setForeground(null);
    139. }
    140. }
    141. // Now set the Window's content view with the decor
    142. mWindow.setContentView(subDecor);
    143. contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {
    144. @Override
    145. public void onAttachedFromWindow() {}
    146. @Override
    147. public void onDetachedFromWindow() {
    148. dismissPopups();
    149. }
    150. });
    151. return subDecor;
    152. }

            代码很长,实际就是判断不同的属性 inflate了一个 ViewGroup 类型的 subDecor,然后返回。其中的 mWindow.getDecorView();  ,mWindow仍然是一个PhoneWindow类型,看他的getDecorView方法:

    1. @Override
    2. public final View getDecorView() {
    3. if (mDecor == null || mForceDecorInstall) {
    4. installDecor();
    5. }
    6. return mDecor;
    7. }

    内部执行了 installDecor:

    1. private void installDecor() {
    2. mForceDecorInstall = false;
    3. if (mDecor == null) {
    4. mDecor = generateDecor(-1);
    5. mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
    6. mDecor.setIsRootNamespace(true);
    7. if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
    8. mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
    9. }
    10. } else {
    11. mDecor.setWindow(this);
    12. }
    13. if (mContentParent == null) {
    14. mContentParent = generateLayout(mDecor);
    15. // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
    16. mDecor.makeOptionalFitsSystemWindows();
    17. final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
    18. R.id.decor_content_parent);
    19. if (decorContentParent != null) {
    20. mDecorContentParent = decorContentParent;
    21. mDecorContentParent.setWindowCallback(getCallback());
    22. if (mDecorContentParent.getTitle() == null) {
    23. mDecorContentParent.setWindowTitle(mTitle);
    24. }
    25. final int localFeatures = getLocalFeatures();
    26. for (int i = 0; i < FEATURE_MAX; i++) {
    27. if ((localFeatures & (1 << i)) != 0) {
    28. mDecorContentParent.initFeature(i);
    29. }
    30. }
    31. mDecorContentParent.setUiOptions(mUiOptions);
    32. if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 ||
    33. (mIconRes != 0 && !mDecorContentParent.hasIcon())) {
    34. mDecorContentParent.setIcon(mIconRes);
    35. } else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 &&
    36. mIconRes == 0 && !mDecorContentParent.hasIcon()) {
    37. mDecorContentParent.setIcon(
    38. getContext().getPackageManager().getDefaultActivityIcon());
    39. mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;
    40. }
    41. if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 ||
    42. (mLogoRes != 0 && !mDecorContentParent.hasLogo())) {
    43. mDecorContentParent.setLogo(mLogoRes);
    44. }
    45. // Invalidate if the panel menu hasn't been created before this.
    46. // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
    47. // being called in the middle of onCreate or similar.
    48. // A pending invalidation will typically be resolved before the posted message
    49. // would run normally in order to satisfy instance state restoration.
    50. PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
    51. if (!isDestroyed() && (st == null || st.menu == null) && !mIsStartingWindow) {
    52. invalidatePanelMenu(FEATURE_ACTION_BAR);
    53. }
    54. } else {
    55. mTitleView = findViewById(R.id.title);
    56. if (mTitleView != null) {
    57. if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
    58. final View titleContainer = findViewById(R.id.title_container);
    59. if (titleContainer != null) {
    60. titleContainer.setVisibility(View.GONE);
    61. } else {
    62. mTitleView.setVisibility(View.GONE);
    63. }
    64. mContentParent.setForeground(null);
    65. } else {
    66. mTitleView.setText(mTitle);
    67. }
    68. }
    69. }
    70. if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
    71. mDecor.setBackgroundFallback(mBackgroundFallbackResource);
    72. }
    73. // Only inflate or create a new TransitionManager if the caller hasn't
    74. // already set a custom one.
    75. if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) {
    76. if (mTransitionManager == null) {
    77. final int transitionRes = getWindowStyle().getResourceId(
    78. R.styleable.Window_windowContentTransitionManager,
    79. 0);
    80. if (transitionRes != 0) {
    81. final TransitionInflater inflater = TransitionInflater.from(getContext());
    82. mTransitionManager = inflater.inflateTransitionManager(transitionRes,
    83. mContentParent);
    84. } else {
    85. mTransitionManager = new TransitionManager();
    86. }
    87. }
    88. mEnterTransition = getTransition(mEnterTransition, null,
    89. R.styleable.Window_windowEnterTransition);
    90. mReturnTransition = getTransition(mReturnTransition, USE_DEFAULT_TRANSITION,
    91. R.styleable.Window_windowReturnTransition);
    92. mExitTransition = getTransition(mExitTransition, null,
    93. R.styleable.Window_windowExitTransition);
    94. mReenterTransition = getTransition(mReenterTransition, USE_DEFAULT_TRANSITION,
    95. R.styleable.Window_windowReenterTransition);
    96. mSharedElementEnterTransition = getTransition(mSharedElementEnterTransition, null,
    97. R.styleable.Window_windowSharedElementEnterTransition);
    98. mSharedElementReturnTransition = getTransition(mSharedElementReturnTransition,
    99. USE_DEFAULT_TRANSITION,
    100. R.styleable.Window_windowSharedElementReturnTransition);
    101. mSharedElementExitTransition = getTransition(mSharedElementExitTransition, null,
    102. R.styleable.Window_windowSharedElementExitTransition);
    103. mSharedElementReenterTransition = getTransition(mSharedElementReenterTransition,
    104. USE_DEFAULT_TRANSITION,
    105. R.styleable.Window_windowSharedElementReenterTransition);
    106. if (mAllowEnterTransitionOverlap == null) {
    107. mAllowEnterTransitionOverlap = getWindowStyle().getBoolean(
    108. R.styleable.Window_windowAllowEnterTransitionOverlap, true);
    109. }
    110. if (mAllowReturnTransitionOverlap == null) {
    111. mAllowReturnTransitionOverlap = getWindowStyle().getBoolean(
    112. R.styleable.Window_windowAllowReturnTransitionOverlap, true);
    113. }
    114. if (mBackgroundFadeDurationMillis < 0) {
    115. mBackgroundFadeDurationMillis = getWindowStyle().getInteger(
    116. R.styleable.Window_windowTransitionBackgroundFadeDuration,
    117. DEFAULT_BACKGROUND_FADE_DURATION_MS);
    118. }
    119. if (mSharedElementsUseOverlay == null) {
    120. mSharedElementsUseOverlay = getWindowStyle().getBoolean(
    121. R.styleable.Window_windowSharedElementsUseOverlay, true);
    122. }
    123. }
    124. }
    125. }

            到这里,几乎就和activity一样了。

  • 相关阅读:
    暄桐林曦老师的三个“知行”心得,帮你少走弯路
    面试字节跳动java岗被算法吊打,60天苦修这些笔记,侥幸收获offer
    Mysql-索引跳跃扫描
    【雷丰阳-谷粒商城 】课程概述
    DeepID人脸识别原理
    # Java手写LRU缓存算法
    Shiro【散列算法、Shiro会话、退出登录 、权限表设计、注解配置鉴权 】(五)-全面详解(学习总结---从入门到深化)
    通过宠物商店理解java面向对象
    2022网络搭建国赛题中的一道路由与交接题目解析
    洛谷-线段覆盖-(区间排序问题总结)
  • 原文地址:https://blog.csdn.net/set_one_name/article/details/127444463