前言
本文主要分析View的draw的流程
View draw
SDK中的注释很详细
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
//判断是否为非法invalidate请求,我们平时调用invalidate即为合法的,即返回false
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
//重置标志位为合法invalidate以及需要draw
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
// Step 1, draw the background, if needed
int saveCount;
if (!dirtyOpaque) {
drawBackground(canvas);
}
// skip step 2 & 5 if possible (common case)
final int viewFlags = mViewFlags;
//表示ScrollBar在不使用时,是否渐变消失,默认为true
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
// we're done...
return;
}
.......非正常情况的代码省略
}
主要看Step.4的dispatchDraw方法,该方法用于绘制child,View中的dispatchDraw方法为空实现,因为View没有child,接下来看看ViewGroup的dispatchDraw方法:
@Override
protected void dispatchDraw(Canvas canvas) {
....................
for (int i = 0; i < childrenCount; i++) {
//1.先画暂态View,具体解释看下文
while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
final View transientChild = mTransientViews.get(transientIndex);
if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
transientChild.getAnimation() != null) {
more |= drawChild(canvas, transientChild, drawingTime);
}
transientIndex++;
if (transientIndex >= transientCount) {
transientIndex = -1;
}
}
int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
final View child = (preorderedList == null)
? children[childIndex] : preorderedList.get(childIndex);
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
//2.画child,drawChild
more |= drawChild(canvas, child, drawingTime);
}
}
...........
}
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
注释1:
暂态View即通过ViewGroup# addTransientView方法添加的View,该View无法获得焦点,不能响应事件等,它纯粹只用于显示效果,类似补间动画中的view,无法响应事件。
总结:
View的draw主要分6个步骤,正常情况下只有4步,省略步骤2、5:
1.画背景
2.条件满足则:saveLayer,为渐变消失准备
3.画自己内容(平时开发当中的onDraw)
4.画Child(通过调用ViewGroup的dispatchDraw方法)
5.条件满足则:画scrollbar渐变消失,restoreLayer
6.画前景