前几篇文章,码进讲述了measure,阶之w机layout流程等,深入接下来将详细分析绘制流程。理解 测量流程决定了View的制流制大小,布局流程决定了View的码进位置,那么绘制流程将决定View的阶之w机样子,一个View该显示什么由绘制流程完成; 那我们就开始开车了; 三大工作流程始于ViewRootImpl#performTraversals,理解在这个方法内部会分别调用performMeasure,performLayout,制流制performDraw三个方法来分别完成测量,码进布局,阶之w机绘制流程。深入那么我们现在先从performDraw方法看起; 里面又调用了ViewRootImpl#draw方法,理解我们来看看ViewRootImpl#draw: 根据fullRedrawNeeded来判断是否需要重置dirty区域,最后调用了ViewRootImpl#drawSoftware方法,并把相关参数传递进去,包括dirty区域,我们接着看该方法的源码; 实例化了Canvas对象,然后锁定该canvas的区域,由dirty区域决定,接着对canvas进行一系列的云服务器提供商属性赋值,最后调用了mView.draw(canvas)方法, mView就是DecorView,也就是说从DecorView开始绘制; 由于ViewGroup没有重写draw方法,因此所有的View都是调用View#draw方法,因此,我们直接看它的源码 可以看到,draw过程比较复杂,但是逻辑十分清晰。首先来看一开始的标记位dirtyOpaque, 该标记位的作用是判断当前View是否是透明的,如果View是透明的,那么根据下面的逻辑可以看出,将不会执行一些步骤,服务器租用比如绘制背景、绘制内容等; 绘制流程的五个步骤: 绘制View的装饰(例如:前景色,滚动条); 这里调用了View#onDraw方法,View中该方法是一个空实现,因为不同的View有着不同的内容,这需要我们自己去实现,即在自定义View中重写该方法来实现; 当前的View是一个ViewGroup类型,服务器托管那么就需要绘制它的子View,这里调用了dispatchDraw,而View中该方法是空实现,实际是ViewGroup重写了这个方法,那么我们来看看; 所谓的绘制装饰,就是指View除了背景、内容、子View的其余部分,例如滚动条等,我们看View#onDrawForeground 到目前为止,View的绘制流程也讲述完毕了; 其实绘制这块还是很重要的,下次还是要继续讲解下; 学如逆水行舟,不进则退;心似平原走马,易放难收; 一起加油老铁们 本文转载自微信公众号「Android开发编程」 【编辑推荐】前言
一、深入performDraw
performDraw
private void performDraw() { //... final boolean fullRedrawNeeded = mFullRedrawNeeded; try { draw(fullRedrawNeeded); } finally { mIsDrawing = false; Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } 二、draw源码详解
1、绘制背景
//绘制背景 private void drawBackground(Canvas canvas) { final Drawable background = mBackground; if (background == null) { return; } // 根据在 layout 过程中获取的 View 的位置参数,来设置背景的边界 setBackgroundBounds(); // 先尝试用HWUI绘制 if (canvas.isHardwareAccelerated() && mAttachInfo != null && mAttachInfo.mThreadedRenderer != null) { mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode); final RenderNode renderNode = mBackgroundRenderNode; if (renderNode != null && renderNode.isValid()) { setBackgroundRenderNodeProperties(renderNode); ((DisplayListCanvas) canvas).drawRenderNode(renderNode); return; } } final int scrollX = mScrollX; final int scrollY = mScrollY; if ((scrollX | scrollY) == 0) { //调用 Drawable 的 draw 方法来进行背景的绘制 background.draw(canvas); } else { // 若 mScrollX 和 mScrollY 有值,则对 canvas 的坐标进行偏移平移画布 canvas.translate(scrollX, scrollY); //调用 Drawable 的 draw 方法来进行背景的绘制 background.draw(canvas); canvas.translate(-scrollX, -scrollY); } } 2、绘制View的内容
// 绘制View本身内容,空实现,子类必须复写 protected void onDraw(Canvas canvas) { } 3、子View进行绘制
4、分发Draw
@Override protected void dispatchDraw(Canvas canvas) { ... // 1. 遍历子View final int childrenCount = mChildrenCount; ... for (int i = 0; i < childrenCount; i++) { 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); if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { // 调用 drawChild 方法,进行子元素绘制 more |= drawChild(canvas, child, drawingTime); } } .... } 5、绘制View
总结