Android View之SurfaceView優(yōu)點(diǎn)

View更新UI問題

對于一個(gè)View的onDraw方法蔗草,不能這樣做咒彤,因?yàn)閺暮笈_(tái)線程修改一個(gè)GUI元素會(huì)被顯式地禁止的。
當(dāng)需要快速地更新View的UI咒精,或者當(dāng)渲染代碼阻塞GUI線程的時(shí)間過長的時(shí)候镶柱,SurfaceView就是解決上述問題的最佳選擇。

SurfaceView和View的區(qū)別

  • 本質(zhì)區(qū)別
    surfaceView是在一個(gè)新起的單獨(dú)線程中可以重新繪制畫面模叙,而View必須在UI的主線程中更新畫面歇拆。

  • 為什么普通View只能在主線程中更新UI?

    • UI線程是最重要的線程,負(fù)責(zé)繪制界面和分發(fā)窗口事件故觅,它既不能被阻塞厂庇,也不是線程安全的。
    • 通常界面繪制和分發(fā)事件本該是多線程訪問的输吏,但是多線程處理時(shí)為了保證訪問資源的正確性权旷,對于某些操作都會(huì)加上同步鎖,這樣會(huì)顯然會(huì)降低效率评也,而且還會(huì)涉及到線程的等待與線程上下文切換炼杖。
    • 為了提高效率,UI線程不在使用這些繁瑣的多線程機(jī)制盗迟,為了保證對UI操作的正確性坤邪,只允許在UI線程中操作UI。在非UI線程中可通過post或者runOnUiThread來刷新view罚缕。
    • UI主線程是多線程訪問不安全的艇纺,因此更新畫面時(shí)可能會(huì)引發(fā)問題,比如你更新畫面的時(shí)間過長邮弹,那么你的主UI線程會(huì)被你正在畫的函數(shù)阻塞黔衡。那么將無法響應(yīng)按鍵,觸屏等消息腌乡。
  • SurfaceView子線程更新UI帶來問題
    事件同步問題盟劫。比如你觸屏了一下,你需要surfaceView中 thread處理与纽,一般就需要有一個(gè)event queue的設(shè)計(jì)來保存touch event侣签,這會(huì)稍稍復(fù)雜一點(diǎn),因?yàn)樯婕暗骄€程同步急迂。

  • 兩種實(shí)例情況

    1. 畫面依賴于onTouch來更新的影所,不需要一直更新,且更新效果簡單僚碎,耗費(fèi)資源少猴娩,這一次touch和下一次touch時(shí)間間隔比較長,可以直接用invalidate更新即可勺阐。

    2. 畫面一直在更新卷中,比如一個(gè)人在跑步,為了避免阻塞main UI thread這樣就需要單獨(dú)的thread不停地重繪ui了皆看,這就要用到SurfaceView仓坞。

SurfaceView原理

  • 什么是Surface?
    簡單地說Surface對應(yīng)了一塊屏幕緩沖區(qū)腰吟,每個(gè)window對應(yīng)一個(gè)Surface无埃,任何View都是畫在Surface上的徙瓶,傳統(tǒng)的view共享一塊屏幕緩沖區(qū),所有的繪制必須在UI線程中進(jìn)行

  • Surface和SurfaceView的關(guān)系
    SurfaceView封裝了一個(gè)Surface對象嫉称,而不是Canvas侦镇,因?yàn)镾urface可以使用后臺(tái)線程繪制。
    對于那些資源敏感的操作或者那些要求快速更新或者高速幀率的地方织阅,例如壳繁,使用3D圖形,創(chuàng)建游戲荔棉,或者實(shí)時(shí)預(yù)覽攝像頭闹炉,這一點(diǎn)特別有用。

  • 什么是SurfaceHolder.CallBack?
    SurfaceHolder.Callback主要是當(dāng)?shù)讓拥腟urface被創(chuàng)建润樱、銷毀或者改變時(shí)提供回調(diào)通知渣触。
    由于繪制必須在surface被創(chuàng)建后才能進(jìn)行,因此SurfaceHolder.Callback中的surfaceCreated 和surfaceDestroyed 就成了繪圖處理代碼的邊界壹若。
    SurfaceHolder嗅钻,可以把它當(dāng)成Surface的容器和控制器,用來操縱Surface店展,處理它的Canvas上畫的效果和動(dòng)畫养篓,控制表面,大小赂蕴,像素等柳弄。

  • 使用代價(jià)
    獨(dú)立于GUI線程進(jìn)行繪圖的代價(jià)是額外的內(nèi)存消耗,所以概说,雖然它是創(chuàng)建定制的View的有效方式–有時(shí)甚至是必須的语御,但是使用Surface View的時(shí)候仍然要保持謹(jǐn)慎。

  • 生命周期
    SurfaceView變得可見時(shí)席怪,Surface被創(chuàng)建;SurfaceView隱藏前纤控,Surface被銷毀挂捻。這樣能節(jié)省資源。

  • 應(yīng)該注意
    所有SurfaceView和SurfaceHolder.Callback的方法都應(yīng)該在UI線程里調(diào)用船万,一般來說就是應(yīng)用程序主線程刻撒。渲染線程所要訪問的各種變量應(yīng)該作同步處理。

