android繪製draw流程分析

概述:

之前分析過一個activity的啓動到顯示流程,具體顯示流程中分析過測量與佈局流程,這裏對具體的繪製渲染流程進行具體的分析。繪製的起始還是從ViewRootImpl開始,在進入到view之後會根據根據是否有邊緣效果稍作區分主要包括以下幾點:

  1. 繪製背景
  2. 如果存在邊緣效果的話,保存畫布圖層用於給邊緣效果做準備
  3. 調用ondraw實現具體的繪製流程
  4. 繪製子view
  5. 如果存在邊緣效果的話,繪製邊緣效果,恢復畫布圖層
  6. 繪製裝飾(滾動條,前景色之類)

大體流程如下圖:
在這裏插入圖片描述

其實從這個大體流程裏也可以知道爲什麼自定義view的時候,如果是繼承自view,那就必須要自己實現ondraw,因爲從這裏流程裏,在view中並沒有具體的繪製操作,只是繪製了前景、背景、邊緣效果,具體的繪製在ondraw中,ondraw需要子類自己去實現。

ViewRootImpl中主要流程代碼如下:

    private void performDraw() {
        ......
        try {
            //這裏傳入的是true
            draw(fullRedrawNeeded);
        } finally {
            mIsDrawing = false;
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }

        ......
        //這裏是true
        if (mReportNextDraw) {
            mReportNextDraw = false;

            ......
            try {
                //向窗口管理服務發送界面繪製完成的消息
                mWindowSession.finishDrawing(mWindow);
            } catch (RemoteException e) {
            }
        }
    }

draw方法較長,這裏主要關注其進入drawSoftware的流程:

    private void draw(boolean fullRedrawNeeded) {
        ......

        if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
            if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
                ......
                //這裏採用硬件渲染來繪製,會重新走一遍performTraversals流程
                mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
            } else {
                //如果硬件渲染器不爲空,且不可用,則使能它,然後重新繪製之後返回
                if (mAttachInfo.mHardwareRenderer != null &&
                        !mAttachInfo.mHardwareRenderer.isEnabled() &&
                        mAttachInfo.mHardwareRenderer.isRequested()) {

                    try {
                        mAttachInfo.mHardwareRenderer.initializeIfNeeded(
                                mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
                    } catch (OutOfResourcesException e) {
                        handleOutOfResourcesException(e);
                        return;
                    }

                    mFullRedrawNeeded = true;
                    scheduleTraversals();
                    return;
                }
                //否則調用軟件繪製
                if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                    return;
                }
            }
        }

        ......
    }
    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty) {

        //繪製軟件渲染
        ......
        try {
            ......

            dirty.setEmpty();
            mIsAnimating = false;
            mView.mPrivateFlags |= View.PFLAG_DRAWN;

            try {
                canvas.translate(-xoff, -yoff);
                if (mTranslator != null) {
                    mTranslator.translateCanvas(canvas);
                }
                canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
                attachInfo.mSetIgnoreDirtyState = false;
                //這裏的mView就是Decorview
                mView.draw(canvas);

                drawAccessibilityFocusedDrawableIfNeeded(canvas);
            } finally {
                ......
            }
        } finally {
        ......
        return true;
    }

Decorview中的draw方法主要調用了父類的方法,Decorview繼承自FrameLayout,draw方法的實現在其祖父類View中:

    public void draw(Canvas canvas) {
        //第一步,如果沒有陰影效果的話,就繪製背景色
        if (!dirtyOpaque) {
            drawBackground(canvas);
        }

        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
        //如果沒有水平或者數值邊緣漸變效果,流程就如if中
        if (!verticalEdges && !horizontalEdges) {
            //第三步,繪製具體內容,這也是所有繼承自view的子view主要衝在的方法
            if (!dirtyOpaque) onDraw(canvas);

            //第四步,繪製子view,這裏是針對viewgroup類型
            dispatchDraw(canvas);

            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }

            //第六步,繪製前景,如滾動條等
            onDrawForeground(canvas);

            // ok,完成返回
            return;
        }

        //以下處理的是帶有邊緣效果的情況,這也是完整的繪製流程
        boolean drawTop = false;
        boolean drawBottom = false;
        boolean drawLeft = false;
        boolean drawRight = false;

        float topFadeStrength = 0.0f;
        float bottomFadeStrength = 0.0f;
        float leftFadeStrength = 0.0f;
        float rightFadeStrength = 0.0f;

        int paddingLeft = mPaddingLeft;

        final boolean offsetRequired = isPaddingOffsetRequired();
        if (offsetRequired) {
            paddingLeft += getLeftPaddingOffset();
        }
        //獲取繪製邊緣效果的left、top、right、bottom四個值
        int left = mScrollX + paddingLeft;
        int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
        int top = mScrollY + getFadeTop(offsetRequired);
        int bottom = top + getFadeHeight(offsetRequired);

        if (offsetRequired) {
            right += getRightPaddingOffset();
            bottom += getBottomPaddingOffset();
        }

        final ScrollabilityCache scrollabilityCache = mScrollCache;
        final float fadeHeight = scrollabilityCache.fadingEdgeLength;
        int length = (int) fadeHeight;

        //如果頂部和底部淡入淡出重疊,則修剪淡入淡出長度重疊的淡化產生奇特的僞像
        if (verticalEdges && (top + length > bottom - length)) {
            length = (bottom - top) / 2;
        }

        //同樣方式處理水平方向
        if (horizontalEdges && (left + length > right - length)) {
            length = (right - left) / 2;
        }
        //確定邊緣漸變繪製位置
        if (verticalEdges) {
            topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
            drawTop = topFadeStrength * fadeHeight > 1.0f;
            bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
            drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
        }

        if (horizontalEdges) {
            leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
            drawLeft = leftFadeStrength * fadeHeight > 1.0f;
            rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
            drawRight = rightFadeStrength * fadeHeight > 1.0f;
        }

        saveCount = canvas.getSaveCount();

        int solidColor = getSolidColor();
        if (solidColor == 0) {
            final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
            //根據以上獲取的繪製位置來保存相對位置的圖層
            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);
        }

        // 步驟三,進行實際內容的繪製
        if (!dirtyOpaque) onDraw(canvas);

        // 步驟四,繪製子view
        dispatchDraw(canvas);

        //步驟五,繪製邊緣效果,並恢復圖層
        final Paint p = scrollabilityCache.paint;
        final Matrix matrix = scrollabilityCache.matrix;
        final Shader fade = scrollabilityCache.shader;

        if (drawTop) {
            matrix.setScale(1, fadeHeight * topFadeStrength);
            matrix.postTranslate(left, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left, top, right, top + length, p);
        }

        if (drawBottom) {
            matrix.setScale(1, fadeHeight * bottomFadeStrength);
            matrix.postRotate(180);
            matrix.postTranslate(left, bottom);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left, bottom - length, right, bottom, p);
        }

        if (drawLeft) {
            matrix.setScale(1, fadeHeight * leftFadeStrength);
            matrix.postRotate(-90);
            matrix.postTranslate(left, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left, top, left + length, bottom, p);
        }

        if (drawRight) {
            matrix.setScale(1, fadeHeight * rightFadeStrength);
            matrix.postRotate(90);
            matrix.postTranslate(right, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(right - length, top, right, bottom, p);
        }

        canvas.restoreToCount(saveCount);

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

        // 步驟六,繪製前景色
        onDrawForeground(canvas);
    }

