view的工作流程:measure ,layout,draw
其中measure是用来测量view的宽和高, layout 用来确定view的位置,draw则用来绘制view。
(1)首先了解Activity的构成
首先会调用setContentView函数来加载布局,具体实现为
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
这里调用了getWindow().setContentView(ID),那么getWindow指的是什么?
public Window getWindow() {
return mWindow;
}
mWindow是在Activity的attach函数中进行初始化的
final void attach(Context context, ActivityThread aThread,参数省略) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window);
所以getWindow指的是PhoneWindow,继续查看PhoneWindow的setContentView
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();
}
.......
}
mContentParent 为ViewGroup类型,第一次会调用installDecor()函数
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
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);
会初始化mDecor 和 mContentParent,本次主要关注mContentParent
会调用generateDecor函数
protected DecorView generateDecor(int featureId) {
.......
return new DecorView(context, featureId, this, getAttributes());
}
在这里会创建一个DecorView,这个DecorView就是Activity中的根View
而generateLayout方法主要是根据情况加载不同的布局给layoutResource
上述创建了DecorView,但是它的内容还无法显示,因为还没有加载到Window中
当我们调用startActivity方法时,最终会调用ActivityThread的handleLaunchActivity方法来创建Activity的
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
Activity a = performLaunchActivity(r, customIntent); //1
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason); //2
注释1:调用performLaunchActivity方法来创建Activity,在这里会调用到Activity的onCreate方法,从而完成DecorView的创建
注释2:handleResumeActivity方法
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token); //1
if (r != null) {
final Activity a = r.activity;
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView(); //2
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager(); //3
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l); //4
}
注释2:得到了DecorView
注释3:得到了WindowManager,WindowManger是一个接口并且继承了ViewManager,它的实现类是WindowManagerImpl
注释4:调用了WindowManager的addView方法
查看WindowManagerImpl.addView方法
public final class WindowManagerImpl implements WindowManager {
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
又调用了WindowManagerGlobal的addView方法
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
....
ViewRootImpl root;
View panelParentView = null;
root = new ViewRootImpl(view.getContext(), display); //1
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
root.setView(view, wparams, panelParentView); //2
注释1:创建一个ViewRootImpl实例
注释2:调用了ViewRootImpl的setView方法并将DecorView作为参数传进去,这样就把DecoreView加载到Window中,但是界面仍然不会显示出来,因为view的工作流程还每执行完成 , 需要经过measure ,layout ,draw彩绘把view绘制出
是通过ViewRootImpl的performTraveals函数,这个方法使ViewTree开始view的工作流程
这里面主要执行了3个方法,分别是performMeasure、performLayout和performDraw,在其方法的内部又会分别调用View的measure、layout和draw方法。同时这里还会调用view的measure方法(进行测量)
performMeasure需要传入两个参数,需要了解MeasureSpec
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
(2)理解MeasureSpec
MeasureSpec是View 的内部类,其封装了View的规格尺寸,包括View的宽高,它的作用是在measure流程中,系统会将View的layoutParams根据父容器所施加的规则转换成对应的MeasureSpec,然后在onMeasure方法中根据这个MeasureSpec来确定View的宽高
MeasureSpec代表32为,其中前两位代表specMode,后30位代表SpecSize
SpecMode:表示测量模式
SpecSize : 表示测量大小