一、调用流程
1. ViewRootImpl # performTraversals()
performTraversals()调用了performDraw(),三大流程的起点都是performTraversals()。
// 没有取消draw也没有创建新的平面 第一次traversals时newSurface为true
if (!cancelDraw && !newSurface) {
// ...
// 开始draw流程
performDraw();
} else {
if (isViewVisible) {
// Try again
// 如果是可见的, 就再调用一次traversals
scheduleTraversals();
// ...
}
// ...
}
2. ViewRootImpl # performDraw()
调用到ViewRootImpl的draw()。
private void performDraw() {
// ...
// mFullRedrawNeeded表示是否需要完全重绘
final boolean fullRedrawNeeded = mFullRedrawNeeded;
mFullRedrawNeeded = false;
mIsDrawing = true;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
try {
// 调用draw()
draw(fullRedrawNeeded);
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
// ...
}
3. ViewRootImpl # draw()
这个方法非常长,主要是去处理绘制区域、坐标等准备工作,之后调用drawSoftware()。
private void draw(boolean fullRedrawNeeded) {
// ...
// 获取需要绘制的区域
final Rect dirty = mDirty;
// ...
// 判断是否需要完全绘制
if (fullRedrawNeeded) {
mAttachInfo.mIgnoreDirtyState = true;
// 如果需要就将区域设置为屏幕的所有区域
dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
}
// ...
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
// ...
} else {
// ...
// 调用drawSoftware()绘制
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
}
}
// ...
}
4. ViewRootImpl # drawSoftware()
- 初始canvas对象
- 调用DecorView的draw()
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
final Canvas canvas;
try {
final int left = dirty.left;
final int top = dirty.top;
final int right = dirty.right;
final int bottom = dirty.bottom;
// 创建一个绘制区域的canvas对象
canvas = mSurface.lockCanvas(dirty);
// 判断lockCanvas有没有改变dirty的顶点值
if (left != dirty.left || top != dirty.top || right != dirty.right
|| bottom != dirty.bottom) {
attachInfo.mIgnoreDirtyState = true;
}
// 设置画布密度
canvas.setDensity(mDensity);
} catch (Surface.OutOfResourcesException e) {
// ...
}
try {
// ...
// 先清空画布
if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
}
dirty.setEmpty();
mIsAnimating = false;
mView.mPrivateFlags |= View.PFLAG_DRAWN;
try {
// 设置画布偏移值
canvas.translate(-xoff, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
attachInfo.mSetIgnoreDirtyState = false;
// 调用draw() 从DecorView开始绘制流程
mView.draw(canvas);
// ...
}
// ...
}
// ...
return true;
}
5. DecorView # draw()
- 先调用父类的draw()去绘制。
- 如果有菜单背景的drawable的画就画上。
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (mMenuBackground != null) {
mMenuBackground.draw(canvas);
}
}
6. View # draw()
FrameLayout和ViewGroup都没有重写draw(),所以就调用到了View的draw()。View的draw()也很长,但是注释写的分步很清楚。
/*
* 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)
*/
绘制背景、绘制自身、绘制子View、绘制阴影、绘制其它的装饰。到这里就调用到Draw向子View分发的过程了。下面就仔细的看看这个方法。
二、View的draw()的实现
根据注释可以将draw()分成六个阶段。
- 绘制背景
- 存储图层
- 绘制自身
- 绘制子View
- 绘制阴影并恢复图层
- 绘制滚动条等效果
1. 绘制背景
View # draw()
如果不为透明,就调用drawBackground()去绘制背景。
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
// 判断是否为透明
if (!dirtyOpaque) {
// 调用drawBackgroud()去绘制背景
drawBackground(canvas);
}
View # drawBackground()
- 设置背景图片边界
- 判断View是否有移动
- N:直接绘制背景图片
- Y:移动到View的偏移位置,绘制背景,再移动回来。
private void drawBackground(Canvas canvas) {
final Drawable background = mBackground;
if (background == null) {
return;
}
// 设置背景图片的边界位置
setBackgroundBounds();
// 硬件加速相关......
final int scrollX = mScrollX;
final int scrollY = mScrollY;
// 判断View是否滑动
if ((scrollX | scrollY) == 0) {
// 没有滑动就绘制背景图片
background.draw(canvas);
} else {
// 如果移动了,就先移动canvas,绘制背景,canvas再移回
canvas.translate(scrollX, scrollY);
background.draw(canvas);
canvas.translate(-scrollX, -scrollY);
}
}
View # setBackgroundBounds()
调用drawable的setBounds()设置边界,传参是由layout过程中生成的顶点值组合的,就相当于是View的顶点。
void setBackgroundBounds() {
if (mBackgroundSizeChanged && mBackground != null) {
// 设置背景图的四个坐标,这四个值就是View的四个顶点值
mBackground.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
mBackgroundSizeChanged = false;
rebuildOutline();
}
}
2. 绘制自身
View # draw()
- 如果不需要绘制阴影,就直接进入绘制自身的步骤。
- 如果View不是透明的,就调用onDraw()去绘制自身。
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);
// ...
}
View # onDraw()
空实现,没有做统一实现,自定义View时需要自己去实现。
protected void onDraw(Canvas canvas) {
}
3. 绘制子View分发过程
View # draw()
在绘制自己完成后就去分发调用子View的绘制过程。
if (!verticalEdges && !horizontalEdges) {
// ...
dispatchDraw(canvas);
// ...
}
ViewGroup # dispatchDraw()
在View中是空实现,所以来看ViewGroup中的实现。会遍历所有的子View,如果子View可见或是存在动画,就调用drawChld()。
@Override
protected void dispatchDraw(Canvas canvas) {
boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
final int childrenCount = mChildrenCount;
final View[] children = mChildren;
int flags = mGroupFlags;
// ...
// 遍历子View
for (int i = 0; i < childrenCount; i++) {
// ...
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
// 调用drawChild去传递
more |= drawChild(canvas, child, drawingTime);
}
}
// ...
}
ViewGroup # drawChild()
直接调用了子View的draw(),实现了传递。
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
View # draw()
这个draw()有三个参数,和之前分析的一个参数的draw()不同,注释上说得很清楚,这个draw()是ViewGroup.drawChild()调用去绘制子View的。和一个参数最大不同是,这个draw()判断了是绘制缓存内容还是去调用一个参数的draw()绘制。
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
// ...
if (!drawingWithDrawingCache) {
// 如果不使用缓存
if (drawingWithRenderNode) {
// 硬件缓存
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
((DisplayListCanvas) canvas).drawRenderNode(renderNode);
} else {
// Fast path for layouts with no backgrounds
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
// 如果需要跳过,就直接去调用分发给这个View的子View的方法
dispatchDraw(canvas);
} else {
// 调用普通的draw()
draw(canvas);
}
}
} else if (cache != null) {
// 如果使用缓存并且缓存不为空
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
if (layerType == LAYER_TYPE_NONE || mLayerPaint == null) {
// no layer paint, use temporary paint to draw bitmap
Paint cachePaint = parent.mCachePaint;
if (cachePaint == null) {
cachePaint = new Paint();
cachePaint.setDither(false);
parent.mCachePaint = cachePaint;
}
cachePaint.setAlpha((int) (alpha * 255));
// 绘制缓存的内容
canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
} else {
// use layer paint to draw the bitmap, merging the two alphas, but also restore
int layerPaintAlpha = mLayerPaint.getAlpha();
if (alpha < 1) {
mLayerPaint.setAlpha((int) (alpha * layerPaintAlpha));
}
// 使用图层的Paint去绘制缓存内容
canvas.drawBitmap(cache, 0.0f, 0.0f, mLayerPaint);
if (alpha < 1) {
mLayerPaint.setAlpha(layerPaintAlpha);
}
}
}
// ...
}
4. 绘制装饰物
View # draw()
if (!verticalEdges && !horizontalEdges) {
// ...
onDrawForeground(canvas);
// ...
}
View # onDrawForeground()
public void onDrawForeground(Canvas canvas) {
// 绘制滚动指示器
onDrawScrollIndicators(canvas);
// 绘制滚动条
onDrawScrollBars(canvas);
final Drawable foreground = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
if (foreground != null) {
// ...
// 如果有前景图片,就绘制它
foreground.draw(canvas);
}
}
5. 如果需要绘制边框阴影
前面那些步骤走完后就能直接return了。如果需要绘制阴影的话,则不会进入前面分析的方法块。而是执行下面的步骤。注释上说这种情况不常见。
View # draw()
- 同样地绘制背景
- 去计算阴影带来的变量影响,对阴影分类去保存图层
- 同样地绘制自身
- 同样地分发绘制流程
- 分类去绘制阴影
- 加载保存的图层
- 同样地绘制装饰
// 共同的步骤,绘制背景图片
if (!dirtyOpaque) {
drawBackground(canvas);
}
// 不需要绘制阴影的情况
if (!verticalEdges && !horizontalEdges) {
// ...
return;
}
// 如果需要绘制阴影
// 根据阴影情况去改变参数......
if (solidColor == 0) {
final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
// 保存图层
if (drawTop) {
canvas.saveLayer(left, top, right, top + length, null, flags);
}
// ...
} else {
scrollabilityCache.setFadeColor(solidColor);
}
// 绘制自身
if (!dirtyOpaque) onDraw(canvas);
// 绘制子View
dispatchDraw(canvas);
// ...
// 绘制阴影
if (drawTop) {
matrix.setScale(1, fadeHeight * topFadeStrength);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, top, right, top + length, p);
}
// ...
// 取出保存的图层
canvas.restoreToCount(saveCount);
// ...
// 绘制装饰
onDrawForeground(canvas);
网友评论