戏说Android view 工作流程《下》
遍历View树performTraversals()执行过程
view树遍历概述
还是回到ViewRoot.java,我们直接看performTraversals(),该函数就是android系统View树遍历工作的核心。一眼看去,发现这个函数挺长的,但是逻辑是非常清晰的,其执行过程可简单概括为根据之前所有设置好的状态,判断是否需要计算视图大小(measure)、是否需要重新安置视图的位置(layout),以及是否需要重绘(draw)视图,可以用以下图来表示该流程。

从上图可以看出,measure过程始于ViewRoot的host.measure(),调的就是view类的measure()函数,该函数然后回调onMeasure。如果host对象是一个ViewGroup实例,一般会重载onMeasure,如果没有的话,则会执行view类中默认的onMeasure。合理的情况是编程人员重载onMeasure并逐一对里面的子view进行measure。我们可以看一下view的measure方法:
眼尖的同学应该发现了,上面这张图比前面的measure和layout多了一步:draw()。在performTraversals()函数中调用的是viewRoot的draw()函数,在该函数中进行一系列的前端处理后,再调用host.draw()。
一般情况下,View对象不应该重载draw()函数,因此,host.draw()就是view.draw()。该函数内部过程也就是View系统绘制过程的核心过程,该函数中会依次绘制前面所说哦四种元素,其中绘制视图本身的具体实现就是回调onDraw()函数,应用程序一般也会重载onDraw()函数以绘制所设计的View的真正界面内容。
Google源码的注释太nice了,我加任何说辞都显得多余,就不画蛇添足了:
protected void dispatchDraw(Canvas canvas) { final int count = mChildrenCount; final View[] children = mChildren; int flags = mGroupFlags;//1 判断mGroupFlags是否设有FLAG_RUN_ANIMATION标识并且不为0.该layout动画指的是加载或移除子视图时候呈现的动画. if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) { final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE; final boolean buildCache = !isHardwareAccelerated();//硬件加速,4.0加入. for (int i = 0; i < count; i++) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { final LayoutParams params = child.getLayoutParams(); attachLayoutAnimationParameters(child, params, i, count); bindLayoutAnimation(child); } }//2 处理padding属性,如果该viewGroup有设置. int saveCount = 0; final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK; if (clipToPadding) { saveCount = canvas.save(); canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop, mScrollX + mRight - mLeft - mPaddingRight, mScrollY + mBottom - mTop - mPaddingBottom); }//3 开始绘制子视图动画之前先清除flag. // We will draw our child's animation, let's reset the flag mPrivateFlags &= ~DRAW_ANIMATION; mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED; boolean more = false; final long drawingTime = getDrawingTime();//4 使用佛如循环,使viewGroup的子视图逐个调用drawChild函数. if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) { for (int i = 0; i < count; i++) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { more |= drawChild(canvas, child, drawingTime); } } } else { for (int i = 0; i < count; i++) { final View child = children[getChildDrawingOrder(count, i)]; if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { more |= drawChild(canvas, child, drawingTime); } } }//5 Draw any disappearing views that have animations if (mDisappearingChildren != null) { final ArrayList<View> disappearingChildren = mDisappearingChildren; final int disappearingCount = disappearingChildren.size() - 1; // Go backwards -- we may delete as animations finish for (int i = disappearingCount; i >= 0; i--) { final View child = disappearingChildren.get(i); more |= drawChild(canvas, child, drawingTime); } } if (clipToPadding) { canvas.restoreToCount(saveCount); }// mGroupFlags might have been updated by drawChild() flags = mGroupFlags; if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) { invalidate(true); } }整个view的工作流程那是相当复杂的,这需要静下心来,加以时日细细揣摩才能略有体会,本人写有小Demo一个,就当抛砖引玉:ClickMe!