View draw流程分析

前言

本文主要分析View的draw的流程

View draw

SDK中的注释很详细

  public void draw(Canvas canvas) {
        final int privateFlags = mPrivateFlags;
        //判断是否为非法invalidate请求,我们平时调用invalidate即为合法的,即返回false
        final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
        //重置标志位为合法invalidate以及需要draw
        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;
       //表示ScrollBar在不使用时,是否渐变消失,默认为true
        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);

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

            // we're done...
            return;
        }
     .......非正常情况的代码省略
}

主要看Step.4的dispatchDraw方法,该方法用于绘制child,View中的dispatchDraw方法为空实现,因为View没有child,接下来看看ViewGroup的dispatchDraw方法:

  @Override
 protected void dispatchDraw(Canvas canvas) {
               ....................
  for (int i = 0; i < childrenCount; i++) {
     //1.先画暂态View,具体解释看下文
     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;
                }
            }
         
            int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
            final View child = (preorderedList == null)
                    ? children[childIndex] : preorderedList.get(childIndex);
            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                //2.画child,drawChild
                more |= drawChild(canvas, child, drawingTime);
            }
        }
...........
}
 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        return child.draw(canvas, this, drawingTime);
    }

注释1:
暂态View即通过ViewGroup# addTransientView方法添加的View,该View无法获得焦点,不能响应事件等,它纯粹只用于显示效果,类似补间动画中的view,无法响应事件。
总结:
View的draw主要分6个步骤,正常情况下只有4步,省略步骤2、5:
1.画背景
2.条件满足则:saveLayer,为渐变消失准备
3.画自己内容(平时开发当中的onDraw)
4.画Child(通过调用ViewGroup的dispatchDraw方法)
5.条件满足则:画scrollbar渐变消失,restoreLayer
6.画前景

    原文作者:GrayMonkey
    原文地址: https://www.jianshu.com/p/07849f5b9b6b
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