surfaceview和view繪制的區(qū)別

一顾孽、SurfaceView和View的繪制流程

1.View的繪制流程

View的繪制流程可以分為三個(gè)階段:measure(測(cè)量)末秃、layout(布局)和draw(繪制)笋妥。這三個(gè)階段是從ViewRootImpl的performTraversals()方法開始,自上而下地遍歷整個(gè)View樹,對(duì)每個(gè)View進(jìn)行相應(yīng)的操作秉颗。

measure階段的目的是確定每個(gè)View的寬度和高度。

這個(gè)階段會(huì)調(diào)用每個(gè)View的measure()方法送矩,該方法會(huì)根據(jù)View的LayoutParams和父View的MeasureSpecs來(lái)計(jì)算出View的MeasureSpecs蚕甥,然后傳遞給View的onMeasure()方法,該方法會(huì)根據(jù)View的MeasureSpecs和自身的內(nèi)容來(lái)設(shè)置View的MeasuredWidth和MeasuredHeight栋荸。View的MeasureSpecs是一個(gè)32位的整數(shù)菇怀,其中高2位表示測(cè)量模式(MeasureSpec.EXACTLY、MeasureSpec.AT_MOST或MeasureSpec.UNSPECIFIED)蒸其,低30位表示測(cè)量大小。View的MeasuredWidth和MeasuredHeight是一個(gè)16位的整數(shù)库快,表示View在測(cè)量階段的寬度和高度摸袁。

layout階段的目的是確定每個(gè)View的位置。

這個(gè)階段會(huì)調(diào)用每個(gè)View的layout()方法义屏,該方法會(huì)根據(jù)View的MeasuredWidth和MeasuredHeight以及父View的位置和邊距來(lái)設(shè)置View的Left靠汁、Top、Right和Bottom闽铐。這四個(gè)屬性表示View在父View坐標(biāo)系中的位置和大小蝶怔。View的layout()方法會(huì)調(diào)用View的onLayout()方法,該方法會(huì)根據(jù)View的子View的MeasuredWidth和MeasuredHeight以及View自身的布局規(guī)則來(lái)設(shè)置子View的位置兄墅。View的onLayout()方法是一個(gè)抽象方法踢星,需要由具體的子類來(lái)實(shí)現(xiàn),例如LinearLayout隙咸、RelativeLayout等沐悦。

draw階段的目的是將每個(gè)View的內(nèi)容繪制到屏幕上。

這個(gè)階段會(huì)調(diào)用每個(gè)View的draw()方法五督,該方法會(huì)創(chuàng)建一個(gè)Canvas對(duì)象藏否,該對(duì)象封裝了一個(gè)Bitmap對(duì)象,該Bitmap對(duì)象表示View的繪制緩沖區(qū)充包。View的draw()方法會(huì)調(diào)用View的onDraw()方法副签,該方法會(huì)使用Canvas對(duì)象的繪圖方法來(lái)繪制View的內(nèi)容,例如drawText()基矮、drawBitmap()等淆储。View的draw()方法還會(huì)調(diào)用View的dispatchDraw()方法,該方法會(huì)遍歷View的子View家浇,并調(diào)用子View的draw()方法遏考,從而實(shí)現(xiàn)View樹的遞歸繪制。View的繪制緩沖區(qū)最終會(huì)被合成到屏幕上蓝谨,這個(gè)過(guò)程由硬件加速或軟件渲染來(lái)完成灌具。

下面是View的繪制流程的源碼分析青团,以ViewRootImpl的performTraversals()方法為入口:

