Android:SurfaceView 的使用(附代碼模板)

前言

摘自《Android群英傳》

Android提供了View進(jìn)行繪圖處理坤次,View可以滿足大部分的繪圖需求古劲,但在某些時候也會心有余而力不足斥赋。我們知道,View通過刷新來重繪視圖产艾,Android 系統(tǒng)通過發(fā)出VSYNC信號來進(jìn)行屏幕的重繪疤剑,刷新的時間間隔為16ms滑绒。如果在16ms內(nèi)View完成了你所需要執(zhí)行的所有操作,那么用戶在視覺上就不會產(chǎn)生卡頓的感覺隘膘;而如果執(zhí)行的操作邏輯太多疑故,特別是需要頻繁刷新的界面上,例如游戲界面弯菊,就會不斷阻塞主線程纵势,從而導(dǎo)致畫面卡頓。很多情況下管钳,在自定義View的log中會看到如下的警告:
“Skipped 47 frames! The application may be doing too much work on its main thread.”
為了避免這一問題的產(chǎn)生钦铁,Android系統(tǒng)提供了SurfaceView組件。

View 和 SurfaceView 的區(qū)別

  • View 主要適用于主動更新的情況下才漆,而 SurfaceView 主要適用于被動更新牛曹,例如頻繁地刷新。
  • View 在主線程中對畫面進(jìn)行刷新醇滥,而 SurfaceView 通常會通過一個子線程來進(jìn)行頁面的刷新黎比。
  • View 在繪圖時沒有使用雙緩沖機(jī)制,而 SurfaceView 在底層實現(xiàn)機(jī)制中就已經(jīng)實現(xiàn)了雙緩沖機(jī)制鸳玩。
    總結(jié)就是阅虫,如果你的自定義View需要頻繁刷新,或者刷新時數(shù)據(jù)處理量比較大不跟,那么你就可以考慮使用 SurfaceView 來取代 View 了书妻。

SurfaceView 的使用

SurfaceView 的使用雖然比 View 復(fù)雜,但是 SurfaceView 在使用時躬拢,有一套使用的模板代碼躲履,大部分的 SurfaceView 繪圖操作都可以套用這樣的模板代碼來進(jìn)行編寫。因此 SurfaceView 的使用會更加簡單聊闯。

創(chuàng)建一個 SurfaceView 的模板
1工猜、 創(chuàng)建 SurfaceView

創(chuàng)建自定義的 MySurfaceView 繼承自 SurfaceView ,并實現(xiàn)兩個接口——SurfaceHolder.Callback, Runnable菱蔬,同時實現(xiàn)其接口方法篷帅,如下:

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
    }

    @Override
    public void run() {
    }
}
2、初始化 SurfaceView

在自定義 SurfaceView 的構(gòu)造方法中拴泌,需要對 SurfaceView 進(jìn)行初始化魏身。通常需要定義以下三個成員變量,如下:

private SurfaceHolder mHolder;
//用于繪圖的canvas
private Canvas mCanvas;
//子線程標(biāo)志位
private boolean mIsDrawing;

初始化方法就是初始化一個 SurfaceHolder 對象并注冊 SurfaceHolder 的回調(diào)方法蚪腐,如下:

mHolder = getHolder();
mHolder.addCallback(this);

另外兩個成員變量——Canvas 和標(biāo)志位箭昵。使用 Canvas 來進(jìn)行繪圖;使用標(biāo)志位來控制之前提到的用于繪制的子線程回季。

3家制、使用 SurfaceView

通過 SurfaceHolder 對象的 lockCanvas() 方法就可以獲得當(dāng)前的 Canvas 繪圖對象正林。接下來就可以與在 View 中進(jìn)行的繪制操作一樣進(jìn)行繪制了。這里需要注意颤殴,獲取到的 Canvas 對象還是繼續(xù)上次的 Canvas 對象觅廓,而不是一個新的 Canvas 對象。因此涵但,之前的繪圖操作都會被保留杈绸。如果需要擦除,則可以在繪制前矮瘟,通過 drawColor() 方法來進(jìn)行清屏操作蝇棉。