到這裏主題流程分析完成,接下來繼續對小流程進行代碼分析:
繪製背景色:drawBackground

    private void drawBackground(Canvas canvas) {
        //獲取背景drawable,若沒有就返回
        final Drawable background = mBackground;
        if (background == null) {
            return;
        }
        //設置背景範圍
        setBackgroundBounds();

        // 如果硬件加速和硬件渲染都使能,則使用顯示列表來繪製渲染節點
        if (canvas.isHardwareAccelerated() && mAttachInfo != null
                && mAttachInfo.mHardwareRenderer != null) {
            mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode);

            final RenderNode renderNode = mBackgroundRenderNode;
            if (renderNode != null && renderNode.isValid()) {
                setBackgroundRenderNodeProperties(renderNode);
                ((DisplayListCanvas) canvas).drawRenderNode(renderNode);
                return;
            }
        }
        //繪製前面獲取到的backgroud背景
        final int scrollX = mScrollX;
        final int scrollY = mScrollY;
        if ((scrollX | scrollY) == 0) {
            background.draw(canvas);
        } else {
            canvas.translate(scrollX, scrollY);
            background.draw(canvas);
            canvas.translate(-scrollX, -scrollY);
        }
    }

繪製具體內容:onDraw

    //view中的ondraw是個空實現,需要子view來實現,比如textview、imageview中都進行了重載,完成具體內容的繪製
    protected void onDraw(Canvas canvas) {
    }

繪製子view:dispatchDraw
該方法實現在viewgroup中,用於繪製子view

    @Override
    protected void dispatchDraw(Canvas canvas) {
        ......
        //遍歷每個子view爲其設置動畫
        if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
            final boolean buildCache = !isHardwareAccelerated();
            for (int i = 0; i < childrenCount; i++) {
                final View child = children[i];
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
                    final LayoutParams params = child.getLayoutParams();
                    attachLayoutAnimationParameters(child, params, i, childrenCount);
                    bindLayoutAnimation(child);
                }
            }
            ......
        }

        ......
        for (int i = 0; i < childrenCount; i++) {
            //優先繪製短暫的子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;
                }
            }

            final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
            final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
            //然後繪製普通的可見view
            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                more |= drawChild(canvas, child, drawingTime);
            }
        }
        while (transientIndex >= 0) {
            // 普通視圖之後可能還有其他臨時視圖
            final View transientChild = mTransientViews.get(transientIndex);
            if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
                    transientChild.getAnimation() != null) {
                more |= drawChild(canvas, transientChild, drawingTime);
            }
            transientIndex++;
            if (transientIndex >= transientCount) {
                break;
            }
        }
        if (preorderedList != null) preorderedList.clear();

        // 繪製正在變爲不可見的有動畫的view
        if (mDisappearingChildren != null) {
            final ArrayList<View> disappearingChildren = mDisappearingChildren;
            final int disappearingCount = disappearingChildren.size() - 1;
            // 遍歷繪製正在變爲不可見的view
            for (int i = disappearingCount; i >= 0; i--) {
                final View child = disappearingChildren.get(i);
                more |= drawChild(canvas, child, drawingTime);
            }
        }
        ......
    }

drawChild又回到了view中的draw方法,對子view進行繪製操作

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

view中的這個draw方法中,主要處理基於圖層類型和硬件加速的渲染行爲,這裏只關注下關鍵代碼:

boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {

    ......
    if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
        mPrivateFlags &= ~PFLAG_DIRTY_MASK;
        dispatchDraw(canvas);
    } else {
        draw(canvas);
    }
    ......
}

至此,繪製流程的簡單分析就結束了,在view的繪製流程中並沒有對界面內容進行繪製,這也就是爲什麼我們自定義view,繼承自view的話,一定要重載ondraw,在ondraw裏做繪製內容的操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章