void performTraversals() {
    // ..........
    boolean viewScrolled = false;
    // 第一次繪制或者窗口大小發(fā)生變化時(shí),執(zhí)行measure階段
    if (mFirst || mReportNextDraw) {
        mFullRedrawNeeded = true;
        mLayoutRequested = true;
    }
    boolean windowSizeMayChange = false;
    // 獲取窗口的寬度和高度
    int desiredWindowWidth = mWinFrame.width();
    int desiredWindowHeight = mWinFrame.height();
    // ..........
    // 如果需要執(zhí)行measure階段
    if (mLayoutRequested && !mStopped) {
        // ..........
        // 調(diào)用View的measure()方法咖楣,傳入窗口的寬度和高度作為MeasureSpecs
        performMeasure(desiredWindowWidth, desiredWindowHeight);
        // 獲取View的MeasuredWidth和MeasuredHeight
        int width = host.getMeasuredWidth();
        int height = host.getMeasuredHeight();
        // ..........
    }
    // ..........
    // 如果需要執(zhí)行l(wèi)ayout階段
    if ((mLayoutRequested || windowSizeMayChange) && !mStopped) {
        // ..........
        // 調(diào)用View的layout()方法督笆,傳入View的Left、Top诱贿、Right和Bottom
        performLayout(lp, desiredWindowWidth, desiredWindowHeight);
        // ..........
    }
    // ..........
    // 如果需要執(zhí)行draw階段
    if (!mStopped) {
        // ..........
        // 調(diào)用View的draw()方法娃肿,傳入一個(gè)Canvas對(duì)象
        performDraw(canvas);
        // ..........
    }
    // ..........
}

View繪制流程示意圖

2.SurfaceView的繪制流程

SurfaceView的繪制流程可以分為兩個(gè)階段:create(創(chuàng)建)和draw(繪制)。這兩個(gè)階段是通過(guò)SurfaceHolder的回調(diào)方法來(lái)觸發(fā)的珠十,create階段的目的是創(chuàng)建一個(gè)Surface料扰,draw階段的目的是在Surface上繪制內(nèi)容。

create階段

調(diào)用SurfaceHolder.Callback的surfaceCreated()和surfaceChanged()方法焙蹭,這兩個(gè)方法會(huì)在Surface被創(chuàng)建或者改變時(shí)被調(diào)用晒杈。在這個(gè)階段,可以獲取SurfaceHolder對(duì)象孔厉,該對(duì)象封裝了一個(gè)Surface對(duì)象拯钻,該Surface對(duì)象表示SurfaceView的繪制緩沖區(qū)∽颍可以在這個(gè)階段創(chuàng)建一個(gè)繪制線程粪般,并將SurfaceHolder對(duì)象傳遞給該線程,以便在后臺(tái)進(jìn)行繪制操作污桦。

draw階段

調(diào)用SurfaceHolder.Callback的surfaceDestroyed()方法亩歹,該方法會(huì)在Surface被銷毀時(shí)被調(diào)用。在這個(gè)階段凡橱,可以停止繪制線程捆憎,并釋放SurfaceHolder對(duì)象。繪制線程可以在運(yùn)行時(shí)獲取SurfaceHolder對(duì)象的鎖梭纹,然后獲取一個(gè)Canvas對(duì)象躲惰,該對(duì)象封裝了Surface對(duì)象,然后使用Canvas對(duì)象的繪圖方法來(lái)繪制內(nèi)容变抽,例如drawText()础拨、drawBitmap()等。繪制完成后绍载,需要釋放Canvas
下面是SurfaceView的繪制流程的源碼分析诡宗,以SurfaceView的init()方法為入口:

private void init() {
    // ..........
    // 創(chuàng)建一個(gè)SurfaceHolder對(duì)象,該對(duì)象封裝了一個(gè)Surface對(duì)象
    mSurfaceHolder = new SurfaceHolder() {
        // ..........
        // 獲取Surface對(duì)象的鎖
        @Override
        public Canvas lockCanvas() {
            return internalLockCanvas(null, false);
        }
        // ..........
        // 釋放Surface對(duì)象的鎖击儡,并將繪制內(nèi)容合成到屏幕上
        @Override
        public void unlockCanvasAndPost(Canvas canvas) {
            mSurface.unlockCanvasAndPost(canvas);
            // ..........
        }
        // ..........
        // 添加SurfaceHolder.Callback對(duì)象塔沃,用于監(jiān)聽Surface的狀態(tài)變化
        @Override
        public void addCallback(Callback callback) {
            synchronized (mCallbacks) {
                // This is a linear search, but in practice we'll
                // have only a couple callbacks, so it doesn't matter.
                if (mCallbacks.contains(callback) == false) {
                    mCallbacks.add(callback);
                }
            }
        }
        // ..........
    };
    // ..........
}
SurfaceView繪制流程示意圖

