在日常开发中,相信不少小伙伴都会有这样一个疑问,我们所写的UI控,Android系统是怎么帮我们绘制到屏幕上的呢?
下面将会和大家一起,一步步的去揭开View绘制流程的神秘面纱。
首先,来看setContentView方法,这个方法应该也是用的最多的方法之一,每次在写好Activity之后,都会调用这个方法,将布局文件或者View传进去。在Activity里点击setContentView方法,来到了AppCompatActivity类
@Override
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}
@Override
public void setContentView(View view) {
getDelegate().setContentView(view);
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
getDelegate().setContentView(view, params);
}
这里有几个setContentView的重载方法,里面分别都有 getDelegate的setContentView实现,继续看getDelegate的setContentView方法,getDelegate返回的是AppCompatDelegate的实例,而AppCompatDelegate是一个抽象类,返回的是实现类的实例
//AppCompatDelegate.java
@NonNull
public static AppCompatDelegate create(@NonNull Activity activity,
@Nullable AppCompatCallback callback) {
return new AppCompatDelegateImpl(activity, callback);
}
//AppCompatDelegateImpl.java
@Override
public void setContentView(int resId) {
ensureSubDecor();
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mAppCompatWindowCallback.getWrapped().onContentChanged();
}
通过这段代码,可以看到,是将resId加载到了contentParent上,而contentParent是android.R.id.content,那么mSubDecor又是什么呢?在ensureSubDecor方法里看到
private void ensureSubDecor() {
mSubDecor = createSubDecor();
}
接着看createSubDecor
private ViewGroup createSubDecor() {
...
ViewGroup subDecor = null;
...
mWindow.setContentView(subDecor);
...
return subDecor;
}
通过上面的代码,我们可以看出mSubDecor是一个ViewGroup,并且被填充到了mWindow里,而Window作为Activity视图的根窗口,它的setContentView里又做了什么呢?Window同样是一个抽象类,我们来看源码里的唯一实现类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) {
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;
}
通过上面一段代码,可以看到传进来的view被加载到了mContentParent里,而mContentParent是一个ViewGroup,并且源码里加了详细的注释
// This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
ViewGroup mContentParent;
实现代码
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
private void installDecor() {
...
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
}
...
}
看到这里的时候,整个Activity的UI层级关系就比较明朗了,第一层级PhoneWindow(主要和WindowMananger交互), 第二层级DecorView(负责View的测量、定位和绘制),第三层级就是我们加入的ContentView。网络上有很多图片,这里就不贴图了哈
@Override
public void addView(View child, LayoutParams params) {
addView(child, -1, params);
}