Activity的setContentView渲染的原理
Posted 花姓-老花
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Activity的setContentView渲染的原理相关的知识,希望对你有一定的参考价值。
通过源码得知Activity、PhoneWindow和Window之间的关系(API25)
PhoneWindow是唯一实现Window的具体实现类,在PhoneWindow中有一个DecorView对象
DecorView对象是所有应用窗口(即Activity界面)的根View,DecorView是FrameLayout的子类,对FrameLayout进行装饰或渲染,是所有应用窗口的根View
setContentView
Activity中setContentView直接调用getWindow().setContentView(),PhoneWindow又是Window的实现类,所以就来具体看PhoneWindow的setContentView方法
setContentView(int layoutResID)源码
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) //mContentParent是放置窗口内容(Activity界面)的视图,如果mContentParent为空
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;
上面知道,如果mContentParent为空时,就调用installDecor来生成mContentParent的View(即是Activity界面的根视图)
加载完布局视图后,最后onContentChanged方法,发现在PhoneWindow并没重写Window的这个方法,onContentChange方法在Window中是空方法
public void onContentChanged();
当Activity的布局改动时,即setContentView()或者addContentView()方法执行完毕时就会调用该方法。
private void installDecor()
mForceDecorInstall = false;
if (mDecor == null) //mDecor是个顶层视图
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 = (TextView) 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);
.......
上面知道,如果mContentParent 为空,通过generateLayout(mDecor)进行赋值,参数mDecor是Activity顶层布局(也可以理解为一个ViewGroup)视图
protected ViewGroup generateLayout(DecorView decor)
// Apply data from current theme.
TypedArray a = getWindowStyle(); //mCurrentParent(ViewGroup)装饰的风格
.....
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 && ActivityManager.isHighEndGfx())
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;
.....
// Inflate the window decor.
int layoutResource; //布局xml资源
int features = getLocalFeatures(); //得到此窗口正常工作的特性。给出了requestfeature()集,被处理的只有这个窗口本身,而不它的容器
......
mDecor.startChanging(); //标记顶层DecorView开始改变
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); //得到上面设置的xml Layout资源,然后添加到DecorView根视图上
ViewGroup contentParent = (ViewGroup)findViewById(ID_android_CONTENT);
if (contentParent == null)
throw new RuntimeException("Window couldn't find content container view");
....... //DecorView相关属性设置
mDecor.finishChanging();
return contentParent;
上面主要根据窗口风格类型选择不窗口的根布局文件,mDecor做为根视图将该窗口根布局添加进去,然后获取id为ID_ANDROID_CONTENT,然后返回给mContentParent对象,从上面就知道installDecor方法实质就是产生mDecor和mContentParent对象
在Activity中设置标题状态(feature)或者相关主题(theme),有两种配置方式
1、通过xml形式android:theme属性,通过这里的getWindowStyle()获取的
2、通过代码形式requestWindowFeature(),在这里通过getLocalFeature()获取的
设置Activity的属性时必须在setContentView方法之前调用requestFeature()方法的原因
setContentView方法工作流程:
Activity的setContentView主要是把Activity布局文件(xml或代码形式)添加到顶层视图上
1、先创建一个DecorView对象mDecor作为所有应用窗口(即Activity界面)的根视图
2、根据不同Feature等风格选择不同窗口修饰布局文件
3、将Activity布局文件添加到mContentParent上
public void setContentView(int layoutResID)
......
if (hasFeature(FEATURE_CONTENT_TRANSITIONS))
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
else
mLayoutInflater.inflate(layoutResID, mContentParent);
......
以上是关于Activity的setContentView渲染的原理的主要内容,如果未能解决你的问题,请参考以下文章
setContentView()给当前Activity加载布局出错
Android-Activity中setContentView流程解析
Activity setContentView背后的一系列源码分析
Activity setContentView背后的一系列源码分析