Android-setContentView做的一些事

从MainActivity的setContentView进入根据源码追踪找到AppCompatDelegate的实现类AppCompatDelegateImplV9,从AppCompatDelegateImplV9#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();
}

将我们MainActivity#setContentView的resId通过LayoutInflater.from(mContext).inflate(resId, contentParent);实例化,添加到界面;
其中mSubDecor则是调用我们AppCompatDelegateImplV9#ensureSubDecor()创建出来的

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)) {
             onTitleChanged(title);
         }
         applyFixedSizeWindow();
         onSubDecorInstalled(mSubDecor);
         mSubDecorInstalled = true;
         PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
         if (!isDestroyed() && (st == null || st.menu == null)) {
             invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
         }
     }
 }

mSubDecorInstalled先判断了是否有这个布局
其中mSubDecor在mSubDecor = createSubDecor()时候创建实例化

private ViewGroup createSubDecor() {
	// 省略部分代码,都是一些属性的判断

	// 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;
	
	// 省略部分代码,对各种情况进行判断,对subDecor进行实例化,如果到这步还是为空就抛出一个异常
	
	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
						+ " }");
	}

	// 省略部分代码,都是通过 subDecor 实例化的布局

	// 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;
}

AppCompatDelegateImplV9#createSubDecor()中先判断了一系列的属性,然后就
调用mWindow.getDecorView();判断DecorView是否已经实例化,最后调用了mWindow.setContentView(subDecor); 将subDecor设置进去;
Window是一个抽象类,根据说明可以知道PhoneWindow实例化了继承自这个类,所以可以查看
PhoneWindow#getDecorView(),在这个方法里面做了调用PhoneWindow#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);
				
		// 省略部分代码,都是一些属性的判断和控件实例化
	}
}

沿着里面往下走可以知道DecorView是由mDecor = generateDecor(-1)创建生成的;
在PhoneWindow#generateDecor()中通过return new DecorView(context, featureId, this, getAttributes());创建一个DecorView

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().getResources());
			if (mTheme != -1) {
				context.setTheme(mTheme);
			}
		}
	} else {
		context = getContext();
	}
	// 最后创建一个DecorView返回
	return new DecorView(context, featureId, this, getAttributes());
}

回到AppCompatDelegateImplV9#createSubDecor()中,接着mWindow.getDecorView();往下走,看到mWindow.setContentView(subDecor); 这里也可以继续跳到PhoneWindow处搜索PhoneWindow#setContentView()

@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
	// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
	// decor, when theme attributes and the like are crystalized. Do not check the feature
	// before this happens.
	if (mContentParent == null) {
		installDecor();
	} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
		mContentParent.removeAllViews();
	}

	if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
		view.setLayoutParams(params);
		final Scene newScene = new Scene(mContentParent, view);
		transitionTo(newScene);
	} else {
		mContentParent.addView(view, params);
	}
	mContentParent.requestApplyInsets();
	final Callback cb = getCallback();
	if (cb != null && !isDestroyed()) {
		cb.onContentChanged();
	}
	mContentParentExplicitlySet = true;
}

在PhoneWindow#setContentView()中,一开始的mSubDecor通过mContentParent.addView(view, params);添加到mContentParent中
mContentParent是在PhoneWindow#installDecor()里面在DecorView之后创建出来的并且通过PhoneWindow#generateLayout(DecorView decor)方法创建出来并添加至DecorView里面

protected ViewGroup generateLayout(DecorView decor) {
	
	// 省略部分代码,都是判断一些主题和属性

	// 开始将控件添加入DecorView
	mDecor.startChanging();
	mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
	
	// 实例化mContentParent,其中findViewById(ID_ANDROID_CONTENT)调用的是Window这个类里面的findViewById,
	// 在里面通过getDecorView().findViewById(id)进行赋值,getDecorView()就是PhoneWindow的DecorView
	
	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();
	}
	// 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;
}

在PhoneWindow#generateLayout(DecorView decor)里面通过
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
实例化mContentParent,其中findViewById(ID_ANDROID_CONTENT)调用的是Window这个类里面的findViewById,在Window里面Window#findViewById()通过getDecorView().findViewById(id)进行实例化,getDecorView()就是PhoneWindow的DecorView
整个的setContentView结构大致如下图(图片是借用网络上一个哥们的)
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_34829270/article/details/84292211