Android View的工作原理4 — draw流程

前言

接着分析Android View的工作原理,前面已经说了重要3步中的2步,本篇内容说一下最后一步,draw流程。

本系列文章:

Android View的工作原理1 — measure基础知识 – 掘金 (juejin.cn)

Android View的工作原理2 — measure流程 – 掘金 (juejin.cn)

Android View的工作原理3 — layout流程 – 掘金 (juejin.cn)

正文

其实View的绘制涉及很多知识点,也是自定义View中最关键的一步,它需要通过canvas即画布绘制出需要的View,关于具体的绘制方法在本篇内容不做细致说明,在后续文章我们抽丝剥茧得再分析。

draw过程

至于什么地方调用draw开始绘制的,这篇文章就不再说明了,具体可以查看前面2篇文章,也就是从ViewRootImpl开始,会直接调用view的draw方法。

对于draw就比较简单了,大致可以分为下面几步:

1 绘制背景 background.draw(canvas)

2 绘制自己 onDraw

3 绘制children dispatchDraw

4 绘制装饰 onDrawScrollbars

其中最主要的就是第2和第3步,还是拿mDecorView举例,它是一个FrameLayout即ViewGroup,当调用mDecorView.draw时,它会先调用onDraw绘制自己,然后发现它自己有children,再去调用dispatchDraw去绘制子View。

源码解析

话不多说,还是直接看一下源码:

//View中的代码
public void draw(Canvas canvas) {
    // ...

    /*
    官方注释的解释,十分清楚
    
     * Draw traversal performs several drawing steps which must be executed
     * in the appropriate order:
     *
     *      1. Draw the background
     *      2. If necessary, save the canvas' layers to prepare for fading
     *      3. Draw view's content
     *      4. Draw children
     *      5. If necessary, draw the fading edges and restore layers
     *      6. Draw decorations (scrollbars for instance)
     *      7. If necessary, draw the default focus highlight
     */

    // 步骤1 绘制背景
    drawBackground(canvas);
    // ...
    if (!verticalEdges && !horizontalEdges) {
        //绘制自己
        onDraw(canvas);
        //绘制子View
        dispatchDraw(canvas);
        //绘制前景
        onDrawForeground(canvas);
        //...
        return;
    }

  //...
}
复制代码

源码十分简单,几乎可以不用看,就记住一句话即可:在onDraw中绘制自己,然后在需要时调用dispatchDraw绘制子View,而这里绘制子View就是通过遍历所有子元素的draw方法即可完成,如此draw事件就一层一层地传递了下去。

当然这2个方法在View中都是抽象方法:

protected void onDraw(Canvas canvas) {
}
复制代码
protected void dispatchDraw(Canvas canvas) {

}
复制代码

但是在ViewGroup中则不是的,在ViewGroup中dispatchDraw就有了实现,实现也就是前面说的遍历子View进行传递绘制:

//ViewGroup的方法
@Override
protected void dispatchDraw(Canvas canvas) {
    final int childrenCount = mChildrenCount;
    final View[] children = mChildren;
    
    //遍历子View
    if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
        for (int i = 0; i < childrenCount; i++) {
            //...
             //绘制子View 
            drawChild(canvas, child, drawingTime)
        //...
        }

        //...
}
复制代码

上面代码省略很多,主要就是遍历子View,调用drawChild方法:

//直接就是调用子View的draw方法
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
    return child.draw(canvas, this, drawingTime);
}
复制代码

可以看出draw事件的传递还是很容易的,主要涉及2个方法。

总结

本篇文章只是一个引子,关于绘制的具体方法后面单独会出一个系列来说,比如canvas概念、如何绘制等等。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享