前言前几篇介绍了measure,布局过程等,接下来详细分析绘制过程。测量过程决定了View的大小,布局过程决定了View的位置,然后绘制过程决定了View的外观,一个View应该显示什么是由绘制过程完成的;然后我们开始开车;1.performDraw的三大工作流程开始在ViewRootImpl#performTraversals中,该方法内部调用了performMeasure、performLayout、performDraw三个方法,分别完成测量、布局、绘制过程。那么让我们从performDraw方法开始;performDrawprivatevoidperformDraw(){//...finalbooleanfullRedrawNeeded=mFullRedrawNeeded;try{draw(fullRedrawNeeded);}finally{mIsDrawing=false;Trace.traceEnd(Trace.TRACE_TAG_VIEW);}}调用了ViewRootImpl#draw方法,来看看atViewRootImpl#draw:privatevoiddraw(booleanfullRedrawNeeded){...//获取mDirty,表示需要重绘的区域finalRectdirty=mDirty;if(mSurfaceHolder!=null){//App拥有表面,wewon'tdraw.dirty。setEmpty();if(animating){if(mScroller!=null){mScroller.abortAnimation();}disposeResizeBuffer();}return;}//如果fullRedrawNeeded为true,则把dirty区域设置为整个屏幕,也就是说需要绘制整个view//第一次绘制过程需要绘制所有viewif(fullRedrawNeeded){mAttachInfo.mIgnoreDirtyState=true;dirty.set(0,0,(int)(mWidth*appScale+0.5f),(int)(mHeight*appScale+0.5f));}//...if(!drawSoftware(surface,mAttachInfo,xOffset,yOffset,scalingRequired,dirty)){return;}}根据fullRedrawNeededDetermine是否需要重置脏区,最后调用ViewRootImpl#drawSoftware方法,并传入相关参数,包括脏区,我们看一下这个方法的源码;privatebooleandrawSoftware(Surfacesurface,AttachInfoattachInfo,intxoff,intyoff,booleanscalingRequired,Rectdirty){//Drawwithsoftwarerenderer.finalCanvascanvas;try{finalintleft=dirty.left;finalinttop=dirty.top;finalintright=dirty.right;finalintbottom=dirty.bottom;//锁定画布区域,由脏区决定canvas=mSurface.lockCanvas(dirty);//脏的矩形可以被Surface.lockCanvas()修改//noinspectionConstantConditionsif(left!=dirty.left||top!=dirty.top||right!=dirty.right||bottom!=dirty.bottom){attachInfo.mIgnoreDirtyState=true;}canvas.setDensity(mDensity);}try{if(!canvas.isOpaque()||yoff!=0||xoff!=0){canvas.drawColor(0,PorterDuff.Mode.CLEAR);}dirty.setEmpty();mIsAnimating=false;attachInfo.mDrawingTime=SystemClock.uptimeMillis();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.draw(canvas);}}returntrue;}实例化Canvas对象,然后锁定画布区域域由脏区决定,然后在画布上进行一系列的属性赋值,最后调用mView.draw(canvas)方法,mView就是DecorView,也就是说从DecorView开始绘制;2.draw源码详解因为ViewGroup没有重写draw方法,所以所有的View都调用了View#draw方法,所以直接看它的源码publicvoiddraw(Canvascanvas){....//1.绘制View本身的背景if(!dirtyOpaque){drawBackground(canvas);}if(!verticalEdges&&!horizo??ntalEdges){//Step3,drawthecontent//2.绘制内容,默认空实现需要重写if(!dirtyOpaque)onDraw(canvas);//3.DrawchildrendispatchDraw(canvas);drawAutofilledHighlight(canvas);//4.DistributeDraw(单View空实现,ViewGroup见下文分析)if(mOverlay!=null&&!mOverlay.isEmpty()){mOverlay.getOverlayView().dispatchDraw(canvas);}//5.绘制装饰(前景色,滚动条)onDrawForeground(canvas);返回;}....}可以看到,draw过程比较复杂,但是逻辑很清晰先看一开始的flagdirtyOpaque。这个flag的作用是判断当前View是否透明。如果View是透明的,那么根据下面的逻辑,可以看出有些步骤是不会执行的,比如绘制背景,绘制内容等;绘制过程五步:绘制View的背景;绘制视图的内容;绘制View的子View(如果有子View);分发抽签;绘制View的装饰(例如:前景色、滚动条);1.绘制背景//绘制背景privatevoiddrawBackground(Canvascanvas){finalDrawablebackground=mBackground;if(background==null){return;}//根据布局过程中获取的View的position参数设置背景BoundarysetBackgroundBounds();//先尝试用HWUI绘制()){setBackgroundRenderNodeProperties(renderNode);((DisplayListCanvas)canvas).drawRenderNode(renderNode);return;}}finalintscrollX=mScrollX;finalintscrollY=mScrollY;if((scrollX|scrollY)==0){//调用Drawabledraw方法绘制背景background.draw(canvas);}else{//如果mScrollX和mScrollY有值,偏移画布坐标平移画布canvas.translate(scrollX,scrollY);//调用Drawable的draw方法绘制背景background.draw(canvas);canvas.translate(-scrollX,-scrollY);}}2.绘制View的内容//DrawView本身Content,空实现,子类必须重写protectedvoidonDraw(Canvascanvas){}这里调用了View#onDraw方法,是View中的空实现,因为不同的View内容不同,需要自己实现,也就是在自定义View中Rewrite这个方法来实现;3.绘制子View。当前View是一个ViewGroup类型,所以需要绘制它的子View。这里调用了dispatchDraw,View中的方法是一个空实现。其实就是ViewGroup重写了这个方法,我们来看一下;@OverrideprotectedvoiddispatchDraw(Canvascanvas){...//遍历子ViewfinalintchildrenCount=mChildrenCount;...for(inti=0;i
