二十二、 View 的工作原理(6)--- View 的 draw 过程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yz_cfm/article/details/91047638

    前面学习了 View 三大流程中的两个:measure 过程(确定 View 的测量宽高)、layout 过程(确定 View 的最终宽高和四个顶点的位置)。下面学习 View 三大流程中的最后一个 --- draw 过程(绘制)。

    下面看一下 View 中的 draw() 方法实现:

/**
 * 实现视图时,请执行 onDraw() 方法,而不是覆盖这个方法。如果确实需要重写此方法,请调用超类版本
 */
public void draw(Canvas canvas) {
    final int privateFlags = mPrivateFlags;
    final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
            (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
    mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

    /*
     * Draw 遍历执行几个必须执行的绘图步骤
     * 按适当次序:
     *      1. 绘制背景( background.draw(canvas) )
     *      2. 如有必要,保存画布的图层以防淡化
     *      3. 绘制自己的内容( onDraw() )
     *      4. 绘制 children( dispatchDraw() )
     *      5. 如有必要,绘制淡化边缘并恢复图层
     *      6. 绘制装饰( 例如 scrollbars -> onDrawScrollBars() )
     */
    // Step 1, 绘制背景
    int saveCount;

    if (!dirtyOpaque) {
        drawBackground(canvas);
    }

    // skip step 2 & 5 if possible (common case)
    ...
    if (!verticalEdges && !horizontalEdges) {
        // Step 3, 绘制自己
        if (!dirtyOpaque) onDraw(canvas);

        // Step 4, 绘制 children
        dispatchDraw(canvas);
        ...

        // Step 6, 绘制装饰(前景,滚动条)
        onDrawForeground(canvas);

        // Step 7, 绘制默认焦点高亮显示
        drawDefaultFocusHighlight(canvas);

        if (debugDraw()) {
            debugDrawFocus(canvas);
        }

        // we're done...
        return;
    }
    ...
    // Step 2, 保存画布的图层
    ...
    if (solidColor == 0) {
        ...
        if (drawTop) {
            canvas.saveLayer(left, top, right, top + length, null, flags);
        }

        if (drawBottom) {
            canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
        }

        if (drawLeft) {
            canvas.saveLayer(left, top, left + length, bottom, null, flags);
        }

        if (drawRight) {
            canvas.saveLayer(right - length, top, right, bottom, null, flags);
        }
    } else {
        scrollabilityCache.setFadeColor(solidColor);
    }

    // Step 3, 绘制自己
    if (!dirtyOpaque) onDraw(canvas);

    // Step 4, 绘制 children
    dispatchDraw(canvas);

    // Step 5, 绘制淡入淡出效果并恢复图层
    ...
    if (drawTop) {
        ...
        canvas.drawRect(left, top, right, top + length, p);
    }

    if (drawBottom) {
        ...
        canvas.drawRect(left, bottom - length, right, bottom, p);
    }

    if (drawLeft) {
        ...
        canvas.drawRect(left, top, left + length, bottom, p);
    }

    if (drawRight) {
        ...
        canvas.drawRect(right - length, top, right, bottom, p);
    }

    canvas.restoreToCount(saveCount);
    drawAutofilledHighlight(canvas);

    // Step 6, 绘制装饰(前景,滚动条)
    onDrawForeground(canvas);

    if (debugDraw()) {
        debugDrawFocus(canvas);
    }
}

    从上面可以看到,View 绘制过程的传递是通过 dispatchDraw() 实现的:

protected void dispatchDraw(Canvas canvas) {
    ...
    for (int i = 0; i < childrenCount; i++) {
        while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
            ...
            if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
                    transientChild.getAnimation() != null) {
                more |= drawChild(canvas, transientChild, drawingTime);
            }
            ...
        }
        ...
        if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
            more |= drawChild(canvas, child, drawingTime);
        }
    }
}

    然后 dispatchDraw() 中又调用 drawChild():

protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
    return child.draw(canvas, this, drawingTime);
}

    接着 drawChild() 又继续调用 View 中的 draw() 方法,如此反复,一层层的进行传递,就完成了整个 View 树的绘制过程。


    下面看一个 View 中的一个特殊方法 --- setWillNotDraw()。

/**
 * 如果该 View 本身不需要绘制任何内容,那么设置这个标记位为 true 以后,系统会进行相应的优化。
 * 默认情况下,View 没有启用这个优化标志位,但是它的子类 ViewGroup 默认会启用这个优化标志位。
 */
public void setWillNotDraw(boolean willNotDraw) {
    setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
}

ViewGroup.java:

private void initViewGroup() {
    // ViewGroup doesn't draw by default
    if (!debugDraw()) {
        setFlags(WILL_NOT_DRAW, DRAW_MASK);
    }
  ...
}

   所以,当我们自定义控件继承于 ViewGroup 并且本身不具备绘制功能时,就可以不管它,默认启用这个优化标志位;但如果我们需要通过 onDraw 来绘制内容时,我们要显式地关闭 WILL_NOT_DRAW 这个标志位,比如可以在其构造方法中调用:

setWillNotDraw(false);

猜你喜欢

转载自blog.csdn.net/yz_cfm/article/details/91047638