本文将解析activity加载布局文件的setContentView源码,由于会涉及到部分activity的启动流程,这里附上一张activity启动流程图:
关于 setContentView 源码分两种情况,因为我们的 activity 有两个:
一种是继承自 Activity(来自sdk: android.app);另一种是继承自AppCompatActivity (来自support库:如androidx.appcompat.app),而这种的本质也是在 Activity 基础上的扩展,所以先看 Activity。这里的activity指的是根activity,而不是我们通常使用的应用activity。
在图中我们看到第8步:ActivityThread 调用 handleLaunchActivity() 方法,执行 Activity 的oncreate() 方法,而我们的 xml布局文件就是在oncreate()中通过:setContentView(R.layout.activity_main); 来加载的。下面就去看一下其内部如何实现的。
(看源码的时候,我们习惯倒着或向上或向内部一步步去追踪代码,这里我们反着以正向来分析)
首先看 ActivityThread 的 handleLaunchActivity() 方法:
- /**
- * Extended implementation of activity launch. Used when server requests a launch or relaunch.
- */
- @Override
- public Activity handleLaunchActivity(ActivityClientRecord r,
- PendingTransactionActions pendingActions, Intent customIntent) {
- // If we are getting ready to gc after going to the background, well
- // we are back active so skip it.
- unscheduleGcIdler();
- mSomeActivitiesChanged = true;
-
- if (r.profilerInfo != null) {
- mProfiler.setProfiler(r.profilerInfo);
- mProfiler.startProfiling();
- }
-
- // Make sure we are running with the most recent config.
- handleConfigurationChanged(null, null);
-
- if (localLOGV) Slog.v(
- TAG, "Handling launch of " + r);
-
- // Initialize before creating the activity
- if (!ThreadedRenderer.sRendererDisabled) {
- GraphicsEnvironment.earlyInitEGL();
- }
- WindowManagerGlobal.initialize();
-
- final Activity a = performLaunchActivity(r, customIntent);
-
- if (a != null) {
- r.createdConfig = new Configuration(mConfiguration);
- reportSizeConfigurations(r);
- if (!r.activity.mFinished && pendingActions != null) {
- pendingActions.setOldState(r.state);
- pendingActions.setRestoreInstanceState(true);
- pendingActions.setCallOnPostCreate(true);
- }
- } else {
- // If there was an error, for any reason, tell the activity manager to stop us.
- try {
- ActivityManager.getService()
- .finishActivity(r.token, Activity.RESULT_CANCELED, null,
- Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
- }
-
- return a;
- }
代码简单来说,就是通过 performLaunchActivity()创建了一个activity,然后返回。去看看他如何创建activity:
- /** Core implementation of activity launch. */
- private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
- ActivityInfo aInfo = r.activityInfo;
- if (r.packageInfo == null) {
- r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
- Context.CONTEXT_INCLUDE_CODE);
- }
-
- ComponentName component = r.intent.getComponent();
- if (component == null) {
- component = r.intent.resolveActivity(
- mInitialApplication.getPackageManager());
- r.intent.setComponent(component);
- }
-
- if (r.activityInfo.targetActivity != null) {
- component = new ComponentName(r.activityInfo.packageName,
- r.activityInfo.targetActivity);
- }
-
- ContextImpl appContext = createBaseContextForActivity(r);
- Activity activity = null;
- try {
- java.lang.ClassLoader cl = appContext.getClassLoader();
- activity = mInstrumentation.newActivity(
- cl, component.getClassName(), r.intent);
- StrictMode.incrementExpectedActivityCount(activity.getClass());
- r.intent.setExtrasClassLoader(cl);
- r.intent.prepareToEnterProcess();
- if (r.state != null) {
- r.state.setClassLoader(cl);
- }
- } catch (Exception e) {
- if (!mInstrumentation.onException(activity, e)) {
- throw new RuntimeException(
- "Unable to instantiate activity " + component
- + ": " + e.toString(), e);
- }
- }
-
- try {
- Application app = r.packageInfo.makeApplication(false, mInstrumentation);
-
- if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
- if (localLOGV) Slog.v(
- TAG, r + ": app=" + app
- + ", appName=" + app.getPackageName()
- + ", pkg=" + r.packageInfo.getPackageName()
- + ", comp=" + r.intent.getComponent().toShortString()
- + ", dir=" + r.packageInfo.getAppDir());
-
- if (activity != null) {
- CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
- Configuration config = new Configuration(mCompatConfiguration);
- if (r.overrideConfig != null) {
- config.updateFrom(r.overrideConfig);
- }
- if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
- + r.activityInfo.name + " with config " + config);
- Window window = null;
- if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
- window = r.mPendingRemoveWindow;
- r.mPendingRemoveWindow = null;
- r.mPendingRemoveWindowManager = null;
- }
- appContext.setOuterContext(activity);
- activity.attach(appContext, this, getInstrumentation(), r.token,
- r.ident, app, r.intent, r.activityInfo, title, r.parent,
- r.embeddedID, r.lastNonConfigurationInstances, config,
- r.referrer, r.voiceInteractor, window, r.configCallback);
-
- if (customIntent != null) {
- activity.mIntent = customIntent;
- }
- r.lastNonConfigurationInstances = null;
- checkAndBlockForNetworkAccess();
- activity.mStartedActivity = false;
- int theme = r.activityInfo.getThemeResource();
- if (theme != 0) {
- activity.setTheme(theme);
- }
-
- activity.mCalled = false;
- if (r.isPersistable()) {
- mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
- } else {
- mInstrumentation.callActivityOnCreate(activity, r.state);
- }
- if (!activity.mCalled) {
- throw new SuperNotCalledException(
- "Activity " + r.intent.getComponent().toShortString() +
- " did not call through to super.onCreate()");
- }
- r.activity = activity;
- }
- r.setState(ON_CREATE);
-
- mActivities.put(r.token, r);
-
- } catch (SuperNotCalledException e) {
- throw e;
-
- } catch (Exception e) {
- if (!mInstrumentation.onException(activity, e)) {
- throw new RuntimeException(
- "Unable to start activity " + component
- + ": " + e.toString(), e);
- }
- }
-
- return activity;
- }
总的来说就是通过反射创建了activity,然后执行了activity的attach方法(在attach方法中初始化了 PhoneWindow ),再然后回调activity的onCreate方法,在oncreate方法中再通过PhoneWindow 的 setContentView 方法加载布局。
内部初始化了 mWindow :
- final void attach(Context context, ActivityThread aThread,
- Instrumentation instr, IBinder token, int ident,
- Application application, Intent intent, ActivityInfo info,
- CharSequence title, Activity parent, String id,
- NonConfigurationInstances lastNonConfigurationInstances,
- Configuration config, String referrer, IVoiceInteractor voiceInteractor,
- Window window, ActivityConfigCallback activityConfigCallback) {
- attachBaseContext(context);
-
- mFragments.attachHost(null /*parent*/);
-
- // 初始化 mWindow
- mWindow = new PhoneWindow(this, window, activityConfigCallback);
- mWindow.setWindowControllerCallback(this);
- mWindow.setCallback(this);
- mWindow.setOnWindowDismissedCallback(this);
- mWindow.getLayoutInflater().setPrivateFactory(this);
- if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
- mWindow.setSoftInputMode(info.softInputMode);
- }
- if (info.uiOptions != 0) {
- mWindow.setUiOptions(info.uiOptions);
- }
- mUiThread = Thread.currentThread();
-
- mMainThread = aThread;
- mInstrumentation = instr;
- mToken = token;
- mIdent = ident;
- mApplication = application;
- mIntent = intent;
- mReferrer = referrer;
- mComponent = intent.getComponent();
- mActivityInfo = info;
- mTitle = title;
- mParent = parent;
- mEmbeddedID = id;
- mLastNonConfigurationInstances = lastNonConfigurationInstances;
- if (voiceInteractor != null) {
- if (lastNonConfigurationInstances != null) {
- mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
- } else {
- mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
- Looper.myLooper());
- }
- }
-
- mWindow.setWindowManager(
- (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
- mToken, mComponent.flattenToString(),
- (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
- if (mParent != null) {
- mWindow.setContainer(mParent.getWindow());
- }
- mWindowManager = mWindow.getWindowManager();
- mCurrentConfig = config;
-
- mWindow.setColorMode(info.colorMode);
-
- setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled());
- enableAutofillCompatibilityIfNeeded();
- }
performLaunchActivity()中回调activity的onCreate方法
- if (r.isPersistable()) {
- mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
- } else {
- mInstrumentation.callActivityOnCreate(activity, r.state);
- }
mInstrumentation的callActivityOnCreate方法(关于mInstrumentation单独去学习):
- /**
- * Perform calling of an activity's {@link Activity#onCreate}
- * method. The default implementation simply calls through to that method.
- * @param activity The activity being created.
- * @param icicle The previously frozen state (or null) to pass through to
- * @param persistentState The previously persisted state (or null)
- */
- public void callActivityOnCreate(Activity activity, Bundle icicle,
- PersistableBundle persistentState) {
- prePerformCreate(activity);
- activity.performCreate(icicle, persistentState);
- postPerformCreate(activity);
- }
callActivityOnCreate 中的 performCreate 方法:
- final void performCreate(Bundle icicle) {
- performCreate(icicle, null);
- }
-
- final void performCreate(Bundle icicle, PersistableBundle persistentState) {
- mCanEnterPictureInPicture = true;
- restoreHasCurrentPermissionRequest(icicle);
- if (persistentState != null) {
- onCreate(icicle, persistentState);
- } else {
- onCreate(icicle);
- }
- writeEventLog(LOG_AM_ON_CREATE_CALLED, "performCreate");
- mActivityTransitionState.readState(icicle);
-
- mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
- com.android.internal.R.styleable.Window_windowNoDisplay, false);
- mFragments.dispatchActivityCreated();
- mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
- }
然后在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.
- /**
- * Set the activity content from a layout resource. The resource will be
- * inflated, adding all top-level views to the activity.
- *
- * @param layoutResID Resource ID to be inflated.
- *
- * @see #setContentView(android.view.View)
- * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
- */
- public void setContentView(@LayoutRes int layoutResID) {
- getWindow().setContentView(layoutResID);
- initWindowDecorActionBar();
- }
-
- /**
- * Set the activity content to an explicit view. This view is placed
- * directly into the activity's view hierarchy. It can itself be a complex
- * view hierarchy. When calling this method, the layout parameters of the
- * specified view are ignored. Both the width and the height of the view are
- * set by default to {@link ViewGroup.LayoutParams#MATCH_PARENT}. To use
- * your own layout parameters, invoke
- * {@link #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}
- * instead.
- *
- * @param view The desired content to display.
- *
- * @see #setContentView(int)
- * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
- */
- public void setContentView(View view) {
- getWindow().setContentView(view);
- initWindowDecorActionBar();
- }
-
- /**
- * Set the activity content to an explicit view. This view is placed
- * directly into the activity's view hierarchy. It can itself be a complex
- * view hierarchy.
- *
- * @param view The desired content to display.
- * @param params Layout parameters for the view.
- *
- * @see #setContentView(android.view.View)
- * @see #setContentView(int)
- */
- public void setContentView(View view, ViewGroup.LayoutParams params) {
- getWindow().setContentView(view, params);
- initWindowDecorActionBar();
- }
这个 getWindow() 就是在attach方法中初始化的PhoneWindow对象,那去PhoneWindow看下他的 setContentView 方法:
- @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();
- }
- mContentParentExplicitlySet = true;
- }
他内部主要做了这几件事:
1. 创建 DecorView,拿到 mContentParent: installDecor();
2. 将我们的界面xml渲染到mContentParent :
mLayoutInflater.inflate(layoutResID, mContentParent);
3.将mContentParentExplicitlySet 置为 true(这个重要,下面会有解释)
- private void installDecor() {
- mForceDecorInstall = false;
- if (mDecor == null) {
- mDecor = generateDecor(-1);
- mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
- mDecor.setIsRootNamespace(true);
- if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
- mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
- }
- } else {
- mDecor.setWindow(this);
- }
- if (mContentParent == null) {
- mContentParent = generateLayout(mDecor);
-
- // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
- mDecor.makeOptionalFitsSystemWindows();
-
- final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
- R.id.decor_content_parent);
-
- if (decorContentParent != null) {
- mDecorContentParent = decorContentParent;
- mDecorContentParent.setWindowCallback(getCallback());
- if (mDecorContentParent.getTitle() == null) {
- mDecorContentParent.setWindowTitle(mTitle);
- }
-
- final int localFeatures = getLocalFeatures();
- for (int i = 0; i < FEATURE_MAX; i++) {
- if ((localFeatures & (1 << i)) != 0) {
- mDecorContentParent.initFeature(i);
- }
- }
-
- mDecorContentParent.setUiOptions(mUiOptions);
-
- if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 ||
- (mIconRes != 0 && !mDecorContentParent.hasIcon())) {
- mDecorContentParent.setIcon(mIconRes);
- } else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 &&
- mIconRes == 0 && !mDecorContentParent.hasIcon()) {
- mDecorContentParent.setIcon(
- getContext().getPackageManager().getDefaultActivityIcon());
- mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;
- }
- if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 ||
- (mLogoRes != 0 && !mDecorContentParent.hasLogo())) {
- mDecorContentParent.setLogo(mLogoRes);
- }
-
- // Invalidate if the panel menu hasn't been created before this.
- // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
- // being called in the middle of onCreate or similar.
- // A pending invalidation will typically be resolved before the posted message
- // would run normally in order to satisfy instance state restoration.
- PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
- if (!isDestroyed() && (st == null || st.menu == null) && !mIsStartingWindow) {
- invalidatePanelMenu(FEATURE_ACTION_BAR);
- }
- } else {
- mTitleView = findViewById(R.id.title);
- if (mTitleView != null) {
- if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
- final View titleContainer = findViewById(R.id.title_container);
- if (titleContainer != null) {
- titleContainer.setVisibility(View.GONE);
- } else {
- mTitleView.setVisibility(View.GONE);
- }
- mContentParent.setForeground(null);
- } else {
- mTitleView.setText(mTitle);
- }
- }
- }
-
- if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
- mDecor.setBackgroundFallback(mBackgroundFallbackResource);
- }
-
- // Only inflate or create a new TransitionManager if the caller hasn't
- // already set a custom one.
- if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) {
- if (mTransitionManager == null) {
- final int transitionRes = getWindowStyle().getResourceId(
- R.styleable.Window_windowContentTransitionManager,
- 0);
- if (transitionRes != 0) {
- final TransitionInflater inflater = TransitionInflater.from(getContext());
- mTransitionManager = inflater.inflateTransitionManager(transitionRes,
- mContentParent);
- } else {
- mTransitionManager = new TransitionManager();
- }
- }
-
- mEnterTransition = getTransition(mEnterTransition, null,
- R.styleable.Window_windowEnterTransition);
- mReturnTransition = getTransition(mReturnTransition, USE_DEFAULT_TRANSITION,
- R.styleable.Window_windowReturnTransition);
- mExitTransition = getTransition(mExitTransition, null,
- R.styleable.Window_windowExitTransition);
- mReenterTransition = getTransition(mReenterTransition, USE_DEFAULT_TRANSITION,
- R.styleable.Window_windowReenterTransition);
- mSharedElementEnterTransition = getTransition(mSharedElementEnterTransition, null,
- R.styleable.Window_windowSharedElementEnterTransition);
- mSharedElementReturnTransition = getTransition(mSharedElementReturnTransition,
- USE_DEFAULT_TRANSITION,
- R.styleable.Window_windowSharedElementReturnTransition);
- mSharedElementExitTransition = getTransition(mSharedElementExitTransition, null,
- R.styleable.Window_windowSharedElementExitTransition);
- mSharedElementReenterTransition = getTransition(mSharedElementReenterTransition,
- USE_DEFAULT_TRANSITION,
- R.styleable.Window_windowSharedElementReenterTransition);
- if (mAllowEnterTransitionOverlap == null) {
- mAllowEnterTransitionOverlap = getWindowStyle().getBoolean(
- R.styleable.Window_windowAllowEnterTransitionOverlap, true);
- }
- if (mAllowReturnTransitionOverlap == null) {
- mAllowReturnTransitionOverlap = getWindowStyle().getBoolean(
- R.styleable.Window_windowAllowReturnTransitionOverlap, true);
- }
- if (mBackgroundFadeDurationMillis < 0) {
- mBackgroundFadeDurationMillis = getWindowStyle().getInteger(
- R.styleable.Window_windowTransitionBackgroundFadeDuration,
- DEFAULT_BACKGROUND_FADE_DURATION_MS);
- }
- if (mSharedElementsUseOverlay == null) {
- mSharedElementsUseOverlay = getWindowStyle().getBoolean(
- R.styleable.Window_windowSharedElementsUseOverlay, true);
- }
- }
- }
- }
里面重点看两个方法:
mDecor = generateDecor(-1); 和 mContentParent = generateLayout(mDecor);
- protected DecorView generateDecor(int featureId) {
- // System process doesn't have application context and in that case we need to directly use
- // the context we have. Otherwise we want the application context, so we don't cling to the
- // activity.
- Context context;
- if (mUseDecorContext) {
- Context applicationContext = getContext().getApplicationContext();
- if (applicationContext == null) {
- context = getContext();
- } else {
- context = new DecorContext(applicationContext, getContext());
- if (mTheme != -1) {
- context.setTheme(mTheme);
- }
- }
- } else {
- context = getContext();
- }
- return new DecorView(context, featureId, this, getAttributes());
- }
创建了DecorView,然后返回。
- protected ViewGroup generateLayout(DecorView decor) {
- // Apply data from current theme.
-
- TypedArray a = getWindowStyle();
-
- if (false) {
- System.out.println("From style:");
- String s = "Attrs:";
- for (int i = 0; i < R.styleable.Window.length; i++) {
- s = s + " " + Integer.toHexString(R.styleable.Window[i]) + "="
- + a.getString(i);
- }
- System.out.println(s);
- }
-
- mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
- int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
- & (~getForcedWindowFlags());
- if (mIsFloating) {
- setLayout(WRAP_CONTENT, WRAP_CONTENT);
- setFlags(0, flagsToUpdate);
- } else {
- setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
- }
-
- if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
- requestFeature(FEATURE_NO_TITLE);
- } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
- // Don't allow an action bar if there is no title.
- requestFeature(FEATURE_ACTION_BAR);
- }
-
- if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {
- requestFeature(FEATURE_ACTION_BAR_OVERLAY);
- }
-
- if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) {
- requestFeature(FEATURE_ACTION_MODE_OVERLAY);
- }
-
- if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) {
- requestFeature(FEATURE_SWIPE_TO_DISMISS);
- }
-
- if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
- setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
- }
-
- if (a.getBoolean(R.styleable.Window_windowTranslucentStatus,
- false)) {
- setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS
- & (~getForcedWindowFlags()));
- }
-
- if (a.getBoolean(R.styleable.Window_windowTranslucentNavigation,
- false)) {
- setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION
- & (~getForcedWindowFlags()));
- }
-
- if (a.getBoolean(R.styleable.Window_windowOverscan, false)) {
- setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForcedWindowFlags()));
- }
-
- if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) {
- setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));
- }
-
- if (a.getBoolean(R.styleable.Window_windowEnableSplitTouch,
- getContext().getApplicationInfo().targetSdkVersion
- >= android.os.Build.VERSION_CODES.HONEYCOMB)) {
- setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));
- }
-
- a.getValue(R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);
- a.getValue(R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);
- if (DEBUG) Log.d(TAG, "Min width minor: " + mMinWidthMinor.coerceToString()
- + ", major: " + mMinWidthMajor.coerceToString());
- if (a.hasValue(R.styleable.Window_windowFixedWidthMajor)) {
- if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();
- a.getValue(R.styleable.Window_windowFixedWidthMajor,
- mFixedWidthMajor);
- }
- if (a.hasValue(R.styleable.Window_windowFixedWidthMinor)) {
- if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();
- a.getValue(R.styleable.Window_windowFixedWidthMinor,
- mFixedWidthMinor);
- }
- if (a.hasValue(R.styleable.Window_windowFixedHeightMajor)) {
- if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();
- a.getValue(R.styleable.Window_windowFixedHeightMajor,
- mFixedHeightMajor);
- }
- if (a.hasValue(R.styleable.Window_windowFixedHeightMinor)) {
- if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();
- a.getValue(R.styleable.Window_windowFixedHeightMinor,
- mFixedHeightMinor);
- }
- if (a.getBoolean(R.styleable.Window_windowContentTransitions, false)) {
- requestFeature(FEATURE_CONTENT_TRANSITIONS);
- }
- if (a.getBoolean(R.styleable.Window_windowActivityTransitions, false)) {
- requestFeature(FEATURE_ACTIVITY_TRANSITIONS);
- }
-
- mIsTranslucent = a.getBoolean(R.styleable.Window_windowIsTranslucent, false);
-
- final Context context = getContext();
- final int targetSdk = context.getApplicationInfo().targetSdkVersion;
- final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
- final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
- final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;
- final boolean targetHcNeedsOptions = context.getResources().getBoolean(
- R.bool.target_honeycomb_needs_options_menu);
- final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);
-
- if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {
- setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE);
- } else {
- setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_FALSE);
- }
-
- if (!mForcedStatusBarColor) {
- mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);
- }
- if (!mForcedNavigationBarColor) {
- mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);
- mNavigationBarDividerColor = a.getColor(R.styleable.Window_navigationBarDividerColor,
- 0x00000000);
- }
-
- WindowManager.LayoutParams params = getAttributes();
-
- // Non-floating windows on high end devices must put up decor beneath the system bars and
- // therefore must know about visibility changes of those.
- if (!mIsFloating) {
- if (!targetPreL && a.getBoolean(
- R.styleable.Window_windowDrawsSystemBarBackgrounds,
- false)) {
- setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
- FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags());
- }
- if (mDecor.mForceWindowDrawsStatusBarBackground) {
- params.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
- }
- }
- if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {
- decor.setSystemUiVisibility(
- decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
- }
- if (a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)) {
- decor.setSystemUiVisibility(
- decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
- }
- if (a.hasValue(R.styleable.Window_windowLayoutInDisplayCutoutMode)) {
- int mode = a.getInt(R.styleable.Window_windowLayoutInDisplayCutoutMode, -1);
- if (mode < LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
- || mode > LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER) {
- throw new UnsupportedOperationException("Unknown windowLayoutInDisplayCutoutMode: "
- + a.getString(R.styleable.Window_windowLayoutInDisplayCutoutMode));
- }
- params.layoutInDisplayCutoutMode = mode;
- }
-
- if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion
- >= android.os.Build.VERSION_CODES.HONEYCOMB) {
- if (a.getBoolean(
- R.styleable.Window_windowCloseOnTouchOutside,
- false)) {
- setCloseOnTouchOutsideIfNotSet(true);
- }
- }
-
- if (!hasSoftInputMode()) {
- params.softInputMode = a.getInt(
- R.styleable.Window_windowSoftInputMode,
- params.softInputMode);
- }
-
- if (a.getBoolean(R.styleable.Window_backgroundDimEnabled,
- mIsFloating)) {
- /* All dialogs should have the window dimmed */
- if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) {
- params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
- }
- if (!haveDimAmount()) {
- params.dimAmount = a.getFloat(
- android.R.styleable.Window_backgroundDimAmount, 0.5f);
- }
- }
-
- if (params.windowAnimations == 0) {
- params.windowAnimations = a.getResourceId(
- R.styleable.Window_windowAnimationStyle, 0);
- }
-
- // The rest are only done if this window is not embedded; otherwise,
- // the values are inherited from our container.
- if (getContainer() == null) {
- if (mBackgroundDrawable == null) {
- if (mBackgroundResource == 0) {
- mBackgroundResource = a.getResourceId(
- R.styleable.Window_windowBackground, 0);
- }
- if (mFrameResource == 0) {
- mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0);
- }
- mBackgroundFallbackResource = a.getResourceId(
- R.styleable.Window_windowBackgroundFallback, 0);
- if (false) {
- System.out.println("Background: "
- + Integer.toHexString(mBackgroundResource) + " Frame: "
- + Integer.toHexString(mFrameResource));
- }
- }
- if (mLoadElevation) {
- mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);
- }
- mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);
- mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT);
- }
-
- // Inflate the window decor.
-
- int layoutResource;
- int features = getLocalFeatures();
- // System.out.println("Features: 0x" + Integer.toHexString(features));
- if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
- layoutResource = R.layout.screen_swipe_dismiss;
- setCloseOnSwipeEnabled(true);
- } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
- if (mIsFloating) {
- TypedValue res = new TypedValue();
- getContext().getTheme().resolveAttribute(
- R.attr.dialogTitleIconsDecorLayout, res, true);
- layoutResource = res.resourceId;
- } else {
- layoutResource = R.layout.screen_title_icons;
- }
- // XXX Remove this once action bar supports these features.
- removeFeature(FEATURE_ACTION_BAR);
- // System.out.println("Title Icons!");
- } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
- && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
- // Special case for a window with only a progress bar (and title).
- // XXX Need to have a no-title version of embedded windows.
- layoutResource = R.layout.screen_progress;
- // System.out.println("Progress!");
- } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
- // Special case for a window with a custom title.
- // If the window is floating, we need a dialog layout
- if (mIsFloating) {
- TypedValue res = new TypedValue();
- getContext().getTheme().resolveAttribute(
- R.attr.dialogCustomTitleDecorLayout, res, true);
- layoutResource = res.resourceId;
- } else {
- layoutResource = R.layout.screen_custom_title;
- }
- // XXX Remove this once action bar supports these features.
- removeFeature(FEATURE_ACTION_BAR);
- } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
- // If no other features and not embedded, only need a title.
- // If the window is floating, we need a dialog layout
- if (mIsFloating) {
- TypedValue res = new TypedValue();
- getContext().getTheme().resolveAttribute(
- R.attr.dialogTitleDecorLayout, res, true);
- layoutResource = res.resourceId;
- } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
- layoutResource = a.getResourceId(
- R.styleable.Window_windowActionBarFullscreenDecorLayout,
- R.layout.screen_action_bar);
- } else {
- layoutResource = R.layout.screen_title;
- }
- // System.out.println("Title!");
- } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
- layoutResource = R.layout.screen_simple_overlay_action_mode;
- } else {
- // Embedded, so no decoration is needed.
- layoutResource = R.layout.screen_simple;
- // System.out.println("Simple!");
- }
-
- mDecor.startChanging();
- mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
-
- ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
- if (contentParent == null) {
- throw new RuntimeException("Window couldn't find content container view");
- }
-
- if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
- ProgressBar progress = getCircularProgressBar(false);
- if (progress != null) {
- progress.setIndeterminate(true);
- }
- }
-
- if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
- registerSwipeCallbacks(contentParent);
- }
-
- // Remaining setup -- of background and title -- that only applies
- // to top-level windows.
- if (getContainer() == null) {
- final Drawable background;
- if (mBackgroundResource != 0) {
- background = getContext().getDrawable(mBackgroundResource);
- } else {
- background = mBackgroundDrawable;
- }
- mDecor.setWindowBackground(background);
-
- final Drawable frame;
- if (mFrameResource != 0) {
- frame = getContext().getDrawable(mFrameResource);
- } else {
- frame = null;
- }
- mDecor.setWindowFrame(frame);
-
- mDecor.setElevation(mElevation);
- mDecor.setClipToOutline(mClipToOutline);
-
- if (mTitle != null) {
- setTitle(mTitle);
- }
-
- if (mTitleColor == 0) {
- mTitleColor = mTextColor;
- }
- setTitleColor(mTitleColor);
- }
-
- mDecor.finishChanging();
-
- return contentParent;
- }
代码长了点,实际也简单,
1. 和我们自定义view有些相似,通过获取自定义属性来设置 mDecor 的状态。 像我们常设置的 “FEATURE_NO_TITLE” 就在这里,这也是我们要把这个属性设置在setContentView之前的原因:
setContentView 在加载布局时会通过 requestFeature() 加载我们设置的属性。
2. 把我们的xml布局文件添加到 decorView (decorView本质是一个FrameLayout):
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
mLayoutInflater.inflate(layoutResID, mContentParent);
去看这个inflate方法:
- public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
- return inflate(resource, root, root != null);
- }
继续看:
- public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
- final Resources res = getContext().getResources();
- if (DEBUG) {
- Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
- + Integer.toHexString(resource) + ")");
- }
-
- final XmlResourceParser parser = res.getLayout(resource);
- try {
- return inflate(parser, root, attachToRoot);
- } finally {
- parser.close();
- }
- }
这里看到时xml解析器:XmlResourceParser ,再进去 inflate:
- public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
- synchronized (mConstructorArgs) {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
-
- final Context inflaterContext = mContext;
- final AttributeSet attrs = Xml.asAttributeSet(parser);
- Context lastContext = (Context) mConstructorArgs[0];
- mConstructorArgs[0] = inflaterContext;
- View result = root;
-
- try {
- // Look for the root node.
- int type;
- while ((type = parser.next()) != XmlPullParser.START_TAG &&
- type != XmlPullParser.END_DOCUMENT) {
- // Empty
- }
-
- if (type != XmlPullParser.START_TAG) {
- throw new InflateException(parser.getPositionDescription()
- + ": No start tag found!");
- }
-
- final String name = parser.getName();
-
- if (DEBUG) {
- System.out.println("**************************");
- System.out.println("Creating root view: "
- + name);
- System.out.println("**************************");
- }
-
- if (TAG_MERGE.equals(name)) {
- if (root == null || !attachToRoot) {
- throw new InflateException("
can be used only with a valid " - + "ViewGroup root and attachToRoot=true");
- }
-
- rInflate(parser, root, inflaterContext, attrs, false);
- } else {
- // Temp is the root view that was found in the xml
- final View temp = createViewFromTag(root, name, inflaterContext, attrs);
-
- ViewGroup.LayoutParams params = null;
-
- if (root != null) {
- if (DEBUG) {
- System.out.println("Creating params from root: " +
- root);
- }
- // Create layout params that match root, if supplied
- params = root.generateLayoutParams(attrs);
- if (!attachToRoot) {
- // Set the layout params for temp if we are not
- // attaching. (If we are, we use addView, below)
- temp.setLayoutParams(params);
- }
- }
-
- if (DEBUG) {
- System.out.println("-----> start inflating children");
- }
-
- // Inflate all children under temp against its context.
- rInflateChildren(parser, temp, attrs, true);
-
- if (DEBUG) {
- System.out.println("-----> done inflating children");
- }
-
- // We are supposed to attach all the views we found (int temp)
- // to root. Do that now.
- if (root != null && attachToRoot) {
- root.addView(temp, params);
- }
-
- // Decide whether to return the root that was passed in or the
- // top view found in xml.
- if (root == null || !attachToRoot) {
- result = temp;
- }
- }
-
- } catch (XmlPullParserException e) {
- final InflateException ie = new InflateException(e.getMessage(), e);
- ie.setStackTrace(EMPTY_STACK_TRACE);
- throw ie;
- } catch (Exception e) {
- final InflateException ie = new InflateException(parser.getPositionDescription()
- + ": " + e.getMessage(), e);
- ie.setStackTrace(EMPTY_STACK_TRACE);
- throw ie;
- } finally {
- // Don't retain static reference on context.
- mConstructorArgs[0] = lastContext;
- mConstructorArgs[1] = null;
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
- }
- return result;
- }
- }
其中是 reateViewFromTag 创建rootView,rInflateChildren是创建子view。
- // Temp is the root view that was found in the xml
- final View temp = createViewFromTag(root, name, inflaterContext, attrs);
-
-
- // Inflate all children under temp against its context.
- rInflateChildren(parser, temp, attrs, true);
这个就是创建view(这里的view是指rootView)的核心代码:
- View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
- boolean ignoreThemeAttr) {
- if (name.equals("view")) {
- name = attrs.getAttributeValue(null, "class");
- }
-
- // Apply a theme wrapper, if allowed and one is specified.
- if (!ignoreThemeAttr) {
- final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
- final int themeResId = ta.getResourceId(0, 0);
- if (themeResId != 0) {
- context = new ContextThemeWrapper(context, themeResId);
- }
- ta.recycle();
- }
-
- if (name.equals(TAG_1995)) {
- // Let's party like it's 1995!
- return new BlinkLayout(context, attrs);
- }
-
- try {
- View view;
- if (mFactory2 != null) {
- view = mFactory2.onCreateView(parent, name, context, attrs);
- } else if (mFactory != null) {
- view = mFactory.onCreateView(name, context, attrs);
- } else {
- view = null;
- }
-
- if (view == null && mPrivateFactory != null) {
- view = mPrivateFactory.onCreateView(parent, name, context, attrs);
- }
-
- if (view == null) {
- final Object lastContext = mConstructorArgs[0];
- mConstructorArgs[0] = context;
- try {
- if (-1 == name.indexOf('.')) {
- view = onCreateView(parent, name, attrs);
- } else {
- view = createView(name, null, attrs);
- }
- } finally {
- mConstructorArgs[0] = lastContext;
- }
- }
-
- return view;
- } catch (InflateException e) {
- throw e;
-
- } catch (ClassNotFoundException e) {
- final InflateException ie = new InflateException(attrs.getPositionDescription()
- + ": Error inflating class " + name, e);
- ie.setStackTrace(EMPTY_STACK_TRACE);
- throw ie;
-
- } catch (Exception e) {
- final InflateException ie = new InflateException(attrs.getPositionDescription()
- + ": Error inflating class " + name, e);
- ie.setStackTrace(EMPTY_STACK_TRACE);
- throw ie;
- }
- }
其中主要的:
- if (-1 == name.indexOf('.')) {
- view = onCreateView(parent, name, attrs);
- } else {
- view = createView(name, null, attrs);
- }
之所以判断是否有“.”,是用来区分是否是sdk的view:如果带点的话,就是sddk的view;否则为自定义的view(这里的自定义view包含support包里view,比如:android.support.v7.widget.AppCompatTextView)。
下面分别看下 onCreateView 和createView:
- protected View onCreateView(View parent, String name, AttributeSet attrs)
- throws ClassNotFoundException {
- return onCreateView(name, attrs);
- }
继续进去:
- protected View onCreateView(String name, AttributeSet attrs)
- throws ClassNotFoundException {
- return createView(name, "android.view.", attrs);
- }
这里就错了,他不是直接进入这个 onCreateView 方法,由于 LayoutInflater是一个抽象类,
public abstract class LayoutInflater
所以这里进入的是他的实现类 { PhoneLayoutInflater } 的两个参数的方法中:
- /** Override onCreateView to instantiate names that correspond to the
- widgets known to the Widget factory. If we don't find a match,
- call through to our super class.
- */
- @Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
- for (String prefix : sClassPrefixList) {
- try {
- View view = createView(name, prefix, attrs);
- if (view != null) {
- return view;
- }
- } catch (ClassNotFoundException e) {
- // In this case we want to let the base class take a crack
- // at it.
- }
- }
-
- return super.onCreateView(name, attrs);
- }
这里的 sClassPrefixList 实际上是view的全类名:
- private static final String[] sClassPrefixList = {
- "android.widget.",
- "android.webkit.",
- "android.app."
- };
- // 另外还应该加一个:android.view
实际他最终还是调用了createView,进去:
- public final View createView(String name, String prefix, AttributeSet attrs)
- throws ClassNotFoundException, InflateException {
- Constructor extends View> constructor = sConstructorMap.get(name);
- if (constructor != null && !verifyClassLoader(constructor)) {
- constructor = null;
- sConstructorMap.remove(name);
- }
- Class extends View> clazz = null;
-
- try {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);
-
- if (constructor == null) {
- // Class not found in the cache, see if it's real, and try to add it
- clazz = mContext.getClassLoader().loadClass(
- prefix != null ? (prefix + name) : name).asSubclass(View.class);
-
- if (mFilter != null && clazz != null) {
- boolean allowed = mFilter.onLoadClass(clazz);
- if (!allowed) {
- failNotAllowed(name, prefix, attrs);
- }
- }
- constructor = clazz.getConstructor(mConstructorSignature);
- constructor.setAccessible(true);
- sConstructorMap.put(name, constructor);
- } else {
- // If we have a filter, apply it to cached constructor
- if (mFilter != null) {
- // Have we seen this name before?
- Boolean allowedState = mFilterMap.get(name);
- if (allowedState == null) {
- // New class -- remember whether it is allowed
- clazz = mContext.getClassLoader().loadClass(
- prefix != null ? (prefix + name) : name).asSubclass(View.class);
-
- boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
- mFilterMap.put(name, allowed);
- if (!allowed) {
- failNotAllowed(name, prefix, attrs);
- }
- } else if (allowedState.equals(Boolean.FALSE)) {
- failNotAllowed(name, prefix, attrs);
- }
- }
- }
-
- Object lastContext = mConstructorArgs[0];
- if (mConstructorArgs[0] == null) {
- // Fill in the context if not already within inflation.
- mConstructorArgs[0] = mContext;
- }
- Object[] args = mConstructorArgs;
- args[1] = attrs;
-
- final View view = constructor.newInstance(args);
- if (view instanceof ViewStub) {
- // Use the same context when inflating ViewStub later.
- final ViewStub viewStub = (ViewStub) view;
- viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
- }
- mConstructorArgs[0] = lastContext;
- return view;
-
- } catch (NoSuchMethodException e) {
- final InflateException ie = new InflateException(attrs.getPositionDescription()
- + ": Error inflating class " + (prefix != null ? (prefix + name) : name), e);
- ie.setStackTrace(EMPTY_STACK_TRACE);
- throw ie;
-
- } catch (ClassCastException e) {
- // If loaded class is not a View subclass
- final InflateException ie = new InflateException(attrs.getPositionDescription()
- + ": Class is not a View " + (prefix != null ? (prefix + name) : name), e);
- ie.setStackTrace(EMPTY_STACK_TRACE);
- throw ie;
- } catch (ClassNotFoundException e) {
- // If loadClass fails, we should propagate the exception.
- throw e;
- } catch (Exception e) {
- final InflateException ie = new InflateException(
- attrs.getPositionDescription() + ": Error inflating class "
- + (clazz == null ? "
" : clazz.getName()), e); - ie.setStackTrace(EMPTY_STACK_TRACE);
- throw ie;
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
- }
- }
内部实际上就是通过反射创建对应的view实例,然后返回。
- final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
- boolean finishInflate) throws XmlPullParserException, IOException {
- rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
- }
-
- /**
- * Recursive method used to descend down the xml hierarchy and instantiate
- * views, instantiate their children, and then call onFinishInflate().
- *
- * Note: Default visibility so the BridgeInflater can
- * override it.
- */
- void rInflate(XmlPullParser parser, View parent, Context context,
- AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
-
- final int depth = parser.getDepth();
- int type;
- boolean pendingRequestFocus = false;
-
- while (((type = parser.next()) != XmlPullParser.END_TAG ||
- parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
-
- if (type != XmlPullParser.START_TAG) {
- continue;
- }
-
- final String name = parser.getName();
-
- if (TAG_REQUEST_FOCUS.equals(name)) {
- pendingRequestFocus = true;
- consumeChildElements(parser);
- } else if (TAG_TAG.equals(name)) {
- parseViewTag(parser, parent, attrs);
- } else if (TAG_INCLUDE.equals(name)) {
- if (parser.getDepth() == 0) {
- throw new InflateException("
cannot be the root element"); - }
- parseInclude(parser, context, parent, attrs);
- } else if (TAG_MERGE.equals(name)) {
- throw new InflateException("
must be the root element"); - } else {
- final View view = createViewFromTag(parent, name, context, attrs);
- final ViewGroup viewGroup = (ViewGroup) parent;
- final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
- rInflateChildren(parser, view, attrs, true);
- viewGroup.addView(view, params);
- }
- }
-
- if (pendingRequestFocus) {
- parent.restoreDefaultFocus();
- }
-
- if (finishInflate) {
- parent.onFinishInflate();
- }
- }
它内部通过while循环不断地通过 XmlPullParser 判断类型然后,不断去创建view。
注意这个标记:mContentParentExplicitlySet 是“不能设置decorView属性”的一个标记,在 setContentView 完成后会把这个变量标记为true,之后再通过 requestFeature() 设置 window的属性会报错:requestFeature() must be called before adding content 。 如下:
- if (mContentParentExplicitlySet) {
- throw new AndroidRuntimeException("requestFeature() must be called before adding content");
- }
他内部对标记:mContentParentExplicitlySet 做了一个判断。
所以我们平时想要隐藏标题栏需要将:
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
设置到 setContentView()之前的原因。
他和activity有一些相似的地方,所有这里就用正常读源码的方式,倒着去看吧。
首先进入AppCompatActivity的setContentView方法:
- @Override
- public void setContentView(@LayoutRes int layoutResID) {
- getDelegate().setContentView(layoutResID);
- }
getDelegate()就是获得一个 { AppCompatDelegate }类型的变量,关于这个类的描述:
This class represents a delegate which you can use to extend AppCompat's support to any activity.
- public AppCompatDelegate getDelegate() {
- if (mDelegate == null) {
- mDelegate = AppCompatDelegate.create(this, this);
- }
- return mDelegate;
- }
而 AppCompatDelegate 的 create 方法,实际上是实例化了一个他的实现类:AppCompatDeletateImpl:
- /**
- * Create a {@link androidx.appcompat.app.AppCompatDelegate} to use with {@code activity}.
- *
- * @param callback An optional callback for AppCompat specific events
- */
- public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
- return new AppCompatDelegateImpl(activity, activity.getWindow(), callback);
- }
然后进到 AppCompatDelegateImpl 去看 setContentView 方法:
- @Override
- public void setContentView(int resId) {
- ensureSubDecor();
- ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
- contentParent.removeAllViews();
- LayoutInflater.from(mContext).inflate(resId, contentParent);
- mOriginalWindowCallback.onContentChanged();
- }
里面几个主要的方法:
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);
下面分别看着几步:
- private void ensureSubDecor() {
- if (!mSubDecorInstalled) {
- mSubDecor = createSubDecor();
-
- // If a title was set before we installed the decor, propagate it now
- CharSequence title = getTitle();
- if (!TextUtils.isEmpty(title)) {
- if (mDecorContentParent != null) {
- mDecorContentParent.setWindowTitle(title);
- } else if (peekSupportActionBar() != null) {
- peekSupportActionBar().setWindowTitle(title);
- } else if (mTitleView != null) {
- mTitleView.setText(title);
- }
- }
-
- applyFixedSizeWindow();
-
- onSubDecorInstalled(mSubDecor);
-
- mSubDecorInstalled = true;
-
- // Invalidate if the panel menu hasn't been created before this.
- // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
- // being called in the middle of onCreate or similar.
- // A pending invalidation will typically be resolved before the posted message
- // would run normally in order to satisfy instance state restoration.
- PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
- if (!mIsDestroyed && (st == null || st.menu == null)) {
- invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
- }
- }
- }
其中, createSubDecor 代码:
- private ViewGroup createSubDecor() {
- TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
-
- if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
- a.recycle();
- throw new IllegalStateException(
- "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
- }
-
- if (a.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false)) {
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- } else if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBar, false)) {
- // Don't allow an action bar if there is no title.
- requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR);
- }
- if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBarOverlay, false)) {
- requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
- }
- if (a.getBoolean(R.styleable.AppCompatTheme_windowActionModeOverlay, false)) {
- requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY);
- }
- mIsFloating = a.getBoolean(R.styleable.AppCompatTheme_android_windowIsFloating, false);
- a.recycle();
-
- // Now let's make sure that the Window has installed its decor by retrieving it
- mWindow.getDecorView();
-
- final LayoutInflater inflater = LayoutInflater.from(mContext);
- ViewGroup subDecor = null;
-
-
- if (!mWindowNoTitle) {
- if (mIsFloating) {
- // If we're floating, inflate the dialog title decor
- subDecor = (ViewGroup) inflater.inflate(
- R.layout.abc_dialog_title_material, null);
-
- // Floating windows can never have an action bar, reset the flags
- mHasActionBar = mOverlayActionBar = false;
- } else if (mHasActionBar) {
- /**
- * This needs some explanation. As we can not use the android:theme attribute
- * pre-L, we emulate it by manually creating a LayoutInflater using a
- * ContextThemeWrapper pointing to actionBarTheme.
- */
- TypedValue outValue = new TypedValue();
- mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);
-
- Context themedContext;
- if (outValue.resourceId != 0) {
- themedContext = new ContextThemeWrapper(mContext, outValue.resourceId);
- } else {
- themedContext = mContext;
- }
-
- // Now inflate the view using the themed context and set it as the content view
- subDecor = (ViewGroup) LayoutInflater.from(themedContext)
- .inflate(R.layout.abc_screen_toolbar, null);
-
- mDecorContentParent = (DecorContentParent) subDecor
- .findViewById(R.id.decor_content_parent);
- mDecorContentParent.setWindowCallback(getWindowCallback());
-
- /**
- * Propagate features to DecorContentParent
- */
- if (mOverlayActionBar) {
- mDecorContentParent.initFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
- }
- if (mFeatureProgress) {
- mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);
- }
- if (mFeatureIndeterminateProgress) {
- mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
- }
- }
- } else {
- if (mOverlayActionMode) {
- subDecor = (ViewGroup) inflater.inflate(
- R.layout.abc_screen_simple_overlay_action_mode, null);
- } else {
- subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
- }
-
- if (Build.VERSION.SDK_INT >= 21) {
- // If we're running on L or above, we can rely on ViewCompat's
- // setOnApplyWindowInsetsListener
- ViewCompat.setOnApplyWindowInsetsListener(subDecor,
- new OnApplyWindowInsetsListener() {
- @Override
- public WindowInsetsCompat onApplyWindowInsets(View v,
- WindowInsetsCompat insets) {
- final int top = insets.getSystemWindowInsetTop();
- final int newTop = updateStatusGuard(top);
-
- if (top != newTop) {
- insets = insets.replaceSystemWindowInsets(
- insets.getSystemWindowInsetLeft(),
- newTop,
- insets.getSystemWindowInsetRight(),
- insets.getSystemWindowInsetBottom());
- }
-
- // Now apply the insets on our view
- return ViewCompat.onApplyWindowInsets(v, insets);
- }
- });
- } else {
- // Else, we need to use our own FitWindowsViewGroup handling
- ((FitWindowsViewGroup) subDecor).setOnFitSystemWindowsListener(
- new FitWindowsViewGroup.OnFitSystemWindowsListener() {
- @Override
- public void onFitSystemWindows(Rect insets) {
- insets.top = updateStatusGuard(insets.top);
- }
- });
- }
- }
-
- if (subDecor == null) {
- throw new IllegalArgumentException(
- "AppCompat does not support the current theme features: { "
- + "windowActionBar: " + mHasActionBar
- + ", windowActionBarOverlay: "+ mOverlayActionBar
- + ", android:windowIsFloating: " + mIsFloating
- + ", windowActionModeOverlay: " + mOverlayActionMode
- + ", windowNoTitle: " + mWindowNoTitle
- + " }");
- }
-
- if (mDecorContentParent == null) {
- mTitleView = (TextView) subDecor.findViewById(R.id.title);
- }
-
- // Make the decor optionally fit system windows, like the window's decor
- ViewUtils.makeOptionalFitsSystemWindows(subDecor);
-
- final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
- R.id.action_bar_activity_content);
-
- final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
- if (windowContentView != null) {
- // There might be Views already added to the Window's content view so we need to
- // migrate them to our content view
- while (windowContentView.getChildCount() > 0) {
- final View child = windowContentView.getChildAt(0);
- windowContentView.removeViewAt(0);
- contentView.addView(child);
- }
-
- // Change our content FrameLayout to use the android.R.id.content id.
- // Useful for fragments.
- windowContentView.setId(View.NO_ID);
- contentView.setId(android.R.id.content);
-
- // The decorContent may have a foreground drawable set (windowContentOverlay).
- // Remove this as we handle it ourselves
- if (windowContentView instanceof FrameLayout) {
- ((FrameLayout) windowContentView).setForeground(null);
- }
- }
-
- // Now set the Window's content view with the decor
- mWindow.setContentView(subDecor);
-
- contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {
- @Override
- public void onAttachedFromWindow() {}
-
- @Override
- public void onDetachedFromWindow() {
- dismissPopups();
- }
- });
-
- return subDecor;
- }
代码很长,实际就是判断不同的属性 inflate了一个 ViewGroup 类型的 subDecor,然后返回。其中的 mWindow.getDecorView(); ,mWindow仍然是一个PhoneWindow类型,看他的getDecorView方法:
- @Override
- public final View getDecorView() {
- if (mDecor == null || mForceDecorInstall) {
- installDecor();
- }
- return mDecor;
- }
内部执行了 installDecor:
- private void installDecor() {
- mForceDecorInstall = false;
- if (mDecor == null) {
- mDecor = generateDecor(-1);
- mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
- mDecor.setIsRootNamespace(true);
- if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
- mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
- }
- } else {
- mDecor.setWindow(this);
- }
- if (mContentParent == null) {
- mContentParent = generateLayout(mDecor);
-
- // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
- mDecor.makeOptionalFitsSystemWindows();
-
- final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
- R.id.decor_content_parent);
-
- if (decorContentParent != null) {
- mDecorContentParent = decorContentParent;
- mDecorContentParent.setWindowCallback(getCallback());
- if (mDecorContentParent.getTitle() == null) {
- mDecorContentParent.setWindowTitle(mTitle);
- }
-
- final int localFeatures = getLocalFeatures();
- for (int i = 0; i < FEATURE_MAX; i++) {
- if ((localFeatures & (1 << i)) != 0) {
- mDecorContentParent.initFeature(i);
- }
- }
-
- mDecorContentParent.setUiOptions(mUiOptions);
-
- if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 ||
- (mIconRes != 0 && !mDecorContentParent.hasIcon())) {
- mDecorContentParent.setIcon(mIconRes);
- } else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 &&
- mIconRes == 0 && !mDecorContentParent.hasIcon()) {
- mDecorContentParent.setIcon(
- getContext().getPackageManager().getDefaultActivityIcon());
- mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;
- }
- if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 ||
- (mLogoRes != 0 && !mDecorContentParent.hasLogo())) {
- mDecorContentParent.setLogo(mLogoRes);
- }
-
- // Invalidate if the panel menu hasn't been created before this.
- // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
- // being called in the middle of onCreate or similar.
- // A pending invalidation will typically be resolved before the posted message
- // would run normally in order to satisfy instance state restoration.
- PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
- if (!isDestroyed() && (st == null || st.menu == null) && !mIsStartingWindow) {
- invalidatePanelMenu(FEATURE_ACTION_BAR);
- }
- } else {
- mTitleView = findViewById(R.id.title);
- if (mTitleView != null) {
- if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
- final View titleContainer = findViewById(R.id.title_container);
- if (titleContainer != null) {
- titleContainer.setVisibility(View.GONE);
- } else {
- mTitleView.setVisibility(View.GONE);
- }
- mContentParent.setForeground(null);
- } else {
- mTitleView.setText(mTitle);
- }
- }
- }
- if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
- mDecor.setBackgroundFallback(mBackgroundFallbackResource);
- }
- // Only inflate or create a new TransitionManager if the caller hasn't
- // already set a custom one.
- if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) {
- if (mTransitionManager == null) {
- final int transitionRes = getWindowStyle().getResourceId(
- R.styleable.Window_windowContentTransitionManager,
- 0);
- if (transitionRes != 0) {
- final TransitionInflater inflater = TransitionInflater.from(getContext());
- mTransitionManager = inflater.inflateTransitionManager(transitionRes,
- mContentParent);
- } else {
- mTransitionManager = new TransitionManager();
- }
- }
-
- mEnterTransition = getTransition(mEnterTransition, null,
- R.styleable.Window_windowEnterTransition);
- mReturnTransition = getTransition(mReturnTransition, USE_DEFAULT_TRANSITION,
- R.styleable.Window_windowReturnTransition);
- mExitTransition = getTransition(mExitTransition, null,
- R.styleable.Window_windowExitTransition);
- mReenterTransition = getTransition(mReenterTransition, USE_DEFAULT_TRANSITION,
- R.styleable.Window_windowReenterTransition);
- mSharedElementEnterTransition = getTransition(mSharedElementEnterTransition, null,
- R.styleable.Window_windowSharedElementEnterTransition);
- mSharedElementReturnTransition = getTransition(mSharedElementReturnTransition,
- USE_DEFAULT_TRANSITION,
- R.styleable.Window_windowSharedElementReturnTransition);
- mSharedElementExitTransition = getTransition(mSharedElementExitTransition, null,
- R.styleable.Window_windowSharedElementExitTransition);
- mSharedElementReenterTransition = getTransition(mSharedElementReenterTransition,
- USE_DEFAULT_TRANSITION,
- R.styleable.Window_windowSharedElementReenterTransition);
- if (mAllowEnterTransitionOverlap == null) {
- mAllowEnterTransitionOverlap = getWindowStyle().getBoolean(
- R.styleable.Window_windowAllowEnterTransitionOverlap, true);
- }
- if (mAllowReturnTransitionOverlap == null) {
- mAllowReturnTransitionOverlap = getWindowStyle().getBoolean(
- R.styleable.Window_windowAllowReturnTransitionOverlap, true);
- }
- if (mBackgroundFadeDurationMillis < 0) {
- mBackgroundFadeDurationMillis = getWindowStyle().getInteger(
- R.styleable.Window_windowTransitionBackgroundFadeDuration,
- DEFAULT_BACKGROUND_FADE_DURATION_MS);
- }
- if (mSharedElementsUseOverlay == null) {
- mSharedElementsUseOverlay = getWindowStyle().getBoolean(
- R.styleable.Window_windowSharedElementsUseOverlay, true);
- }
- }
- }
- }
到这里,几乎就和activity一样了。