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

前言

摘自《Android群英傳》

Android提供了View進行繪圖處理锹淌,View可以滿足大部分的繪圖需求,但在某些時候也會心有余而力不足窗慎。我們知道,View通過刷新來重繪視圖起宽,Android 系統(tǒng)通過發(fā)出VSYNC信號來進行屏幕的重繪,刷新的時間間隔為16ms济榨。如果在16ms內View完成了你所需要執(zhí)行的所有操作燎含,那么用戶在視覺上就不會產生卡頓的感覺;而如果執(zhí)行的操作邏輯太多腿短,特別是需要頻繁刷新的界面上屏箍,例如游戲界面,就會不斷阻塞主線程橘忱,從而導致畫面卡頓赴魁。很多情況下,在自定義View的log中會看到如下的警告:
“Skipped 47 frames! The application may be doing too much work on its main thread.”
為了避免這一問題的產生钝诚,Android系統(tǒng)提供了SurfaceView組件颖御。

View 和 SurfaceView 的區(qū)別

  • View 主要適用于主動更新的情況下,而 SurfaceView 主要適用于被動更新凝颇,例如頻繁地刷新潘拱。
  • View 在主線程中對畫面進行刷新,而 SurfaceView 通常會通過一個子線程來進行頁面的刷新拧略。
  • View 在繪圖時沒有使用雙緩沖機制芦岂,而 SurfaceView 在底層實現(xiàn)機制中就已經(jīng)實現(xiàn)了雙緩沖機制。
    總結就是垫蛆,如果你的自定義View需要頻繁刷新禽最,或者刷新時數(shù)據(jù)處理量比較大,那么你就可以考慮使用 SurfaceView 來取代 View 了袱饭。

SurfaceView 的使用

SurfaceView 的使用雖然比 View 復雜川无,但是 SurfaceView 在使用時,有一套使用的模板代碼虑乖,大部分的 SurfaceView 繪圖操作都可以套用這樣的模板代碼來進行編寫懦趋。因此 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 的構造方法中,需要對 SurfaceView 進行初始化继找。通常需要定義以下三個成員變量,如下:

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

初始化方法就是初始化一個 SurfaceHolder 對象并注冊 SurfaceHolder 的回調方法逃沿,如下:

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

另外兩個成員變量——Canvas 和標志位婴渡。使用 Canvas 來進行繪圖幻锁;使用標志位來控制之前提到的用于繪制的子線程。

3边臼、使用 SurfaceView

通過 SurfaceHolder 對象的 lockCanvas() 方法就可以獲得當前的 Canvas 繪圖對象哄尔。接下來就可以與在 View 中進行的繪制操作一樣進行繪制了。這里需要注意柠并,獲取到的 Canvas 對象還是繼續(xù)上次的 Canvas 對象岭接,而不是一個新的 Canvas 對象。因此臼予,之前的繪圖操作都會被保留鸣戴。如果需要擦除,則可以在繪制前粘拾,通過 drawColor() 方法來進行清屏操作窄锅。

繪制時,充分利用 SurfaceView 的三個回調方法缰雇,在 surfaceCreated() 方法里開啟子線程進行繪制入偷,而子線程使用一個 while (mIsDrawing) {} 的循環(huán)來不停地進行繪制,而在繪制的具體邏輯中械哟,通過 lockCanvas() 方法來獲取 Canvas 對象來繪制疏之,并通過 unlockCanvasAndPost(mCanvas) 方法對畫布內容進行提交。整個 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;
    //子線程標志位
    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 代碼塊中,保證每次都能將內容提交糯崎。

最后

關于 SurfaceView 的實例演練几缭,可以看看我的這篇文章 Android:貝塞爾曲線原理分析

按照慣例,需要送上 demo 下載沃呢,如下 gif 所示:

mySurfaceView.gif
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末年栓,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子薄霜,更是在濱河造成了極大的恐慌某抓,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惰瓜,死亡現(xiàn)場離奇詭異否副,居然都是意外死亡,警方通過查閱死者的電腦和手機崎坊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門备禀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事曲尸「承” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵另患,是天一觀的道長纽乱。 經(jīng)常有香客問我,道長昆箕,這世上最難降的妖魔是什么鸦列? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮鹏倘,結果婚禮上薯嗤,老公的妹妹穿的比我還像新娘。我一直安慰自己第股,他們只是感情好应民,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著夕吻,像睡著了一般诲锹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涉馅,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天归园,我揣著相機與錄音,去河邊找鬼稚矿。 笑死庸诱,一個胖子當著我的面吹牛,可吹牛的內容都是我干的晤揣。 我是一名探鬼主播桥爽,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼昧识!你這毒婦竟也來了钠四?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤跪楞,失蹤者是張志新(化名)和其女友劉穎缀去,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體甸祭,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡缕碎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了池户。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片咏雌。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡凡怎,死狀恐怖,靈堂內的尸體忽然破棺而出处嫌,到底是詐尸還是另有隱情栅贴,我是刑警寧澤斟湃,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布熏迹,位于F島的核電站,受9級特大地震影響凝赛,放射性物質發(fā)生泄漏注暗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一墓猎、第九天 我趴在偏房一處隱蔽的房頂上張望捆昏。 院中可真熱鬧,春花似錦毙沾、人聲如沸骗卜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽寇仓。三九已至,卻和暖如春烤宙,著一層夾襖步出監(jiān)牢的瞬間遍烦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工躺枕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留服猪,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓拐云,卻偏偏與公主長得像罢猪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子叉瘩,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內容