Android基礎(14)SurfaceView

一. 概述

Android中提供了View進行繪圖處理,View可以滿足大部分的繪圖需求撮执,但是有時候鞭达,View卻顯得力不從心,所以Android提供了SurfaceView給Android開發(fā)者北秽,以滿足更多的繪圖需求。下面就讓我們一起來了解一下SurfaceView最筒。

我們知道View是通過刷新來重繪視圖贺氓,系統(tǒng)通過發(fā)出VSSYNC信號來進行屏幕的重繪,刷新的時間間隔是16ms,如果我們可以在16ms以內將繪制工作完成床蜘,則沒有任何問題辙培,如果我們繪制過程邏輯很復雜,并且我們的界面更新還非常頻繁悄泥,這時候就會造成界面的卡頓虏冻,影響用戶體驗,為此Android提供了SurfaceView來解決這一問題弹囚。

View和SurfaceView的區(qū)別:
1 . View適用于主動更新的情況厨相,而SurfaceView則適用于被動更新的情況,比如頻繁刷新界面鸥鹉。
2 . View在主線程中對頁面進行刷新蛮穿,而SurfaceView則開啟一個子線程來對頁面進行刷新。
3 . View在繪圖時沒有實現雙緩沖機制毁渗,SurfaceView在底層機制中就實現了雙緩沖機制践磅。

雙緩沖技術是游戲開發(fā)中的一個重要的技術。當一個動畫爭先顯示時灸异,程序又在改變它府适,前面還沒有顯示完,程序又請求重新繪制肺樟,這樣屏幕就會不停地閃爍檐春。而雙緩沖技術是把要處理的圖片在內存中處理好之后,再將其顯示在屏幕上么伯。
雙緩沖主要是為了解決 反復局部刷屏帶來的閃爍疟暖。把要畫的東西先畫到一個內存區(qū)域里,然后整體的一次性畫出來田柔。

二. 如何使用SurfaceView
1 . 創(chuàng)建SurfaceView

我們需要自定義一個類繼承自SurfaceView俐巴,并且實現兩個接口以及接口定義的方法,當然硬爆,與自定義View類似欣舵,還要重寫三個構造函數。下面是代碼:

public class SurfaceViewTemplate extends SurfaceView implements SurfaceHolder.Callback, Runnable {
    public SurfaceViewTemplate(Context context) {
        this(context, null);
    }

    public SurfaceViewTemplate(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
       //創(chuàng)建
    }

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

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        //銷毀
    }

    @Override
    public void run() {
        //子線程
    }
}

前面三個構造函數的寫法和自定義View是相同的摆屯,接下來的三個方法分別在SurfaceView創(chuàng)建邻遏、改變糠亩、銷毀的時候進行調用,最后的run()方法中寫我們子線程中執(zhí)行的繪圖邏輯即可准验。

2 . 初始化SurfaceView

這一步我們主要是定義三個成員變量以備后面繪圖時使用赎线,然后初始化這三個成員變量并且注冊對應的回調方法。代碼如下:

private SurfaceHolder mSurfaceHolder;
//繪圖的Canvas
private Canvas mCanvas;
//子線程標志位
private boolean mIsDrawing;

/**
 * 初始化View
 */
private void initView(){
    mSurfaceHolder = getHolder();
    //注冊回調方法
    mSurfaceHolder.addCallback(this);
    //設置一些參數方便后面繪圖
    setFocusable(true);
    setKeepScreenOn(true);
    setFocusableInTouchMode(true);
}

public SurfaceViewSinFun(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    //在三個參數的構造方法中完成初始化操作
    initView();
}
3 . 使用SurfaceView

(1) 通過lockCanvas()方法獲得Canvas對象
(2) 在子線程中使用Canvas對象進行繪制
(3) 使用unlockCanvasAndPost()方法將畫布內容進行提交

注意: lockCanvas() 方法獲得的Canvas對象仍然是上次繪制的對象糊饱,由于我們是不斷進行繪制垂寥,但是每次得到的Canvas對象都是第一次創(chuàng)建的Canvas對象。

SurfaceView的繪制可以使用下面的模板代碼來實現另锋,唯一的不同就是繪制邏輯的不同滞项,代碼如下:

public class SurfaceViewTemplate extends SurfaceView implements SurfaceHolder.Callback, Runnable {
    private SurfaceHolder mSurfaceHolder;
    //繪圖的Canvas
    private Canvas mCanvas;
    //子線程標志位
    private boolean mIsDrawing;
    public SurfaceViewTemplate(Context context) {
        this(context, null);
    }