創(chuàng)建SurfaceView

  • 要?jiǎng)?chuàng)建一個(gè)新的SurfaceView耿导,需要?jiǎng)?chuàng)建一個(gè)新的擴(kuò)展了SurfaceView的類声怔,并實(shí)現(xiàn)SurfaceHolder.Callback。因?yàn)槔L制工作在子線程中進(jìn)行舱呻,模板代碼一般會(huì)直接實(shí)現(xiàn)Runnable接口醋火。
    如果是比較復(fù)雜的情況悠汽,那可能就需要使用線程池,任務(wù)隊(duì)列進(jìn)行處理了芥驳。因?yàn)閮纱问录|發(fā)間隔時(shí)間可能比一次事件處理時(shí)間還短柿冲,為保證響應(yīng)正確性,一般用任務(wù)隊(duì)列處理兆旬。

  • SurfaceHolder回調(diào)可以在底層的Surface被創(chuàng)建和銷毀的時(shí)候通知View假抄,并傳遞給它對SurfaceHolder對象的引用,其中包含了當(dāng)前有效的Surface丽猬。

  • 一個(gè)典型的SurfaceView設(shè)計(jì)模型包括一個(gè)由Thread所派生的類宿饱,它可以接收對當(dāng)前的SurfaceHolder的引用,并獨(dú)立地更新它脚祟。

使用場景

  • SurfaceView使用的方式與任何View所派生的類都是完全相同的谬以。可以像其他View那樣應(yīng)用動(dòng)畫愚铡,并把它們放到布局中蛉签。

  • SurfaceView封裝的Surface支持使用本章前面所描述的所有標(biāo)準(zhǔn)Canvas方法進(jìn)行繪圖,同時(shí)也支持完全的OpenGL ES庫沥寥。

  • 使用OpenGL碍舍,你可以再Surface上繪制任何支持的2D或者3D對象,與在2D畫布上模擬相同的效果相比邑雅,這種方法可以依靠硬件加速(可用的時(shí)候)來極大地提高性能片橡。

  • 對于顯示動(dòng)態(tài)的3D圖像來說,例如淮野,那些使用Google Earth功能的應(yīng)用程序捧书,或者那些提供沉浸體驗(yàn)的交互式游戲,SurfaceView特別有用骤星。它還是實(shí)時(shí)顯示攝像頭預(yù)覽的最佳選擇经瓷。

簡單畫圖板代碼

package com.hcsys.display.views;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.EventLog;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
 * des:
 * author: admin
 * time: 2016/10/24 11:32.
 * e-mail:guohaichang@163.com
 */
public class CustomSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
    // SurfaceHolder
    private SurfaceHolder mHolder;
    // 用于繪圖的Canvas
    private Canvas mCanvas;
    // 子線程標(biāo)志位
    private volatile boolean mIsDrawing;
    //畫筆
    private Paint mPaint;
    //繪制路徑
    private Path mPath;

    public CustomSurfaceView(Context context) {
        super(context);
        initView();
    }

    public CustomSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

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

    private void initView() {
        mHolder = getHolder();
        mHolder.addCallback(this);
        setFocusable(true);
        setFocusableInTouchMode(true);
        this.setKeepScreenOn(true);
        //mHolder.setFormat(PixelFormat.OPAQUE);
        mPaint = new Paint();
        mPaint.setColor(Color.BLUE);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(10);
        mPaint.setAntiAlias(true);//抗鋸齒
        mPath = new Path();
    }

    @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() {
        long start = System.currentTimeMillis();
        while (mIsDrawing) {
            draw();
        }
        long end = System.currentTimeMillis();
        // 50 - 100
        if (end - start < 100) {//保證線程運(yùn)行時(shí)間不少于100ms
            try {
                Thread.sleep(100 - (end - start));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private synchronized void draw() {
        try {
            mCanvas = mHolder.lockCanvas();
            mCanvas.drawColor(Color.WHITE);
            mCanvas.drawPath(mPath,mPaint);
        } catch (Exception e) {
        } finally {
            if (mCanvas != null){
                mHolder.unlockCanvasAndPost(mCanvas);
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                mPath.moveTo(event.getX(),event.getY());
                break;
            case MotionEvent.ACTION_MOVE:
                mPath.lineTo(event.getX(),event.getY());
                break;
            case MotionEvent.ACTION_UP:

                break;
        }
        return true;
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市洞难,隨后出現(xiàn)的幾起案子舆吮,更是在濱河造成了極大的恐慌,老刑警劉巖队贱,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件色冀,死亡現(xiàn)場離奇詭異,居然都是意外死亡柱嫌,警方通過查閱死者的電腦和手機(jī)锋恬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來编丘,“玉大人与学,你說我怎么就攤上這事彤悔。” “怎么了癣防?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵蜗巧,是天一觀的道長。 經(jīng)常有香客問我蕾盯,道長幕屹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任级遭,我火速辦了婚禮望拖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘挫鸽。我一直安慰自己说敏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布丢郊。 她就那樣靜靜地躺著盔沫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪枫匾。 梳的紋絲不亂的頭發(fā)上架诞,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天,我揣著相機(jī)與錄音干茉,去河邊找鬼谴忧。 笑死,一個(gè)胖子當(dāng)著我的面吹牛角虫,可吹牛的內(nèi)容都是我干的沾谓。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼戳鹅,長吁一口氣:“原來是場噩夢啊……” “哼均驶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起枫虏,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤辣恋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后模软,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡饮潦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年燃异,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片继蜡。...
    茶點(diǎn)故事閱讀 39,991評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡回俐,死狀恐怖逛腿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情仅颇,我是刑警寧澤单默,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站忘瓦,受9級(jí)特大地震影響搁廓,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜耕皮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一境蜕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧凌停,春花似錦粱年、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至赐俗,卻和暖如春拉队,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背秃励。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工氏仗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人夺鲜。 一個(gè)月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓皆尔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親币励。 傳聞我的和親對象是個(gè)殘疾皇子慷蠕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評論 2 355

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