美文网首页
View的draw过程详解

View的draw过程详解

作者: 空山Echo | 来源:发表于2019-05-10 15:53 被阅读0次

解决问题:

  1. View和ViewGroup的draw过程
  2. 自定义View 继承 ViewGroup,复写onDraw()方法,onDraw()为什么没有被调用?
  3. 继承ViewGroup的自定义控件如何调用onDraw()绘制内容?

draw过程的大致步骤

draw过程主要是绘制View的外观。ViewGroup除了负责绘制自己之外,还需要负责绘制所有的子View。而不含子View的View对象,就负责绘制自己就可以了。过程的主要流程如下:


image.png
  1. 绘制 backgroud(drawBackground)
  2. 如果需要的话,保存canvas的layer,来准备fading(不是必要的步骤)
  3. 绘制view的content(onDraw方法)
  4. 绘制children(dispatchDraw方法)
  5. 如果需要的话,绘制fading edges,然后还原layer(不是必要的步骤)
  6. 绘制装饰器、比如scrollBar(onDrawForeground)

1.1、 View的draw过程

绘制工作主要从draw方法开始

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 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)
         */

        // Step 1, draw the background, if needed
        int saveCount;

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

        // skip step 2 & 5 if possible (common case)
        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
        if (!verticalEdges && !horizontalEdges) {
            // Step 3, draw the content
            if (!dirtyOpaque) onDraw(canvas);

            // Step 4, draw the children
            dispatchDraw(canvas);

            drawAutofilledHighlight(canvas);

            // Overlay is part of the content and draws beneath Foreground
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }

            // Step 6, draw decorations (foreground, scrollbars)
            onDrawForeground(canvas);

            // Step 7, draw the default focus highlight
            drawDefaultFocusHighlight(canvas);

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

            // we're done...
            return;
        }
  • View中 onDraw是一个空实现,由各种类型的View自定义实现
  • dispatchDraw()负责控制子View绘制的,在view是空实现,但是在ViewGroup中有具体的实现;这是可以理解的,毕竟只有ViewGroup才需要分发绘制这是可以理解的,毕竟只有ViewGroup才需要分发绘制

1.2、 ViewGroup的draw过程

绘制工作主要从dispatchDraw方法开始

// ViewGroup.java
protected void dispatchDraw(Canvas canvas) {
    final int childrenCount = mChildrenCount;
    final View[] children = mChildren;

    for (int i = 0; i < childrenCount; i++) {
        while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
            final View transientChild = mTransientViews.get(transientIndex);
            if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
                transientChild.getAnimation() != null) {
                more |= drawChild(canvas, transientChild, drawingTime);
            }
            transientIndex++;
            if (transientIndex >= transientCount) {
                transientIndex = -1;
            }
        }

        final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
        final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
        if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
            more |= drawChild(canvas, child, drawingTime);
        }
    }
    ...
}

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

可见 dispatchView内部负责遍历子View,分别调用子VIew的draw()。而且我们自定义ViewGroup中一般也是重写此方法完成绘制

2、自定义View 继承 ViewGroup,复写onDraw()方法,onDraw()为什么没有被调用?

  • ViewGroup的源码,ViewGroup里面没有onDraw(Canvas canvas)方法,主要的绘制方法是父类View的draw(Canvas canvas) 方法。
  • 分析draw方法,如果一个View不需要绘制任何内容则开启WILL_NOT_DRAW标记位,系统会进行相应优化,不会调用onDraw方法。View默认没有开启,ViewGroup默认开启
public void draw(Canvas canvas) {
    // Step 3, draw the content 调用onDraw方法
     if (!dirtyOpaque) onDraw(canvas);

    // Step 4, draw the children
     dispatchDraw(canvas);

    // Step 6, draw decorations (foreground, scrollbars)
     onDrawForeground(canvas);
}
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);

initViewGroup方法中是ViewGroup开启了WILL_NOT_DRAW

ViewGroup.java
private void initViewGroup() {
        // ViewGroup doesn't draw by default
        if (!debugDraw()) {
            //这个调用了View的方法.
            setFlags(WILL_NOT_DRAW, DRAW_MASK);
        }
       ......
    }

setFlags方法导致mPrivateFlags 重新赋值 为 true.所以 if (!dirtyOpaque) onDraw(canvas); 这样的onDraw() 方法就进不来了.导致ViewGroup不会调用onDraw() 方法.

3、继承ViewGroup的自定义控件如何调用onDraw()绘制内容?

如果明确知道需要通过onDraw绘制内容两种方法:

  1. 通过setWillNotDraw方法设置WILL_NOT_DRAW 为false
 /**
     * @param willNotDraw whether or not this View draw on its own
     */
public void setWillNotDraw(boolean willNotDraw) {
        setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
    }
  1. 设置背景
    那我们看一下设置背景的方法
    public void setBackground(Drawable background) {
        setBackgroundDrawable(background);
    }

调用了setBackgroundDrawable方法

/**
     * @deprecated use {@link #setBackground(Drawable)} instead
     */
    @Deprecated
    public void setBackgroundDrawable(Drawable background) {
        computeOpaqueFlags();
        ....省略一大段代码
        computeOpaqueFlags();
        ....省略一点点代码
        invalidate(true);
        invalidateOutline();
    }

我们可以发现在setBackgroundDrawable调用了computeOpaqueFlags方法重新计算mPrivateFlags的值.接着调用invalidate方法触发onDraw方法.

相关文章

网友评论

      本文标题:View的draw过程详解

      本文链接:https://www.haomeiwen.com/subject/kavooqtx.html