二、SurfaceView和View的繪制原理

1.View的繪制原理

View的繪制是基于Window的Surface來(lái)實(shí)現(xiàn)的阳谍,Window的Surface是一個(gè)原生的緩沖區(qū)蛀柴,用來(lái)保存當(dāng)前窗口的像素?cái)?shù)據(jù)螃概,它是由屏幕顯示內(nèi)容合成器(Screen Compositor)所管理的。屏幕顯示內(nèi)容合成器是一個(gè)系統(tǒng)級(jí)的服務(wù)鸽疾,它負(fù)責(zé)將所有Window的Surface合成到屏幕上吊洼,形成最終的顯示效果。屏幕顯示內(nèi)容合成器的具體實(shí)現(xiàn)可能有所不同制肮,例如SurfaceFlinger冒窍、HWComposer等,但它們的基本原理都是類似的豺鼻。


View使用的Canvas繪制合成思維導(dǎo)圖

View的繪制過(guò)程中综液,會(huì)通過(guò)Window的lockCanvas方法,鎖定Window的Surface中的Canvas對(duì)象儒飒,用來(lái)在Surface上繪制View的內(nèi)容谬莹,繪制完成后,會(huì)通過(guò)unlockCanvasAndPost方法约素,解鎖Surface中的Canvas對(duì)象届良,并將繪制結(jié)果提交到Surface的緩沖區(qū)中笆凌。Surface會(huì)將緩沖區(qū)中的內(nèi)容交給屏幕顯示內(nèi)容合成器圣猎,由它來(lái)將Surface的內(nèi)容合成到屏幕上,完成繪制流程乞而。

下面我們重點(diǎn)分析draw階段的源碼送悔,draw階段的源碼如下:

    private void performDraw(Canvas canvas) {
        // .............
        // 如果沒(méi)有傳入Canvas對(duì)象,就從Window的Surface中
        // 獲取一個(gè)Canvas對(duì)象爪模,用于在Surface上繪制View的內(nèi)容
        if (canvas == null) {
            // If the view hierarchy contains a SurfaceView that is not
            // updated (asynchronous mode), we will use the previous frame
            // to render the view hierarchy. This means we won't see the
            // updated state of the SurfaceView but there is no way around
            // this, unless we were to wait for the new frame to be ready.
            // But then, we would end up with a blank frame and drop below
            // 60 fps.
            if (mAttachInfo.mThreadedRenderer != null) {
                canvas = mAttachInfo.mThreadedRenderer.getCanvas();
            } else {
                canvas = mSurface.lockCanvas(mDirty);
            }
        }
        // .............
        // 調(diào)用View的draw()方法欠啤,傳入Canvas對(duì)象,用于在Surface上繪制View的內(nèi)容屋灌,
        // 該方法會(huì)先繪制View的背景洁段,然后根據(jù)是否需要邊緣漸變效果,
        // 調(diào)用onDraw方法或者drawWithFadingEdges方法共郭,用于在Surface上繪制View的內(nèi)容祠丝,
        // 然后調(diào)用dispatchDraw方法,用于在Surface上繪制View的子View的內(nèi)容除嘹,
        // 然后調(diào)用onDrawForeground方法写半,用于在Surface上繪制View的前景,
        // 最后調(diào)用debugDrawFocus方法尉咕,用于在Surface上繪制View的焦點(diǎn)狀態(tài)
        mAttachInfo.mView.draw(canvas);
        // .............
        // 如果從Window的Surface中獲取了Canvas對(duì)象叠蝇,
        // 就釋放Surface中的Canvas對(duì)象,
        // 并將繪制結(jié)果提交到Surface的緩沖區(qū)中
        if (mAttachInfo.mThreadedRenderer == null) {
            mSurface.unlockCanvasAndPost(canvas);
        }
        // .............
    }

