view作为构成android界面的基本元素,深入了解view的绘制流程对开发人员来说是很有必要的。
我们创建一个Activity都会在onCreate方法中写setContentView(layoutResId),把我们的布局传进去,那此时我们的布局就添加到屏幕上了吗?当然不是的。那我们的这个布局是在什么时候添加进屏幕的呢?
在ActivityThread.java 的handleResumeActivity中
java
//ActivityThread.java
@Override
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,boolean
isForward, String reason) {
//...
wm.addView(decor, l);
//...
}
这里调用wm的addView,才把DecorView添加到窗口,wm其实就是WindowManagerImpl,接着我们跟进到WindowManagerImpl的addView
java
//WindowManagerImpl.java
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyTokens(params);
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}
这里调用的是mGlobal的addView方法,mGlobal其实就是WindowManagerGlobal,是管理整个进程所有窗口信息的,接下来看看WindowManagerGlobal的addView方法
java
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
//...
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
ViewRootImpl root;
if (windowlessSession == null) {
root = new ViewRootImpl(view.getContext(), display);
} else {
root = new ViewRootImpl(view.getContext(), display,windowlessSession);
}
//为DecorView设置LayoutParams
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
root.setView(view, wparams, panelParentView, userId);
//...
}
接下来才到关键的地方了,ViewRootImpl的setView方法
java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
//...
//遍历view树逻辑
requestLayout();
//...
//将窗口添加到WMS上面
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(),mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibilities(), inputChannel,
mTempInsets,mTempControls);
//...
//将ViewRootImpl设置为DecorView的parent
view.assignParent(this);
}
在这里的requestLayout,最终会走到performTraversals方法,这里才真正开始遍历view的measure、layout、draw等流程
java
private void performTraversals() {
//...
//预测量,最多会执行3次onMeasure
// Ask host how big it wants to be
windowSizeMayChange |= measureHierarchy(host, lp, mView.getContext().getResources(),
desiredWindowWidth, desiredWindowHeight);
//...
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
//...
performLayout(lp, mWidth, mHeight);
//...
if (!performDraw() && mSyncBufferCallback != null) {
mSyncBufferCallback.onBufferReady(null);
}
//...
}
预测量
java
private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
//...
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
final DisplayMetrics packageMetrics = res.getDisplayMetrics();
//默认320dp
res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
if (baseSize != 0 && desiredWindowWidth > baseSize) {
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width,lp.privateFlags);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height,
lp.privateFlags);
//第一次预测量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0 {
goodMeasure = true;
} else {
//宽度不够则再将剩余空间一般给child继续测量
baseSize = (baseSize + desiredWindowWidth) / 2;
childWidthMeasureSpec = getRootMeasureSpec(baseSize,
lp.width,lp.privateFlags);
performMeasure(childWdithMeasureSpec,childHeightMeasureSpec);
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
if (DEBUG_DIALOG) Log.v(mTag, "Good!");
goodMeasure = true;
}
}
}
}
//如果宽度还不满足,则将全部宽度给child去测量
if (!goodMeasure) {
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width,
lp.privateFlags);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height,
lp.privateFlags);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight())
{
windowSizeMayChange = true;
}
}
}
测量
java
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
java
//View.java
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
//...
//最终执行到对应view的onMeasure中
onMeasure(widthMeasureSpec, heightMeasureSpec);
//...
}
布局
java
//ViewRootImpl.java
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
//...
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
//...
}
//View.java
public void layout(int l, int t, int r, int b) {
//...
//在这里给左上右下去赋值,之后的getWidth和getHeight才能获取到值
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
//...
//这里调用到对应view的onLayout方法
onLayout(changed, l, t, r, b);
//...
}
绘制
java
//ViewRootImpl.java
private boolean performDraw() {
//...
boolean canUseAsync = draw(fullRedrawNeeded, usingAsyncReport && mSyncBuffer);
//...
}
private boolean draw(boolean fullRedrawNeeded, boolean forceDraw) {
//...
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
return false;
}
}
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
//...
//执行到对应view的draw方法
mView.draw(canvas);
//...
}