繪制時,充分利用 SurfaceView 的三個回調(diào)方法芥永,在 surfaceCreated() 方法里開啟子線程進(jìn)行繪制篡殷,而子線程使用一個 while (mIsDrawing) {} 的循環(huán)來不停地進(jìn)行繪制,而在繪制的具體邏輯中埋涧,通過 lockCanvas() 方法來獲取 Canvas 對象來繪制板辽,并通過 unlockCanvasAndPost(mCanvas) 方法對畫布內(nèi)容進(jìn)行提交。整個 SurfaceView 模板代碼如下:

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
 * Created by Deeson on 2017/5/23.
 */
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {

    private SurfaceHolder mHolder;
    //用于繪圖的canvas
    private Canvas mCanvas;
    //子線程標(biāo)志位
    private boolean mIsDrawing;

    public MySurfaceView (Context context) {
        super(context);
        init();
    }

    public MySurfaceView (Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MySurfaceView (Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mHolder = getHolder();
        mHolder.addCallback(this);
        setFocusable(true);
        setFocusableInTouchMode(true);
        this.setKeepScreenOn(true);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mIsDrawing = true;
        new Thread(this).start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mIsDrawing = false;
    }

    @Override
    public void run() {
        while (mIsDrawing) {
            draw();
        }
    }

    private void draw() {
        try {
            mCanvas = mHolder.lockCanvas();
            //draw something
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != mCanvas) {
                mHolder.unlockCanvasAndPost(mCanvas);
            }
        }
    }
}

以上代碼基本可以滿足大部分 SurfaceView 的繪圖需求棘催,唯一需要注意的是在繪制方法中劲弦,將 mHolder.unlockCanvasAndPost(mCanvas);方法放到 finally 代碼塊中,保證每次都能將內(nèi)容提交醇坝。

本文轉(zhuǎn)自 https://juejin.cn/post/6844903487310921741邑跪,如有侵權(quán),請聯(lián)系刪除呼猪。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末画畅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子宋距,更是在濱河造成了極大的恐慌轴踱,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谚赎,死亡現(xiàn)場離奇詭異淫僻,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)壶唤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進(jìn)店門雳灵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人闸盔,你說我怎么就攤上這事悯辙。” “怎么了?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵笑撞,是天一觀的道長。 經(jīng)常有香客問我钓觉,道長茴肥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任荡灾,我火速辦了婚禮瓤狐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘批幌。我一直安慰自己础锐,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布荧缘。 她就那樣靜靜地躺著皆警,像睡著了一般。 火紅的嫁衣襯著肌膚如雪截粗。 梳的紋絲不亂的頭發(fā)上信姓,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天,我揣著相機(jī)與錄音绸罗,去河邊找鬼意推。 笑死,一個胖子當(dāng)著我的面吹牛珊蟀,可吹牛的內(nèi)容都是我干的菊值。 我是一名探鬼主播,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼育灸,長吁一口氣:“原來是場噩夢啊……” “哼腻窒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起磅崭,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤定页,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后绽诚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體典徊,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年恩够,在試婚紗的時候發(fā)現(xiàn)自己被綠了卒落。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡蜂桶,死狀恐怖儡毕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤腰湾,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布雷恃,位于F島的核電站,受9級特大地震影響费坊,放射性物質(zhì)發(fā)生泄漏倒槐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一附井、第九天 我趴在偏房一處隱蔽的房頂上張望讨越。 院中可真熱鬧,春花似錦永毅、人聲如沸把跨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽着逐。三九已至,卻和暖如春意蛀,著一層夾襖步出監(jiān)牢的瞬間滨嘱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工浸间, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留太雨,地道東北人。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓魁蒜,卻偏偏與公主長得像囊扳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子兜看,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,728評論 2 351

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