之前分析过一个activity的启动到显示流程,具体显示流程中分析过测量与布局流程,这里对具体的绘制渲染流程进行具体的分析。绘制的起始还是从ViewRootImpl开始,在进入到view之后会根据根据是否有边缘效果稍作区分主要包括以下几点:
大体流程如下图:
其实从这个大体流程里也可以知道为什么自定义view的时候,如果是继承自view,那就必须要自己实现ondraw,因为从这里流程里,在view中并没有具体的绘制操作,只是绘制了前景、背景、边缘效果,具体的绘制在ondraw中,ondraw需要子类自己去实现。
ViewRootImpl中主要流程代码如下:
private void performDraw() {
......
try {
//这里传入的是true
draw(fullRedrawNeeded);
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
......
//这里是true
if (mReportNextDraw) {
mReportNextDraw = false;
......
try {
//向窗口管理服务发送界面绘制完成的消息
mWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
}
}
}
draw方法较长,这里主要关注其进入drawSoftware的流程:
private void draw(boolean fullRedrawNeeded) {
......
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
......
//这里采用硬件渲染来绘制,会重新走一遍performTraversals流程
mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
} else {
//如果硬件渲染器不为空,且不可用,则使能它,然后重新绘制之后返回
if (mAttachInfo.mHardwareRenderer != null &&
!mAttachInfo.mHardwareRenderer.isEnabled() &&
mAttachInfo.mHardwareRenderer.isRequested()) {
try {
mAttachInfo.mHardwareRenderer.initializeIfNeeded(
mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
return;
}
mFullRedrawNeeded = true;
scheduleTraversals();
return;
}
//否则调用软件绘制
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
}
}
......
}
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
//绘制软件渲染
......
try {
......
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;
//这里的mView就是Decorview
mView.draw(canvas);
drawAccessibilityFocusedDrawableIfNeeded(canvas);
} finally {
......
}
} finally {
......
return true;
}
Decorview中的draw方法主要调用了父类的方法,Decorview继承自FrameLayout,draw方法的实现在其祖父类View中:
public void draw(Canvas canvas) {
//第一步,如果没有阴影效果的话,就绘制背景色
if (!dirtyOpaque) {
drawBackground(canvas);
}
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
//如果没有水平或者数值边缘渐变效果,流程就如if中
if (!verticalEdges && !horizontalEdges) {
//第三步,绘制具体内容,这也是所有继承自view的子view主要冲在的方法
if (!dirtyOpaque) onDraw(canvas);
//第四步,绘制子view,这里是针对viewgroup类型
dispatchDraw(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
//第六步,绘制前景,如滚动条等
onDrawForeground(canvas);
// ok,完成返回
return;
}
//以下处理的是带有边缘效果的情况,这也是完整的绘制流程
boolean drawTop = false;
boolean drawBottom = false;
boolean drawLeft = false;
boolean drawRight = false;
float topFadeStrength = 0.0f;
float bottomFadeStrength = 0.0f;
float leftFadeStrength = 0.0f;
float rightFadeStrength = 0.0f;
int paddingLeft = mPaddingLeft;
final boolean offsetRequired = isPaddingOffsetRequired();
if (offsetRequired) {
paddingLeft += getLeftPaddingOffset();
}
//获取绘制边缘效果的left、top、right、bottom四个值
int left = mScrollX + paddingLeft;
int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
int top = mScrollY + getFadeTop(offsetRequired);
int bottom = top + getFadeHeight(offsetRequired);
if (offsetRequired) {
right += getRightPaddingOffset();
bottom += getBottomPaddingOffset();
}
final ScrollabilityCache scrollabilityCache = mScrollCache;
final float fadeHeight = scrollabilityCache.fadingEdgeLength;
int length = (int) fadeHeight;
//如果顶部和底部淡入淡出重叠,则修剪淡入淡出长度重叠的淡化产生奇特的伪像
if (verticalEdges && (top + length > bottom - length)) {
length = (bottom - top) / 2;
}
//同样方式处理水平方向
if (horizontalEdges && (left + length > right - length)) {
length = (right - left) / 2;
}
//确定边缘渐变绘制位置
if (verticalEdges) {
topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
drawTop = topFadeStrength * fadeHeight > 1.0f;
bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
}
if (horizontalEdges) {
leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
drawLeft = leftFadeStrength * fadeHeight > 1.0f;
rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
drawRight = rightFadeStrength * fadeHeight > 1.0f;
}
saveCount = canvas.getSaveCount();
int solidColor = getSolidColor();
if (solidColor == 0) {
final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
//根据以上获取的绘制位置来保存相对位置的图层
if (drawTop) {
canvas.saveLayer(left, top, right, top + length, null, flags);
}
if (drawBottom) {
canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
}
if (drawLeft) {
canvas.saveLayer(left, top, left + length, bottom, null, flags);
}
if (drawRight) {
canvas.saveLayer(right - length, top, right, bottom, null, flags);
}
} else {
scrollabilityCache.setFadeColor(solidColor);
}
// 步骤三,进行实际内容的绘制
if (!dirtyOpaque) onDraw(canvas);
// 步骤四,绘制子view
dispatchDraw(canvas);
//步骤五,绘制边缘效果,并恢复图层
final Paint p = scrollabilityCache.paint;
final Matrix matrix = scrollabilityCache.matrix;
final Shader fade = scrollabilityCache.shader;
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);
}
if (drawBottom) {
matrix.setScale(1, fadeHeight * bottomFadeStrength);
matrix.postRotate(180);
matrix.postTranslate(left, bottom);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, bottom - length, right, bottom, p);
}
if (drawLeft) {
matrix.setScale(1, fadeHeight * leftFadeStrength);
matrix.postRotate(-90);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, top, left + length, bottom, p);
}
if (drawRight) {
matrix.setScale(1, fadeHeight * rightFadeStrength);
matrix.postRotate(90);
matrix.postTranslate(right, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(right - length, top, right, bottom, p);
}
canvas.restoreToCount(saveCount);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// 步骤六,绘制前景色
onDrawForeground(canvas);
}
到这里主题流程分析完成,接下来继续对小流程进行代码分析:
绘制背景色:drawBackground
private void drawBackground(Canvas canvas) {
//获取背景drawable,若没有就返回
final Drawable background = mBackground;
if (background == null) {
return;
}
//设置背景范围
setBackgroundBounds();
// 如果硬件加速和硬件渲染都使能,则使用显示列表来绘制渲染节点
if (canvas.isHardwareAccelerated() && mAttachInfo != null
&& mAttachInfo.mHardwareRenderer != null) {
mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode);
final RenderNode renderNode = mBackgroundRenderNode;
if (renderNode != null && renderNode.isValid()) {
setBackgroundRenderNodeProperties(renderNode);
((DisplayListCanvas) canvas).drawRenderNode(renderNode);
return;
}
}
//绘制前面获取到的backgroud背景
final int scrollX = mScrollX;
final int scrollY = mScrollY;
if ((scrollX | scrollY) == 0) {
background.draw(canvas);
} else {
canvas.translate(scrollX, scrollY);
background.draw(canvas);
canvas.translate(-scrollX, -scrollY);
}
}
绘制具体内容:onDraw
//view中的ondraw是个空实现,需要子view来实现,比如textview、imageview中都进行了重载,完成具体内容的绘制
protected void onDraw(Canvas canvas) {
}
绘制子view:dispatchDraw
该方法实现在viewgroup中,用于绘制子view
@Override
protected void dispatchDraw(Canvas canvas) {
......
//遍历每个子view为其设置动画
if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
final boolean buildCache = !isHardwareAccelerated();
for (int i = 0; i < childrenCount; i++) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
final LayoutParams params = child.getLayoutParams();
attachLayoutAnimationParameters(child, params, i, childrenCount);
bindLayoutAnimation(child);
}
}
......
}
......
for (int i = 0; i < childrenCount; i++) {
//优先绘制短暂的子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;
}
}
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
//然后绘制普通的可见view
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
more |= drawChild(canvas, child, drawingTime);
}
}
while (transientIndex >= 0) {
// 普通视图之后可能还有其他临时视图
final View transientChild = mTransientViews.get(transientIndex);
if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
transientChild.getAnimation() != null) {
more |= drawChild(canvas, transientChild, drawingTime);
}
transientIndex++;
if (transientIndex >= transientCount) {
break;
}
}
if (preorderedList != null) preorderedList.clear();
// 绘制正在变为不可见的有动画的view
if (mDisappearingChildren != null) {
final ArrayList disappearingChildren = mDisappearingChildren;
final int disappearingCount = disappearingChildren.size() - 1;
// 遍历绘制正在变为不可见的view
for (int i = disappearingCount; i >= 0; i--) {
final View child = disappearingChildren.get(i);
more |= drawChild(canvas, child, drawingTime);
}
}
......
}
drawChild又回到了view中的draw方法,对子view进行绘制操作
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
view中的这个draw方法中,主要处理基于图层类型和硬件加速的渲染行为,这里只关注下关键代码:
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
......
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchDraw(canvas);
} else {
draw(canvas);
}
......
}
至此,绘制流程的简单分析就结束了,在view的绘制流程中并没有对界面内容进行绘制,这也就是为什么我们自定义view,继承自view的话,一定要重载ondraw,在ondraw里做绘制内容的操作。