    public SurfaceViewTemplate(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SurfaceViewTemplate(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    @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){
            drawSomething();
        }
    }
    //繪圖邏輯
    private void drawSomething() {
        try {
            //獲得canvas對象
            mCanvas = mSurfaceHolder.lockCanvas();
            //繪制背景
            mCanvas.drawColor(Color.WHITE);
            //繪圖
        }catch (Exception e){

        }finally {
            if (mCanvas != null){
                //釋放canvas對象并提交畫布
                mSurfaceHolder.unlockCanvasAndPost(mCanvas);
            }
        }
    }

    /**
     * 初始化View
     */
    private void initView(){
        mSurfaceHolder = getHolder();
        mSurfaceHolder.addCallback(this);
        setFocusable(true);
        setKeepScreenOn(true);
        setFocusableInTouchMode(true);
    }
}
三. 案例
繪制正弦曲線
public class SurfaceViewSinFun extends SurfaceView implements SurfaceHolder.Callback, Runnable {
    private SurfaceHolder mSurfaceHolder;
    //繪圖的Canvas
    private Canvas mCanvas;
    //子線程標志位
    private boolean mIsDrawing;
    private int x = 0, y = 0;
    private Paint mPaint;
    private Path mPath;
    public SurfaceViewSinFun(Context context) {
        this(context, null);
    }

    public SurfaceViewSinFun(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SurfaceViewSinFun(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mPaint = new Paint();
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(5);
        mPath = new Path();
        //路徑起始點(0, 100)
        mPath.moveTo(0, 100);
        initView();
    }

    @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){
            drawSomething();
            x += 1;
            y = (int)(100 * Math.sin(2 * x * Math.PI / 180) + 400);
            //加入新的坐標點
            mPath.lineTo(x, y);
        }
    }

    private void drawSomething() {
        try {
            //獲得canvas對象
            mCanvas = mSurfaceHolder.lockCanvas();
            //繪制背景
            mCanvas.drawColor(Color.WHITE);
            //繪制路徑
            mCanvas.drawPath(mPath, mPaint);
        } catch (Exception e){

        } finally {
            if ( mCanvas != null ) {
                //釋放canvas對象并提交畫布
                mSurfaceHolder.unlockCanvasAndPost(mCanvas);
            }
        }
    }

    /**
     * 初始化View
     */
    private void initView(){
        mSurfaceHolder = getHolder();
        mSurfaceHolder.addCallback(this);
        setFocusable(true);
        setKeepScreenOn(true);
        setFocusableInTouchMode(true);
    }
}
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市夭坪,隨后出現的幾起案子文判,更是在濱河造成了極大的恐慌,老刑警劉巖室梅,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件戏仓,死亡現場離奇詭異,居然都是意外死亡亡鼠,警方通過查閱死者的電腦和手機赏殃,發(fā)現死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來间涵,“玉大人仁热,你說我怎么就攤上這事」戳ǎ” “怎么了抗蠢?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長思劳。 經常有香客問我物蝙,道長,這世上最難降的妖魔是什么敢艰? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮册赛,結果婚禮上钠导,老公的妹妹穿的比我還像新娘。我一直安慰自己森瘪,他們只是感情好牡属,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著扼睬,像睡著了一般逮栅。 火紅的嫁衣襯著肌膚如雪悴势。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天措伐,我揣著相機與錄音特纤,去河邊找鬼。 笑死侥加,一個胖子當著我的面吹牛捧存,可吹牛的內容都是我干的。 我是一名探鬼主播担败,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼昔穴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了提前?” 一聲冷哼從身側響起吗货,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎狈网,沒想到半個月后宙搬,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡孙援,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年害淤,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拓售。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡窥摄,死狀恐怖,靈堂內的尸體忽然破棺而出础淤,到底是詐尸還是另有隱情崭放,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布鸽凶,位于F島的核電站币砂,受9級特大地震影響,放射性物質發(fā)生泄漏玻侥。R本人自食惡果不足惜决摧,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望凑兰。 院中可真熱鬧掌桩,春花似錦、人聲如沸姑食。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽音半。三九已至则拷,卻和暖如春贡蓖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背煌茬。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工斥铺, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人宣旱。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓仅父,卻偏偏與公主長得像,于是被迫代替她去往敵國和親浑吟。 傳聞我的和親對象是個殘疾皇子笙纤,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內容