setContentView源码分析
基于
AppCompatDelegateImplV9
找到切入点
public class MainActivity extends AppCompatActivity {
private GLPanorama mGLPanorama;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//分析这里的源码
setContentView(R.layout.activity_main);
}
}
点击setContentView来到AppCompatActivity类
@Override
public void setContentView(@LayoutRes int layoutResID) {
//获取代理实现类
getDelegate().setContentView(layoutResID);
}
public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
return create(activity, activity.getWindow(), callback);
}
public static AppCompatDelegate create(Dialog dialog, AppCompatCallback callback) {
return create(dialog.getContext(), dialog.getWindow(), callback);
}
/*
* 这里是重点,通过判断sdk版本来加载不同的实现类
* 我们会点击每个实现类会发现它们最终都继承了AppCompatDelegateImplV9类
*/
private static AppCompatDelegate create(Context context, Window window,
AppCompatCallback callback) {
if (Build.VERSION.SDK_INT >= 24) {
return new AppCompatDelegateImplN(context, window, callback);
} else if (Build.VERSION.SDK_INT >= 23) {
return new AppCompatDelegateImplV23(context, window, callback);
} else if (Build.VERSION.SDK_INT >= 14) {
return new AppCompatDelegateImplV14(context, window, callback);
} else if (Build.VERSION.SDK_INT >= 11) {
return new AppCompatDelegateImplV11(context, window, callback);
} else {
return new AppCompatDelegateImplV9(context, window, callback);
}
}
我们直接来到AppCompatDelegateImplV9类
@Override
public void setContentView(View v) {
ensureSubDecor();
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
contentParent.addView(v);
mOriginalWindowCallback.onContentChanged();
}
/*
* 因为我们在外部传入的是id,所以会走到这个方法
*/
@Override
public void setContentView(int resId) {
//这里是入口
ensureSubDecor();
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
//把我们的布局放到contentParent里面
LayoutInflater.from(mContext).inflate(resId, contentParent);
mOriginalWindowCallback.onContentChanged();
}
@Override
public void setContentView(View v, ViewGroup.LayoutParams lp) {
ensureSubDecor();
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
contentParent.addView(v, lp);
mOriginalWindowCallback.onContentChanged();
}
private void ensureSubDecor() {
//因为我们在onCreate中调用setContentView,所以这里的mSubDecorInstalled为false
if (!mSubDecorInstalled) {
//这里去进行创建我们的根布局
mSubDecor = createSubDecor();
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);
}
}
}
private ViewGroup createSubDecor() {
TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
a.recycle();
//如果不设置AppCompat主题,这里就会抛出异常
throw new IllegalStateException(
"You need to use a Theme.AppCompat theme (or descendant) with this activity.");
}
//初始化相关特征标志
if (a.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false)) {
//一般我们的主题默认都是NoTitle
//这里我们就会发现为什么我们设置activity全屏需要在setContentView之前
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();
//通过PhoneWindow创建DecorView
mWindow.getDecorView();
final LayoutInflater inflater = LayoutInflater.from(mContext);
ViewGroup subDecor = null;
if (!mWindowNoTitle) {
...因为是NoTitle主题,所以不会进入这里
} else {
//这里去创建我们的subDecor布局
if (mOverlayActionMode) {
///调用了requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY)会走进来
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 (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);
}
ViewUtils.makeOptionalFitsSystemWindows(subDecor);
//这里的contentView就是我们编写的布局的父布局,我们从下面的代码可以看出来。
final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
R.id.action_bar_activity_content);
//这里的windowContentView是我们原来的布局的父容器
final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
if (windowContentView != null) {
//在这里对原来的windowContentView里面的布局进行替换给contentView
while (windowContentView.getChildCount() > 0) {
final View child = windowContentView.getChildAt(0);
windowContentView.removeViewAt(0);
contentView.addView(child);
}
//将原来windowContentView的id由原来的android.R.id.content设置为View.NO_ID
windowContentView.setId(View.NO_ID);
//这里将contentView的id设置为android.R.id.content
contentView.setId(android.R.id.content);
if (windowContentView instanceof FrameLayout) {
((FrameLayout) windowContentView).setForeground(null);
}
}
//在这里就将我们的subDecor添加给了PhoneWindow
mWindow.setContentView(subDecor);
contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {
@Override
public void onAttachedFromWindow() {}
@Override
public void onDetachedFromWindow() {
dismissPopups();
}
});
return subDecor;
}
我们来到PhoneWindow这个类
@Override
public void setContentView(View view) {
setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
/*
* 我们主要看这个方法
*/
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
if (mContentParent == null) {
//进行DecorView的初始化
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
//是否有transitions动画。没有,进入else
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
} else {
//重要!!将这个subDecor添加到这个mContentParent里面了
//mContentParent是FrameLayout,在之前设置的View.NO_ID
mContentParent.addView(view, params);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
private void installDecor() {
if (mDecor == null) {
//创建DecorView
mDecor = generateDecor(-1);
...
}
...
if (mContentParent == null) {
//将DecorView传入进行进行mContentParent的初始化一系列操作
mContentParent = generateLayout(mDecor);
....
}
}
generateDecor(-1)
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();
}
return new DecorView(context, featureId, this, getAttributes());
}
generateLayout(mDecor)
protected ViewGroup generateLayout(DecorView decor) {
TypedArray a = getWindowStyle();
//设置一堆标志位...
...
mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
if (!mForcedStatusBarColor) {
//获取主题状态栏的颜色
mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);
}
if (!mForcedNavigationBarColor) {
//获取底部NavigationBar颜色
mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);
}
//获取主题一些资源
...
int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
...我们设置不同的主题以及样式,会采用不同的布局文件...
} else {
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple;
// System.out.println("Simple!");
}
mDecor.startChanging();
//把screen_simple放到了DecorView里面
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
...
return contentParent;
}
走到这里我们就知道了Actiity的setContentView的大部分的主要流程,创建DecorView,设置DecorView的ContentView,并将我们AppCompatDelegateImplV9中创建的subDecor里面的contentView的id替换成DecorView的ContentView的id。接下来就该将我们的布局添加至AppCompatDelegateImplV9中创建的subDecor的contentView中。
@Override
public void setContentView(int resId) {
ensureSubDecor();
//找到SubDecor中的contentView
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
//将我们的布局添加到contentParent中
LayoutInflater.from(mContext).inflate(resId, contentParent);
mOriginalWindowCallback.onContentChanged();
}
从这里我们就会发现MainActivity.setContentView()->AppCompatDelegateImplV9.setContentView()->ensureSubDecor()->createSubDecor()->PhoneWindow.setContentView()->installDecor()->generateDecor()->generateLayout()->mContentParent.addView(view, params)->AppCompatDelegateImplV9.subDecor.contentView.addView()
网友评论