本片文章讲解:UI绘制流程-源码讲解(基于API28的源码分析)
文章大纲:
part1:View是如何被添加到屏幕窗口上
part2:View的绘制流程
本片先讲part1:View是如何被添加到屏幕窗口上,part2:放在下一篇文章讲解。
part1:View是如何被添加到屏幕窗口上
首先,MainActivity中调用了setContentView(R.layout.activity_main);方法,我们跟进去,来到了Activity的源码当中:
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback{
//省略....
/**
* Set the activity content from a layout resource. The resource will be
* inflated, adding all top-level views to the activity.
*
* @param layoutResID Resource ID to be inflated.
*
* @see #setContentView(android.view.View)
* @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
*/
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
//省略....
}
其中getWindow()方法返回了Window对象:
class Activity{
/**
* Retrieve the current {@link android.view.Window} for the activity.
* This can be used to directly access parts of the Window API that
* are not available through Activity/Screen.
*
* @return Window The current window, or null if the activity is not
* visual.
*/
public Window getWindow() {
return mWindow;
}
}
继续查找Window这个类,发现类注释上面有这样一段话:
/**
* Abstract base class for a top-level window look and behavior policy. An
* instance of this class should be used as the top-level view added to the
* window manager. It provides standard UI policies such as a background, title
* area, default key processing, etc.
*
* <p>The only existing implementation of this abstract class is
* android.view.PhoneWindow, which you should instantiate when needing a
* Window.
*/
public abstract class Window {
//...
}
The only existing implementation of this abstract class is android.view.PhoneWindow,也就是说PhoneWindow是Window类的唯一继承实现。那么我们可以找到PhoneWindow类的setContentView方法了:
public class PhoneWindow extends Window implements MenuBuilder.Callback {
//....
@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();
}
}
//....
}
在PhoneWindow类中有两个比较重要的方法installDecor()和mLayoutInflater.inflate(layoutResID, mContentParent)。接下来我们分别说一下这个两个方法的作用:
installDecor方法:
class PhoneWinow{
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();//注意这里 方法①
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);//注意这里 方法②
}
}
}
这里先提一句,就是在installDecor方法里面有一个mContentParent = generateLayout(mDecor);这里给mContentParent赋值之后,回到setContentView方法中也有一个mContentParent对象,调用的方法为 mLayoutInflater.inflate(layoutResID, mContentParent),这两个mContentParent是同一个对象。
然后我们跟进到generateDecor方法里面:
class PhoneWindow{
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();
}
return new DecorView(context, featu