基于 Android API 26 Platform 源码
写作背景
在上一篇探究Android View 绘制流程,Xml 文件到 View 对象的转换过程我们了解了setContentView(resId) 如何把 xml 文件转换成 Java 中的 View 对象。本篇文章在此基础上继续探究,View 是如何展示到 Activity 上的。
很多 Android 开发者都知道一个事情
当 Activity 执行 onResume() 方法后,代表 Activity 显示到前台
这句话很短,但是背后隐藏了多少方法的调用呢?下面我们将一层一层的剥开源码寻找真相。
onion.jpg
先从 setContentView(resId) 入手
先说明一下,从 Android 的 Launcher 上点击应用的 Icon 的启动过程比较复杂,本人仍在学习。如果想了解如何启动一个 Activity 的过程可以参考Android Launcher 启动 Activity 的工作过程,这里我们只从关注 Activity 中的 View 显示出来。所以直接从 Activity 的一些方法入手。
在 Activity 的 onCreate(savedInstanceState) 中调用 setContentView(resId),而setContentView(resId)则会调用 PhoneWindow.setContentView(layoutResID)
源码并不是太长
@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();
}
}
这里忽略转场动画和一些回调相关的逻辑代码后如下
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
mContentParent.requestApplyInsets();
其中 mContentParent 是一个 ViewGroup 引用
private ViewGroup mContentParent;
这样开代码比较简单明了
1. 判断 mContentParent 是否为空,如果为空执行 installDecor()
2. 如果 mContentParent 不为空,清除 mContentParent 的所有子 View
3. 把传入的布局文件转换为 View 对象添加到 mContentParent
分析 installDecor()
然后我们再看下 installDecor() ,因为源码比较长,我们分成几个部分解读
第一部分
if (mDecor == null) {
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
这几行代码最重要的是调用了方法 generateDecor() 其实就是创建一个 DecorView。这里是不是能想到探究Android View 绘制流程,Canvas 的由来中最后的那张图,我们做个类似的截图截个图
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new TextView(getApplicationContext()));
}
@Override
protected void onResume() {
super.onResume();
}
}
activity_view_01.png
我们看到一个 Activity 页面最底层的 View 就是我们刚看到的 DecorView
第二部分
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
……
}
这里看到了对 mContentParent 的赋值操作,调用了 generateLayout(mDecor)
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
TypedArray a = getWindowStyle();
//设置 Windows Style ,title 、action_bar 、设置键盘弹出方式之类的属性
//……
//……
int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features &