Android View 的绘制流程
view作为构成android界面的基本元素,深入了解view的绘制流程对开发人员来说是很有必要的。
我们创建一个Activity都会在onCreate方法中写setContentView(layoutResId),把我们的布局传进去,那此时我们的布局就添加到屏幕上了吗?当然不是的。那我们的这个布局是在什么时候添加进屏幕的呢?
在ActivityThread.java 的handleResumeActivity中
//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
//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方法
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设置LayoutParamsview.setLayoutParams(wparams);mViews.add(view);mRoots.add(root);mParams.add(wparams);root.setView(view, wparams, panelParentView, userId);//...
}
接下来才到关键的地方了,ViewRootImpl的setView方法
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的parentview.assignParent(this);
}
在这里的requestLayout,最终会走到performTraversals方法,这里才真正开始遍历view的measure、layout、draw等流程
private void performTraversals() {//...//预测量,最多会执行3次onMeasure// Ask host how big it wants to bewindowSizeMayChange |= measureHierarchy(host, lp, mView.getContext().getResources(),desiredWindowWidth, desiredWindowHeight);//...// Ask host how big it wants to beperformMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//...performLayout(lp, mWidth, mHeight);//...if (!performDraw() && mSyncBufferCallback != null) {mSyncBufferCallback.onBufferReady(null);}//...
}
预测量
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();//默认320dpres.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;}}
}
测量
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);}
}
//View.java
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {//...//最终执行到对应view的onMeasure中onMeasure(widthMeasureSpec, heightMeasureSpec);//...
}
布局
//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);//...
}
绘制
//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);//...
}