View draw流程分析

weixin_34321977發表於2017-09-04

前言

本文主要分析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.畫前景

相關文章