DecorView的创建
当我们创建一个Activity时,会通过setContentView来设置我们自定义的布局文件
1. onCreate(Nullable Bundle savedInstanceState)
创建Activity的onCreate方法setContentView设置布局资源
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//R.layout.activity_main就是我们自定义的布局文件
setContentView(R.layout.activity_main);
}
2. Activity#setContent(@LayoutRes int layoutResID)
public void setContentView(@LayoutRes int layoutResID) {
//getWindow()返回的对象是PhoneWindow,PhoneWindow是Window的唯一实现类
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
3. PhoneWindow#setContentView(int layoutResID)
初始化DecorView和获取mContentParent,我们就是在mContentParent里添加自定义布局
@Override
public void setContentView(int layoutResID) {
//mContentParent是DecorView的子View,是一个id为R.id.content的FrameLayout
if (mContentParent == null) {
//初始化创建DecorView,mContentParent在installDecor方法内获取
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
//移除所有子View,也就是我们自定义布局
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
//将我们自定义的布局文件解析后添加到mContentParent
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
4. PhoneWindow#installDecor()
初始化DecorView和获取mContentParent
// 创建DecorView
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
//生成mDecor
//generateDecor直接实例化返回一个DecorView
mDecor = generateDecor(-1);
// setDescendantFocusability的参数值:
// FOCUS_BEFORE_DESCENDANTS: 父布局会优先子布局获取焦点
// FOCUS_AFTER_DESCENDANTS :只有当子布局不需要获取焦点时,父布局才获取焦点
// FOCUS_BLOCK_DESCENDANTS : 覆盖子布局直接由父布局获取焦点
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
// 设置是否root节点
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
//将PhoneWindow设置到DecorView中
mDecor.setWindow(this);
}
if (mContentParent == null) {
//从DecorView获取mContentParent
mContentParent = generateLayout(mDecor);
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeOptionalFitsSystemWindows();
// 省略代码...
if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
mDecor.setBackgroundFallback(mBackgroundFallbackResource);
}
// 省略代码...
}
}
5. PhoneWindow#generateDecor(int featureId)
创建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());
if (mTheme != -1) {
context.setTheme(mTheme);
}
}
} else {
context = getContext();
}
//构造DecorView
return new DecorView(context, featureId, this, getAttributes());
}
6. PhoneWindow#generateLayout(DecorView decor)
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
// 获取Theme属性
TypedArray a = getWindowStyle();
// 省略解析属性代码...
// 更具features获取匹配的layoutResource布局文件,该布局文件包含了id为content的布局
// 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) {
//如果存在FEATURE_LEFT_ICON或FEATURE_RIGHT_ICON
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();
//将layoutResource布局文件解析并且添加到DecorView
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
//之后findViewById获取id为content的ViewGroup
ViewGroup contentParent = (ViewGroup) findViewById(ID_ANDROID_CONTENT);
//如果为null直接报错,所以layoutResource资源中必须包含id为content的View
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
//省略部分代码...
mDecor.finishChanging();
//返回contentParent,mContentParent就获取完成
return contentParent;
}
总结
setContentView的流程大致走完,主要完成的以下几项
- 初始化创建DecorView
- 根据features选择合适的layoutResource,再解析添加到DecorView
- 通过findViewById获取id为content的ViewGroup,也就是mContentParent
- 把自定义的布局文件添加到mContentParent
这样就完成DecorView的创建流程大致就以上几个步骤,该流程主要创建了Activty已DecorView为根节点的View树,还未执行View的measure,layout和draw三大步骤。
网友评论