如果沒(méi)有傳入Canvas對(duì)象年缎,就從Window的Surface中獲取一個(gè)Canvas對(duì)象悔捶,用于在Surface上繪制View的內(nèi)容铃慷,該過(guò)程是通過(guò)Surface的lockCanvas方法實(shí)現(xiàn)的,該方法會(huì)返回一個(gè)Canvas對(duì)象炎功,用于在Surface上繪制內(nèi)容枚冗。該方法的源碼如下:

public Canvas lockCanvas(Rect inOutDirty) throws Surface.OutOfResourcesException, 
IllegalArgumentException {
    synchronized (mLock) {
        checkNotReleasedLocked();
        if (mLockedObject != 0) {
            throw new IllegalArgumentException("Surface was already locked");
        }
        mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
        return mCanvas;
    }
}

lockCanvas方法主要做了以下幾件事:

  1. 同步鎖定mLock對(duì)象,用于保證線程安全蛇损。

  2. 檢查Surface是否已經(jīng)被釋放赁温,如果是,拋出異常淤齐。

  3. 檢查Surface是否已經(jīng)被鎖定股囊,如果是,拋出異常更啄。

  4. 調(diào)用nativeLockCanvas方法稚疹,傳入mNativeObject、mCanvas和inOutDirty參數(shù)祭务,該方法會(huì)返回一個(gè)mLockedObject對(duì)象内狗,用于標(biāo)識(shí)Surface的鎖定狀態(tài)。

  5. 返回mCanvas對(duì)象义锥,用于在Surface上繪制內(nèi)容柳沙。

調(diào)用View的draw()方法,傳入Canvas對(duì)象拌倍,用于在Surface上繪制View的內(nèi)容赂鲤,draw()方法會(huì)先繪制View的背景,然后根據(jù)是否需要邊緣漸變效果柱恤,調(diào)用onDraw方法或者drawWithFadingEdges方法数初,用于在Surface上繪制View的內(nèi)容,然后調(diào)用dispatchDraw方法梗顺,用于在Surface上繪制View的子View的內(nèi)容泡孩,然后調(diào)用onDrawForeground方法,用于在Surface上繪制View的前景寺谤,最后調(diào)用debugDrawFocus方法仑鸥,用于在Surface上繪制View的焦點(diǎn)狀態(tài)。該方法的源碼如下:

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;

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

        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        if (!verticalEdges && !horizontalEdges) {
            // common case onDraw(canvas); 
        } else {
            // …
        }

        dispatchDraw(canvas);

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

        onDrawForeground(canvas);

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

