Android學習筆記---深入理解View#05

分類:技術 時間:2016-10-24

本篇終于來到了View的三大流程的最后一個流程了,本次會帶著大家探究一下View的 performDraw() 究竟是如何工作的。

先看performDraw()

由于 performDraw() 的代碼并不多,那我們就先看看它的實現吧!

private void performDraw() {
        if (mAttachInfo.mDisplayState == Display.STATE_OFF amp;amp; !mReportNextDraw) {
            return;
        }

        final boolean fullRedrawNeeded = mFullRedrawNeeded;
        mFullRedrawNeeded = false;

        mIsDrawing = true;
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, quot;drawquot;);
        try {
            // 1. 調用繪制方法
            draw(fullRedrawNeeded);
        } finally {
            mIsDrawing = false;
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }

        // For whatever reason we didn't create a HardwareRenderer, end any
        // hardware animations that are now dangling
        if (mAttachInfo.mPendingAnimatingRenderNodes != null) {
            final int count = mAttachInfo.mPendingAnimatingRenderNodes.size();
            for (int i = 0; i lt; count; i  ) {
                mAttachInfo.mPendingAnimatingRenderNodes.get(i).endAllAnimators();
            }
            mAttachInfo.mPendingAnimatingRenderNodes.clear();
        }

        if (mReportNextDraw) {
            mReportNextDraw = false;
            if (mAttachInfo.mHardwareRenderer != null) {
                mAttachInfo.mHardwareRenderer.fence();
            }

            if (LOCAL_LOGV) {
                Log.v(TAG, quot;FINISHED DRAWING: quot;   mWindowAttributes.getTitle());
            }
            if (mSurfaceHolder != null amp;amp; mSurface.isValid()) {
                mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);
                SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
                if (callbacks != null) {
                    for (SurfaceHolder.Callback c : callbacks) {
                        if (c instanceof SurfaceHolder.Callback2) {
                            ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
                                    mSurfaceHolder);
                        }
                    }
                }
            }
            try {
                mWindowSession.finishDrawing(mWindow);
            } catch (RemoteException e) {
            }
        }
    }

從上面的代碼可以看出,View的繪制流程并不復雜,其中最主要的就是調用了 draw() 方法。那我們來看看 draw() 方法的代碼。我們只探究繪制流程的基本過程,所以我只把關鍵的代碼貼出。其實其中關鍵的代碼也就那么一句。

private void draw(boolean fullRedrawNeeded) {
......
     mAttachInfo.mTreeObserver.dispatchOnDraw();
......
}

android 就是通過上面的那句代碼將繪制的任務分發到 view tree 中的每一個View中。那我們再來看看這個 ViewTreeObserver 對象是什么。下面是該類的官方注釋聲明。

/**
 * A view tree observer is used to register listeners that can be notified of global
 * changes in the view tree. Such global events include, but are not limited to,
 * layout of the whole tree, beginning of the drawing pass, touch mode change....
 *
 * A ViewTreeObserver should never be instantiated by applications as it is provided
 * by the views hierarchy. Refer to {@link android.view.View#getViewTreeObserver()}
 * for more information.
 */
public final class ViewTreeObserver

ViewTreeObserver 用于注冊關于 view tree 的各種事件的監聽器,例如繪制事件,布局事件,滾動事件等等。 View 的繪制過程就是通過 ViewTreeObserver 注冊監聽來進行事件分發的。

/**
     * Interface definition for a callback to be invoked when the view tree is about to be drawn.
     */
    public interface OnDrawListener {
        /**
         * lt;pgt;Callback method to be invoked when the view tree is about to be drawn. At this point,
         * views cannot be modified in any way.lt;/pgt;
         * 
         * lt;pgt;Unlike with {@link OnPreDrawListener}, this method cannot be used to cancel the
         * current drawing pass.lt;/pgt;
         * 
         * lt;pgt;An {@link OnDrawListener} listener lt;stronggt;cannot be added or removedlt;/stronggt;
         * from this method.lt;/pgt;
         *
         * @see android.view.View#onMeasure
         * @see android.view.View#onLayout
         * @see android.view.View#onDraw
         */
        public void onDraw();
    }
/**
* Notifies registered listeners that the drawing pass is about to start.
*/
public final void dispatchOnDraw() {
        if (mOnDrawListeners != null) {
            final ArrayListlt;OnDrawListenergt; listeners = mOnDrawListeners;
            int numListeners = listeners.size();
            for (int i = 0; i lt; numListeners;   i) {
                listeners.get(i).onDraw();
          }
     }
}

通過監聽器來調用 view tree 中的各個View的 onDraw() 方法。

View的draw()

到這里我們基本的 performDraw() 的流程已經清楚了。現在我們再回到 View 類中來看看它的 draw() 是如何實現的。下面就是 View 中的 draw() 方法的代碼。

/**
     * Manually render this view (and all of its children) to the given Canvas.
     * The view must have already done a full layout before this function is
     * called.  When implementing a view, implement
     * {@link #onDraw(android.graphics.Canvas)} instead of overriding this method.
     * If you do need to override this method, call the superclass version.
     *
     * @param canvas The Canvas to which the View is rendered.
     */
    @CallSuper
    public void draw(Canvas canvas) {
        final int privateFlags = mPrivateFlags;
        final boolean dirtyOpaque = (privateFlags amp; PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE amp;amp;
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
        mPrivateFlags = (privateFlags amp; ~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 amp; 5 if possible (common case)
        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags amp; FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags amp; FADING_EDGE_VERTICAL) != 0;
        if (!verticalEdges amp;amp; !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 amp;amp; !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }

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

            // we're done...
            return;
        }

代碼中的注釋說了,一次繪制的流程必須有序的執行下面的六步的操作:

  1. 繪制背景
  2. 必要時保存 canvas 層來為View的fading準備
  3. 繪制View的內容
  4. 繪制View的孩子
  5. 必要時繪制View的fading并且恢復第2步保存的 canvas
  6. 繪制View的decoration,例如前景和滾動條

以上的就是View的繪制步驟。當我們需要繼承一個View并需要對View的繪制進行自定義時,我們只需要完成第3步就可以了,其他的步驟 View 已經幫我們完成了。我們只需重寫View的 onDraw() 方法即可。

總結

View 的基本繪制過程算是比較簡單吧,按照慣例我們也用張圖片來進行總結。


Tags: 安卓開發

文章來源:http://www.jianshu.com/p/bd9780a967a9


ads
ads

相關文章
ads

相關文章

ad