android之View绘制到窗口上的过程
以前在研究自定义空间的时候,查看过View和ViewGroup绘制的流程的。只是定性的知道会经过onMeasure—onLayout—onDraw这些流程。上一篇Android视图加载到窗口的过程分析中分析了视图加载到窗口的过程。主要就是一系列的addView操作,这篇就从addView方法开始看看View是如何绘制到窗口上的。
ActivityThread#handleResumeActivity方法
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) { …… ActivityClientRecord r = performResumeActivity(token, clearHide); …… if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView();//前面分析过,这个是Window对象所维护的装饰窗口,最顶层的窗口 decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager();//获取WindowManager,继承自ViewManager,不可实例化,是个接口 WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (a.mVisibleFromClient) { a.mWindowAdded = true; wm.addView(decor, l);// 通过WindowManager添加到窗口 } …… }
可以看到最顶层的装饰窗口在activity resume的时候通过windowManager#addView方法添加。
WindowManagerImpl# addView
public void addView(View view, ViewGroup.LayoutParams params) { mGlobal.addView(view, params, mDisplay, mParentWindow); } public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ViewRootImpl root; View panelParentView = null; ...... root = new ViewRootImpl(view.getContext(), display); // 创建一个ViewRoot对象 view.setLayoutParams(wparams); if (mViews == null) { index = 1; mViews = new View[1]; mRoots = new ViewRootImpl[1]; mParams = new WindowManager.LayoutParams[1]; } else { index = mViews.length + 1; Object[] old = mViews; mViews = new View[index]; System.arraycopy(old, 0, mViews, 0, index-1); old = mRoots; mRoots = new ViewRootImpl[index]; System.arraycopy(old, 0, mRoots, 0, index-1); old = mParams; mParams = new WindowManager.LayoutParams[index]; System.arraycopy(old, 0, mParams, 0, index-1); } index--; mViews[index] = view; mRoots[index] = root;// 将view和ViewRootImp关联起来 ViewRootImp是链接View和WindowManagerService的桥梁 mParams[index] = wparams; } try { root.setView(view, wparams, panelParentView);// 调用ViewRoot的setView方法 } ...... }
setView:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { requestLayout(); // 请求UI开始绘制重新绘制View树 ...... try { mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); // 通知WindowManagerService添加一个窗口 会调用到addWindow方法 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mInputChannel); } }
两种情况会导致调用到requestLayout,改变视图显示属性,比如setVisibility,是直接或者间接调用该函数。
@Override public void requestLayout() { checkThread();// 本次调用是否是在UI线程调用的。 mLayoutRequested = true; scheduleTraversals(); } void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().postSyncBarrier(); //分发一个异步消息,处理函数中调用performTraversals()对View进行重新遍历。 mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);// mTraversalRunnable是一个Runnable对象 scheduleConsumeBatchedInput(); } } final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal();//执行doTraversal() } } void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); …… try { performTraversals(); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } …… } }
View树遍历的核心函数 measure—layout--draw
private void performTraversals() { ...... // Ask host how big it wants to be performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); ...... performLayout(); …… performDraw(); …… mIsInTraversal = false; }
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); } }
mView.measure(childWidthMeasureSpec,childHeightMeasureSpec);就执行到了熟悉onMeasure(childWidthMeasureSpec,childHeightMeasureSpec);里面。