draw()方法主要做了以下幾件事:

  1. 清除View的臟標(biāo)志矗漾,設(shè)置View的繪制標(biāo)志锈候,表示View已經(jīng)被繪制過(guò)。
  2. 如果View不是完全不透明的敞贡,繪制View的背景泵琳,該方法會(huì)調(diào)用Drawable的draw方法,傳入Canvas對(duì)象,用于在Surface上繪制Drawable的內(nèi)容获列。
  3. 根據(jù)View的邊緣漸變標(biāo)志谷市,判斷是否需要繪制邊緣漸變效果,如果不需要击孩,直接調(diào)用onDraw方法迫悠,傳入Canvas對(duì)象,用于在Surface上繪制View的內(nèi)容巩梢,該方法是一個(gè)空方法创泄,一般由子類重寫,實(shí)現(xiàn)具體的繪制邏輯括蝠,例如TextView會(huì)繪制文本鞠抑,ImageView會(huì)繪制圖片等。如果需要忌警,調(diào)用drawWithFadingEdges方法搁拙,傳入Canvas對(duì)象,用于在Surface上繪制帶有邊緣漸變效果的View的內(nèi)容法绵,該方法會(huì)先保存Canvas的狀態(tài)箕速,然后根據(jù)邊緣漸變的方向,裁剪Canvas的區(qū)域朋譬,然后調(diào)用onDraw方法盐茎,傳入Canvas對(duì)象,用于在Surface上繪制View的內(nèi)容此熬,最后恢復(fù)Canvas的狀態(tài)庭呜。
  4. 調(diào)用dispatchDraw方法滑进,傳入Canvas對(duì)象犀忱,用于在Surface上繪制View的子View的內(nèi)容,該方法會(huì)遍歷View的子View列表扶关,根據(jù)它們的可見性阴汇、透明度、動(dòng)畫等屬性节槐,決定是否需要繪制搀庶,如果需要,就調(diào)用子View的draw方法铜异,傳入Canvas對(duì)象哥倔,用于在Surface上繪制子View的內(nèi)容,該方法是一個(gè)遞歸的過(guò)程揍庄,直到所有的子View都被繪制完畢咆蒿。
  5. 如果View有Overlay,且Overlay不為空,調(diào)用Overlay的getOverlayView方法沃测,獲取Overlay的View對(duì)象缭黔,然后調(diào)用Overlay的View對(duì)象的dispatchDraw方法,傳入Canvas對(duì)象蒂破,用于在Surface上繪制Overlay的內(nèi)容馏谨,該方法與View的dispatchDraw方法類似,也是一個(gè)遞歸的過(guò)程附迷,直到所有的Overlay的子View都被繪制完畢惧互。
  6. 調(diào)用onDrawForeground方法,傳入Canvas對(duì)象喇伯,用于在Surface上繪制View的前景壹哺,該方法會(huì)繪制View的滾動(dòng)條、前景Drawable等內(nèi)容艘刚。
  7. 如果開啟了調(diào)試模式管宵,調(diào)用debugDrawFocus方法,傳入Canvas對(duì)象攀甚,用于在Surface上繪制View的焦點(diǎn)狀態(tài)巫糙,該方法會(huì)繪制View的邊框、焦點(diǎn)框等內(nèi)容哼勇。

如果從Window的Surface中獲取了Canvas對(duì)象惯疙,就釋放Surface中的Canvas對(duì)象,并將繪制結(jié)果提交到Surface的緩沖區(qū)中荚斯,該過(guò)程是通過(guò)Surface的unlockCanvasAndPost方法實(shí)現(xiàn)的埠居,該方法會(huì)釋放Surface中的Canvas對(duì)象,并將繪制結(jié)果提交到Surface的緩沖區(qū)中事期。unlockCanvasAndPost方法的源碼如下:

public void unlockCanvasAndPost(Canvas canvas) {
    synchronized (mLock) {
        checkNotReleasedLocked();
        if (mLockedObject == 0) {
            throw new IllegalStateException("Surface was not locked");
        }
        nativeUnlockCanvasAndPost(mNativeObject, canvas);
        nativeRelease(mLockedObject);
        mLockedObject = 0;
    }
}

unlockCanvasAndPost方法主要做了以下幾件事:

  1. 同步鎖定mLock對(duì)象滥壕,用于保證線程安全。

  2. 檢查Surface是否已經(jīng)被釋放兽泣,如果是绎橘,拋出異常。

  3. 檢查Surface是否已經(jīng)被鎖定唠倦,如果否称鳞,拋出異常。

  4. 調(diào)用nativeUnlockCanvasAndPost方法稠鼻,傳入mNativeObject和canvas參數(shù)冈止,該方法會(huì)將繪制結(jié)果提交到Surface的緩沖區(qū)中,并通知SurfaceFlinger進(jìn)行合成候齿。

  5. 調(diào)用nativeRelease方法熙暴,傳入mLockedObject參數(shù)苫亦,該方法會(huì)釋放Surface的鎖定狀態(tài),并減少Surface的強(qiáng)引用計(jì)數(shù)怨咪。

