Android自定义控件之onDraw详解

    前面两篇我们以Linealayout为入口分析了自定义控件的onMeasure方法和onLayout方法,本篇我们分析自定义控件比较重要的一个方法onDraw,该方法与之前的两个方法调用过程有所区别,并不是由ViewGroup来调用View的方法,而是相对独立的,这里我们需要先分析View的onDraw,再分析Linealayout的方法。

 一.Draw详解

    View 的onDraw方法是通过draw方法调用,这里我们从draw方法开始分析:
Android自定义控件之onDraw详解
   这里小字部分解释了draw方法绘制整个界面的过程具体如下:
        1. Draw the background(绘制背景)
        2. If necessary, save the canvas’ layers to prepare for fading(如果需要,为保存这层为边缘的滑动效果作准备)
        3. Draw view’s content(绘制内容) 
        4. Draw children(绘制子View) 
        5. If necessary, draw the fading edges and restore layers(如果需要,绘制边缘效果并且保存图层)
        6. Draw decorations (scrollbars for instance)(绘制边框,比如scrollbars,TextView)

继续看后续实现:

1.绘制背景

Android自定义控件之onDraw详解
Android自定义控件之onDraw详解
    如果需要背景就绘制背景,背景实际就是一个Drawable,设置时先设置边界,再调用draw方法完成绘制。

2.跳过第二步和第五步

Android自定义控件之onDraw详解
    由于第二步和第五步是绘制View滑到边缘时的效果等等的边缘特效,一般用不到。

3.绘制内容

Android自定义控件之onDraw详解
Android自定义控件之onDraw详解
     绘制View的内容,这里就调用了onDraw方法,也就是说onDraw方法是实现View的具体显示内容的,因此我们在自定义View 的时候只重写onDraw,在View部分没有给出具体实现,需要按照自己需求做重写。

4.绘制子View

Android自定义控件之onDraw详解
这个方法实际上不是为View准备的而是给viewGroup准备的,由于ViewGroup是继承自View,后续我们再做分析。

5.绘制前景

Android自定义控件之onDraw详解
Android自定义控件之onDraw详解
    这里绘制的主要是滑动条,也就说实际上说有的View都是有滑动条的,只是显示与隐藏的区别。

6.绘制默认焦点高亮

    Android自定义控件之onDraw详解
Android自定义控件之onDraw详解

二.扩展

    这里重点分析一下onDraw和dispatchDraw这两个方法的区别:

1.onDraw()

    该方法用于绘制自身内容,之前也说过调用时在绘制完背景以后调用。

2.dispatchDraw()

    该方法用于绘制子View,需要注意的是当ViewGroup没有背景时,不会调用draw方法,会直接调用dispatchDraw,但是在draw方法中会调用dispatchDraw,也就是说我们在自定义ViewGroup时应该重写dispatchDraw方法。

3.Canvas

    控件的所有绘制工作最终都会交由canvas来处理,Canvas被draw调用,当draw的时候需要4个基本的元素:

    1、a Bitmap to hold the pixels (一个bitmap来存放所有的像素)
    2、a Canvas to host the draw calls (writing into the bitmap)(canvas来主持draw的调用,将像素写入bitmap)
    3、a drawing primitive (e.g. Rect, Path, text, Bitmap), (绘制的具体内容,可以文本,图片,形状之类)
    4、a paint (to describe the colors and styles for the drawing).(一只‘笔’,用来定义颜色,和绘制的类型)
    在实际的使用中我们主要是paint和Canvas配合使用,这里需要提一下很多新手会在onDraw方法中去新建paint,这里需要注意onDraw方法会重复调用,如果在此处新建paint的话就形成了重复造*的尴尬,最好的方法是在构造方法来初始化。