[TOC]

概念

DecorView: 该类继承自FrameLayout,实现了RootViewSurfaceTaker, WindowCallbacks接口。它是所有应用窗口的根View,PhoneWindow设置DecorView为应用窗口的根视图。

PhoneWindow: 该类在setContentView时,帮我们创建了一个DecorView(父类为FrameLayout)窗口顶层视图

ViewRootImpl: 是连接WindowManager与DecorView的纽带,View的整个绘制流程的三大步(measure、layout、draw)以及我们一些addView()的操作,都是通过ViewRootImpl完成的。

WindowManager: 应用程序界面和窗口管理器

源码分析

Activity的的setContentView方法调用,首先进入installDecor方法,进行decorView的创建。

@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) {
//这里开始创建decorview
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;
}

generateDecor方法为decorview的主要创建方法,参数为featureId,默认值为-1;

private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
//decorview的主要创建方法
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);
//...
}
}

generateDecor方法主要是对decorview的上下文进行创建,当拿到context、featureId、phonewindow以及windowmanage的布局参数后,便开始真正的new 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, this);
if (mTheme != -1) {
context.setTheme(mTheme);
}
}
} else {
context = getContext();
}
return new DecorView(context, featureId, this, getAttributes());
}

DecorView的构造方法

    DecorView(Context context, int featureId, PhoneWindow window,
WindowManager.LayoutParams params) {
//...
updateAvailableWidth();

setWindow(window);

updateLogTag(params);

//...
}

setWindow方法设置DecorView的mWindow变量,若当前上下文是DecorContext,也更新decorContext的mPhoneWindow值。

    void setWindow(PhoneWindow phoneWindow) {
mWindow = phoneWindow;
Context context = getContext();
if (context instanceof DecorContext) {
DecorContext decorContext = (DecorContext) context;
decorContext.setPhoneWindow(mWindow);
}
//...
}

DecorContext.java中,设置mPhoneWindow的值,同时从phoneWindow中获取上下文,取得context的弱引用。

void setPhoneWindow(PhoneWindow phoneWindow) {
mPhoneWindow = phoneWindow;
final Context context = phoneWindow.getContext();
mContext = new WeakReference<>(context);
mResources = context.getResources();
}

流程图

image-20210319102418129

补充

View 提供的获取坐标方法

getTop: View 自身的顶边到父View顶边的距离

getLeft: View 自身的左边到父View左边的距离

getRight: View 自身的右边到父View左边的距离

getBottom: View 自身的底边到父View上边的距离

MotionEvent 提供的获取坐标方法

getX : 触摸点到当前控件左边缘的距离

getY : 触摸点当前控件顶边缘的距离

getRawX : 触摸点屏幕左边缘的距离

getRawY : 触摸点到屏幕顶边缘的距离

MeasureSpec的三种类型

/** @hide */
@IntDef({UNSPECIFIED, EXACTLY, AT_MOST})
@Retention(RetentionPolicy.SOURCE)
public @interface MeasureSpecMode {}

UNSPECIFIED:父级没有对子级施加任何约束。

EXACTLY:父级已确定子级的确切大小

AT_MOST:子级可以根据需要的大小而定,最大可以达到指定的大小。

onFinishInflate 调用时机

setContentView > onFinishInflate > view绘制流程(performMeasure、performLayout、performDraw)