Surface會(huì)將緩沖區(qū)中的內(nèi)容交給屏幕顯示內(nèi)容合成器屋剑,由它來(lái)將Surface的內(nèi)容合成到屏幕上,完成繪制流程诗眨。屏幕顯示內(nèi)容合成器是一個(gè)系統(tǒng)級(jí)的服務(wù)唉匾,它負(fù)責(zé)將所有Window的Surface和其他層合成到屏幕上,形成最終的顯示效果匠楚。屏幕顯示內(nèi)容合成器的源碼位于frameworks/native/services/surfaceflinger目錄下巍膘,它是一個(gè)C++的程序,使用OpenGL ES來(lái)進(jìn)行圖形渲染芋簿。屏幕顯示內(nèi)容合成器的主要類和方法如下:

  • SurfaceFlinger類峡懈,它是屏幕顯示內(nèi)容合成器的核心類,它管理著所有的Surface和Layer与斤,以及與客戶端的通信和與硬件的交互肪康。

  • SurfaceFlinger::onMessageReceived方法,它是SurfaceFlinger的消息處理方法撩穿,它會(huì)根據(jù)不同的消息類型磷支,執(zhí)行不同的操作,例如創(chuàng)建Surface食寡、銷毀Surface雾狈、更新Surface、合成Surface等抵皱。

  • SurfaceFlinger::doComposition方法善榛,它是SurfaceFlinger的合成方法,它會(huì)遍歷所有的Layer呻畸,根據(jù)它們的Z-order(層級(jí)順序)移盆,將它們的內(nèi)容繪制到一個(gè)FrameBuffer對(duì)象上,然后將FrameBuffer對(duì)象的內(nèi)容顯示到屏幕上擂错。

View繪制原理示意圖

2.SurfaceView的繪制原理

SurfaceView的繪制原理是基于SurfaceView自己的Surface來(lái)實(shí)現(xiàn)的味滞,SurfaceView的Surface是一個(gè)獨(dú)立于Window的Surface樱蛤,用來(lái)展示Surface中的數(shù)據(jù)钮呀。

SurfaceView使用的Canvas繪制合成思維導(dǎo)圖

以SurfaceView的init()方法為入口,init()方法是SurfaceView的初始化方法昨凡,它會(huì)創(chuàng)建一個(gè)SurfaceHolder對(duì)象爽醋,用于管理SurfaceView的Surface,以及一個(gè)SurfaceViewUpdateThread對(duì)象便脊,用于在非UI線程中更新SurfaceView的內(nèi)容蚂四。

init()方法的源碼如下:

private void init() {
    // .............
    // 創(chuàng)建一個(gè)SurfaceHolder對(duì)象,用于管理SurfaceView的Surface
    mSurfaceHolder = new SurfaceHolder() {
        // .............
    };
    // 創(chuàng)建一個(gè)SurfaceViewUpdateThread對(duì)象,用于在非UI線程中更新SurfaceView的內(nèi)容
    mSurfaceViewUpdateThread = new SurfaceViewUpdateThread();
    // .............
}

init()方法主要做了以下幾件事:

  • 創(chuàng)建一個(gè)SurfaceHolder對(duì)象遂赠,用于管理SurfaceView的Surface久妆,該對(duì)象提供了一些方法,用于獲取跷睦、鎖定筷弦、解鎖和銷毀Surface,以及設(shè)置Surface的格式抑诸、尺寸烂琴、回調(diào)等屬性。
  • 創(chuàng)建一個(gè)SurfaceViewUpdateThread對(duì)象蜕乡,用于在非UI線程中更新SurfaceView的內(nèi)容奸绷,該對(duì)象是一個(gè)繼承自Thread的子類,它重寫了run()方法层玲,用于在循環(huán)中不斷地繪制SurfaceView的內(nèi)容号醉。

