具体请看我写的另外一篇文章:自定义View基础 - 最易懂的自定义View原理系列
绘制View视图
同measure、layout过程一样,draw过程根据View的类型分为两种情况:
如果View = 单一View,则仅绘制本身View;如果View = VieGroup(包含子View),除了绘制自身View外,还需要绘制子View。接下来,我将详细分析这两种情况下的draw过程。
在没有现成的View,需要自己实现的时候,就使用自定义View,一般继承自View、SurfaceView等
特点是:不包含子View。
如下图所示:
单一View的layout过程下面我将一个个方法进行详细分析。
作用:根据给定的 Canvas 自动渲染 View(包括其所有子 View)。
在调用该方法之前必须要完成 layout 过程
public void draw(Canvas canvas) { // 特别注意: // 所有的视图最终都是调用 View 的 draw ()绘制视图( ViewGroup 没有复写此方法) // 在自定义View时,不应该复写该方法,而是复写 onDraw(Canvas) 方法进行绘制。 // 如果自定义的视图确实要复写该方法,那么需要先调用 super.draw(canvas)完成系统的绘制,然后再进行自定义的绘制。 ... /* * 绘制过程如下: * 1. 绘制view背景 * 2. 绘制view内容 * 3. 绘制子View * 4. 绘制装饰(渐变框,滑动条等等) */ int saveCount; if (!dirtyOpaque) { // 步骤1: 绘制本身View背景 drawBackground(canvas); } // 如果有必要,就保存图层(还有一个复原图层) // 优化技巧: // 当不需要绘制 Layer 时,“保存图层“和“复原图层“这两步会跳过 // 因此在绘制的时候,节省 layer 可以提高绘制效率 final int viewFlags = mViewFlags; if (!verticalEdges && !horizontalEdges) { if (!dirtyOpaque) // 步骤2:绘制本身View内容 onDraw(canvas); // View 中:默认为空实现 // ViewGroup中:自定义View时需要进行复写!!!! .. // 步骤3:绘制子View dispatchDraw(canvas); // 由于单一View没有子View,所以View 中:默认为空实现 ... // 步骤4:绘制滑动条和前景色等等 onDrawScrollBars(canvas); // we're done... return; } ... }下面,我们继续分析在draw()中调用的drawBackground()、 onDraw()、dispatchDraw()、onDrawScrollBars(canvas)
请记住:自定义View中必须 且 只需要复写onDraw()。
至此,单一View的draw过程已经分析完毕。
单一View的draw过程解析如下:
只需要绘制自身View
单一View的draw过程利用现有组件根据特定布局方式来组成新的组件,大多继承自ViewGroup或各种Layout
特点:含有子View
步骤1:ViewGroup绘制自身(含背景、内容);步骤2:ViewGroup遍历子View并绘制包含的所有子View;
类似于单一View的draw过程
步骤3:ViewGroup绘制装饰(滚动指示器、滚动条、和前景)
这样自上而下、一层层地传递下去,直到完成整个View树的draw过程
如下图所示:
ViewGroup的draw过程下面我将对每个步骤和方法进行详细分析。
作用:根据给定的 Canvas 自动渲染 View
在调用该方法之前必须要完成 layout 过程
源码分析:(与单一View draw过程的draw()类似)
public void draw(Canvas canvas) { // 特别注意: // 所有的视图最终都是调用 View 的 draw ()绘制视图( ViewGroup 没有复写此方法) // 在自定义View时,不应该复写该方法,而是复写 onDraw(Canvas) 方法进行绘制。 // 如果自定义的视图确实要复写该方法,那么需要先调用 super.draw(canvas)完成系统的绘制,然后再进行自定义的绘制。 ... /* * 绘制过程如下: * 1. 绘制view背景 * 2. 绘制view内容 * 3. 绘制子View * 4. 绘制装饰(渐变框,滑动条等等) */ int saveCount; if (!dirtyOpaque) { // 步骤1: 绘制本身View背景 drawBackground(canvas); } // 如果有必要,就保存图层(还有一个复原图层) // 优化技巧: // 当不需要绘制 Layer 时,“保存图层“和“复原图层“这两步会跳过 // 因此在绘制的时候,节省 layer 可以提高绘制效率 final int viewFlags = mViewFlags; if (!verticalEdges && !horizontalEdges) { if (!dirtyOpaque) // 步骤2:绘制本身View内容 onDraw(canvas); // View 中:默认为空实现 // ViewGroup中:自定义View时需要进行复写!!!! .. // 步骤3:绘制子View dispatchDraw(canvas); // View 中:默认为空实现 // ViewGroup中:系统已经复写好对其子视图进行绘制我们不需要复写 ... // 步骤4:绘制滑动条和前景色等等 onDrawScrollBars(canvas); // we're done... return; } ... }至此,ViewGroup的draw过程已经分析完毕。
对于ViewGroup的draw过程流程如下:
ViewGroup的draw过程View 中有一个特殊的方法:setWillNotDraw()
public void setWillNotDraw(boolean willNotDraw) { setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK); } 该方法用于设置 WILL_NOT_DRAW 标记位该标记位的作用是:当一个View不需要绘制内容时,系统进行相应优化默认情况下:View 不启用该标记位(设置为true);ViewGroup 默认启用(设置为false)
应用场景:
setWillNotDraw参数设置为true:当自定义View继承自 ViewGroup 、且本身并不具备任何绘制时,设置为 true 后,系统会进行相应的优化。
setWillNotDraw参数设置为false:当自定义View继承自 ViewGroup 、且需要绘制内容时,那么设置为 false,来关闭 WILL_NOT_DRAW 这个标记位。
步骤1:ViewGroup绘制自身(含背景、内容);步骤2:ViewGroup遍历子View并绘制包含的所有子View;
类似于单一View的draw过程
步骤3:ViewGroup绘制装饰(滚动指示器、滚动条、和前景)
对于View的draw过程只需要绘制自身(含背景、内容)+装饰即可
一个图总结自定义View - draw过程,如下图:
自定义View - draw过程转载于:https://www.cnblogs.com/xinmengwuheng/p/7070092.html