下面重點(diǎn)分析SurfaceView的繪制過(guò)程,通過(guò)SurfaceHolder的lockCanvas方法辛块,鎖定SurfaceView的Surface中的Canvas對(duì)象扣癣,用來(lái)在Surface上繪制內(nèi)容,繪制完成后憨降,會(huì)通過(guò)unlockCanvasAndPost方法父虑,解鎖Surface中的Canvas對(duì)象,并將繪制結(jié)果提交到Surface的緩沖區(qū)中授药。SurfaceFlinger會(huì)將SurfaceView的Surface和其他層合成到屏幕上士嚎,完成繪制流程。

繪制過(guò)程的源碼如下:

class SurfaceViewUpdateThread extends Thread {
    // .............
    @Override
    public void run() {
        // .............
        // 循環(huán)繪制SurfaceView的內(nèi)容
        while (mRunning) {
            // .............
            // 通過(guò)SurfaceHolder的lockCanvas方法悔叽,鎖定SurfaceView的Surface中的Canvas對(duì)象莱衩,用來(lái)在Surface上繪制內(nèi)容
            Canvas canvas = mSurfaceHolder.lockCanvas();
            if (canvas != null) {
                // .............
                // 在Canvas上繪制SurfaceView的內(nèi)容,例如娇澎,使用Paint對(duì)象繪制顏色笨蚁、文本、圖形等
                canvas.drawColor(Color.BLACK);
                canvas.drawText("Hello, SurfaceView!", 100, 100, mPaint);
                // .............
                // 通過(guò)SurfaceHolder的unlockCanvasAndPost方法趟庄,解鎖Surface中的Canvas對(duì)象括细,并將繪制結(jié)果提交到Surface的緩沖區(qū)中
                mSurfaceHolder.unlockCanvasAndPost(canvas);
            }
            // .............
        }
    }
}


繪制過(guò)程主要做了以下兩件事:

  • 循環(huán)繪制SurfaceView的內(nèi)容,直到線程停止或者Surface被銷毀戚啥。

  • 通過(guò)SurfaceHolder的lockCanvas方法奋单,鎖定SurfaceView的Surface中的Canvas對(duì)象,用來(lái)在Surface上繪制內(nèi)容猫十,lockCanvas方法會(huì)返回一個(gè)Canvas對(duì)象览濒,用于在Surface上繪制內(nèi)容呆盖。

lockCanvas方法的源碼如下:

public Canvas lockCanvas() {
    return internalLockCanvas(null, false);
}

private final Canvas internalLockCanvas(Rect dirty, boolean hardware) {
    // .............
    // 調(diào)用Surface的lockCanvas方法,傳入dirty參數(shù)贷笛,該方法會(huì)返回一個(gè)Canvas對(duì)象应又,用于在Surface上繪制內(nèi)容
    Canvas c = mSurface.lockCanvas(dirty);
    // .............
    return c;
}


lockCanvas方法主要做了以下兩件事:

  • 調(diào)用Surface的lockCanvas方法,傳入dirty參數(shù)乏苦,該方法會(huì)返回一個(gè)Canvas對(duì)象丁频,用于在Surface上繪制內(nèi)容,該方法的源碼與前面分析的Window的Surface的lockCanvas方法相同邑贴,不再贅述席里。

  • 在Canvas上繪制SurfaceView的內(nèi)容,例如拢驾,使用Paint對(duì)象繪制顏色奖磁、文本、圖形等繁疤,該過(guò)程與普通的View的繪制過(guò)程類似咖为,不再贅述。

通過(guò)SurfaceHolder的unlockCanvasAndPost方法稠腊,解鎖Surface中的Canvas對(duì)象躁染,并將繪制結(jié)果提交到Surface的緩沖區(qū)中,unlockCanvasAndPost方法會(huì)釋放Surface中的Canvas對(duì)象架忌,并將繪制結(jié)果提交到Surface的緩沖區(qū)中吞彤。

unlockCanvasAndPost方法的源碼如下:


public void unlockCanvasAndPost(Canvas canvas) {
    // .............
    // 調(diào)用Surface的unlockCanvasAndPost方法,傳入canvas參數(shù)叹放,該方法會(huì)釋放Surface中的Canvas對(duì)象饰恕,并將繪制結(jié)果提交到Surface的緩沖區(qū)中
    mSurface.unlockCanvasAndPost(canvas);
    // .............
}

unlockCanvasAndPost方法主要做了以下兩件事:

  • 調(diào)用Surface的unlockCanvasAndPost方法,傳入canvas參數(shù)井仰,該方法會(huì)釋放Surface中的Canvas對(duì)象埋嵌,并將繪制結(jié)果提交到Surface的緩沖區(qū)中,該方法的源碼與前面分析的Window的Surface的unlockCanvasAndPost方法相同俱恶,不再贅述雹嗦。

  • SurfaceFlinger會(huì)將SurfaceView的Surface和其他層合成到屏幕上,完成繪制流程合是,該過(guò)程與前面分析的Window的Surface的合成過(guò)程類似了罪,不再贅述。

SurfaceView繪制原理示意圖

三端仰、SurfaceView和View內(nèi)的Canvas的區(qū)別

View使用的Canvas SurfaceView使用的Canvas
所屬的Surface Window的Surface SurfaceView自己的Surface
繪制線程 只能在UI線程中繪制 可以在獨(dú)立的線程中繪制
繪制時(shí)機(jī) 受到View樹的測(cè)量捶惜、布局和繪制的影響,需要等待Window的刷新 不受View的繪制流程的控制荔烧,可以在任何時(shí)候進(jìn)行繪制
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吱七,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子鹤竭,更是在濱河造成了極大的恐慌踊餐,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,002評(píng)論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件臀稚,死亡現(xiàn)場(chǎng)離奇詭異吝岭,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)吧寺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門窜管,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人稚机,你說(shuō)我怎么就攤上這事幕帆。” “怎么了赖条?”我有些...
    開封第一講書人閱讀 169,787評(píng)論 0 365
  • 文/不壞的土叔 我叫張陵失乾,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我纬乍,道長(zhǎng)碱茁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,237評(píng)論 1 300
  • 正文 為了忘掉前任仿贬,我火速辦了婚禮纽竣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘茧泪。我一直安慰自己退个,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,237評(píng)論 6 398
  • 文/花漫 我一把揭開白布调炬。 她就那樣靜靜地躺著语盈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缰泡。 梳的紋絲不亂的頭發(fā)上刀荒,一...
    開封第一講書人閱讀 52,821評(píng)論 1 314
  • 那天,我揣著相機(jī)與錄音棘钞,去河邊找鬼缠借。 笑死,一個(gè)胖子當(dāng)著我的面吹牛宜猜,可吹牛的內(nèi)容都是我干的泼返。 我是一名探鬼主播,決...
    沈念sama閱讀 41,236評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼姨拥,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼绅喉!你這毒婦竟也來(lái)了渠鸽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,196評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤柴罐,失蹤者是張志新(化名)和其女友劉穎徽缚,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體革屠,經(jīng)...
    沈念sama閱讀 46,716評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡凿试,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,794評(píng)論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了似芝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片那婉。...
    茶點(diǎn)故事閱讀 40,928評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖党瓮,靈堂內(nèi)的尸體忽然破棺而出详炬,到底是詐尸還是另有隱情,我是刑警寧澤麻诀,帶...
    沈念sama閱讀 36,583評(píng)論 5 351
  • 正文 年R本政府宣布痕寓,位于F島的核電站,受9級(jí)特大地震影響蝇闭,放射性物質(zhì)發(fā)生泄漏呻率。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,264評(píng)論 3 336
  • 文/蒙蒙 一呻引、第九天 我趴在偏房一處隱蔽的房頂上張望礼仗。 院中可真熱鬧,春花似錦逻悠、人聲如沸元践。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)单旁。三九已至,卻和暖如春饥伊,著一層夾襖步出監(jiān)牢的瞬間象浑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工琅豆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留愉豺,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,378評(píng)論 3 379
  • 正文 我出身青樓茫因,卻偏偏與公主長(zhǎng)得像蚪拦,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,937評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容