什麼是SurfaceView?Surface的意思是表層,表面的意思,那麼SurfaceView就是指一個在表層的View對象。爲何 說是在表層呢,這是由於它有點特殊跟其餘View不同,其餘View是繪製在表層外,而它就是充當表層對象。假設你要在一個球上畫畫,那麼球的表層就當 作你的畫布對象,你畫的東西會擋住它的表層,咱們默認沒使用SurfaceView,那麼球的表層就是空白的,若是咱們使用了SurfaceView,我 們能夠理解爲咱們拿來的球自己表面就具備紋路,你是畫再紋路之上的,若是你畫的是半透明的,那麼你將能夠透過你畫的東西看到球面自己的紋路。SDK的文檔 說到:SurfaceView就是在Window上挖一個洞,它就是顯示在這個洞裏,其餘的View是顯示在Window上,因此View能夠顯式在 SurfaceView之上,你也能夠添加一些層在SurfaceView之上。php
SurfaceView還有其餘的特性,上面咱們講了它能夠控制幀數,那它是什麼控制的呢?這就須要瞭解它的使用機制。通常在不少遊戲設計中,咱們都是開闢一個後臺線程計算遊戲相關的數據,而後根據這些計算完的新數據再刷新視圖對象,因爲對View執行繪製操做只能在UI線程上, 因此當你在另一個線程計算完數據後,你須要調用View.invalidate方法通知系統刷新View對象,因此遊戲相關的數據也須要讓UI線程能訪 問到,這樣的設計架構比較複雜,要是能讓後臺計算的線程能直接訪問數據,而後更新View對象那改多好。咱們知道View的更新只能在UI線程中,因此使 用自定義View沒辦法這麼作,可是SurfaceView就能夠了。它一個很好用的地方就是容許其餘線程(不是UI線程)繪製圖形(使用Canvas),根據它這個特性,你就能夠控制它的幀數,你若是讓這個線程1秒執行50次繪製,那麼最後顯示的就是50幀。html
首先SurfaceView也是一個View,它也有本身的生命週期。由於它須要另一個線程來執行繪製操做,因此咱們能夠在它生命週期的初始化階 段開闢一個新線程,而後開始執行繪製,當生命週期的結束階段咱們插入結束繪製線程的操做。這些是由其內部一個SurfaceHolder對象完成的。 SurfaceHolder,顧名思義,它裏面保存了一個隊Surface對象的引用,而咱們執行繪製方法就是操做這個 Surface,SurfaceHolder由於保存了對Surface的引用,因此使用它來處理Surface的生命週期,說到底 SurfaceView的生命週期其實就是Surface的生命週期,由於SurfaceHolder保存對Surface的引用,因此使用 SurfaceHolder來處理生命週期的初始化。android
SurfaceView是視圖(View)的繼承類,這個視圖裏內嵌了一個專門用於繪製的Surface。你能夠控制這個Surface的格式和尺寸。Surfaceview控制這個Surface的繪製位置。
surface是縱深排序(Z-ordered)的,這代表它總在本身所在窗口的後面。surfaceview提供了一個可見區域,只有在這個可見區域內 的surface部份內容纔可見,可見區域外的部分不可見。surface的排版顯示受到視圖層級關係的影響,它的兄弟視圖結點會在頂端顯示。這意味者 surface的內容會被它的兄弟視圖遮擋,這一特性能夠用來放置遮蓋物(overlays)(例如,文本和按鈕等控件)。注意,若是surface上面 有透明控件,那麼它的每次變化都會引發框架從新計算它和頂層控件的透明效果,這會影響性能。
你能夠經過SurfaceHolder接口訪問這個surface,getHolder()方法能夠獲得這個接口。
surfaceview變得可見時,surface被建立;surfaceview隱藏前,surface被銷燬。這樣能節省資源。若是你要查看 surface被建立和銷燬的時機,能夠重載surfaceCreated(SurfaceHolder)和 surfaceDestroyed(SurfaceHolder)。
surfaceview的核心在於提供了兩個線程:UI線程和渲染線程。這裏應注意:
1> 全部SurfaceView和SurfaceHolder.Callback的方法都應該在UI線程裏調用,通常來講就是應用程序主線程。渲染線程所要訪問的各類變量應該做同步處理。
2> 因爲surface可能被銷燬,它只在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()之間有效,因此要確保渲染線程訪問的是合法有效的surface。編程
接下來呢,說說本身對它的理解
一、定義canvas
能夠直接從內存或者DMA等硬件接口取得圖像數據,是個很是重要的繪圖容器。架構
它的特性是:能夠在主線程以外的線程中向屏幕繪圖上。這樣能夠避免畫圖任務繁重的時候形成主線程阻塞,從而提升了程序的反應速度。在遊戲開發中多用到SurfaceView,遊戲中的背景、人物、動畫等等儘可能在畫布canvas中畫出。app
二、實現框架
首先繼承SurfaceView並實現SurfaceHolder.Callback接口
使用接口的緣由:由於使用SurfaceView 有一個原則,全部的繪圖工做必須得在Surface 被建立以後才能開始(Surface—表面,這個概念在 圖形編程中經常被提到。基本上咱們能夠把它看成顯存的一個映射,寫入到Surface 的內容
能夠被直接複製到顯存從而顯示出來,這使得顯示速度會很是快),而在Surface 被銷燬以前必須結束。因此Callback 中的surfaceCreated 和surfaceDestroyed 就成了繪圖處理代碼的邊界。ide
須要重寫的方法函數
(1)public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){}
//在surface的大小發生改變時激發
(2)public void surfaceCreated(SurfaceHolder holder){}
//在建立時激發,通常在這裏調用畫圖的線程。
(3)public void surfaceDestroyed(SurfaceHolder holder) {}
//銷燬時激發,通常在這裏將畫圖的線程中止、釋放。
整個過程:繼承SurfaceView並實現SurfaceHolder.Callback接口 ----> SurfaceView.getHolder()得到SurfaceHolder對象 ---->SurfaceHolder.addCallback(callback)添加回調函數---->SurfaceHolder.lockCanvas()得到Canvas對象並鎖定畫布----> Canvas繪畫 ---->SurfaceHolder.unlockCanvasAndPost(Canvas canvas)結束鎖定畫圖,並提交改變,將圖形顯示。
三、SurfaceHolder
這裏用到了一個類SurfaceHolder,能夠把它當成surface的控制器,用來操縱surface。處理它的Canvas上畫的效果和動畫,控制表面,大小,像素等。
幾個須要注意的方法:
(1)、abstract void addCallback(SurfaceHolder.Callback callback);
// 給SurfaceView當前的持有者一個回調對象。
(2)、abstract Canvas lockCanvas();
// 鎖定畫布,通常在鎖定後就能夠經過其返回的畫布對象Canvas,在其上面畫圖等操做了。
(3)、abstract Canvas lockCanvas(Rect dirty);
// 鎖定畫布的某個區域進行畫圖等..由於畫完圖後,會調用下面的unlockCanvasAndPost來改變顯示內容。
// 相對部份內存要求比較高的遊戲來講,能夠不用重畫dirty外的其它區域的像素,能夠提升速度。
(4)、abstract void unlockCanvasAndPost(Canvas canvas);
// 結束鎖定畫圖,並提交改變。
四、實例
這裏的例子實現了一個矩形和一個計時器
package xl.test; import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.os.Bundle; import android.view.SurfaceHolder; import android.view.SurfaceView; public class ViewTest extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new MyView(this)); } //視圖內部類 class MyView extends SurfaceView implements SurfaceHolder.Callback { private SurfaceHolder holder; private MyThread myThread; public MyView(Context context) { super(context); // TODO Auto-generated constructor stub holder = this.getHolder(); holder.addCallback(this); myThread = new MyThread(holder);//建立一個繪圖線程 } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // TODO Auto-generated method stub } @Override public void surfaceCreated(SurfaceHolder holder) { // TODO Auto-generated method stub myThread.isRun = true; myThread.start(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub myThread.isRun = false; } } //線程內部類 class MyThread extends Thread { private SurfaceHolder holder; public boolean isRun ; public MyThread(SurfaceHolder holder) { this.holder =holder; isRun = true; } @Override public void run() { int count = 0; while(isRun) { Canvas c = null; try { synchronized (holder) { c = holder.lockCanvas();//鎖定畫布,通常在鎖定後就能夠經過其返回的畫布對象Canvas,在其上面畫圖等操做了。 c.drawColor(Color.BLACK);//設置畫布背景顏色 Paint p = new Paint(); //建立畫筆 p.setColor(Color.WHITE); Rect r = new Rect(100, 50, 300, 250); c.drawRect(r, p); c.drawText("這是第"+(count++)+"秒", 100, 310, p); Thread.sleep(1000);//睡眠時間爲1秒 } } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } finally { if(c!= null) { holder.unlockCanvasAndPost(c);//結束鎖定畫圖,並提交改變。 } } } } } }
結來講,就是SurfaceView有如下三個特色:
A. 具備獨立的繪圖表面;
B. 須要在宿主窗口上挖一個洞來顯示本身;
C. 它的UI繪製能夠在獨立的線程中進行,這樣就能夠進行復雜的UI繪製,而且不會影響應用程序的主線程響應用戶輸入。
什麼是雙緩衝?
不用畫布,直接在窗口上進行繪圖叫作無緩衝繪圖。用了一個畫布,將全部內容都先畫到畫布上,在總體繪製到窗口上,就該叫作單緩衝繪圖,那個畫布就是一個緩衝區。用了兩個畫布,一個進行臨時的繪圖,一個進行最終的繪圖,這樣就叫作雙緩衝繪圖。
surfaceView自身實現了雙緩衝,而View沒有。其實view你也能夠本身實現,可是實現的結構不如surfaceView好。
surfaceView經過 surfaceHolder.lockCanvas 鎖定畫布,實現下一張圖片的繪製,再經過另外的線程刷新界面,繪製圖片。
view則是直接在ondraw裏繪製圖片,刷新界面。其實view也能夠實現雙緩衝機制,你能夠在另個出ondraw的方法中繪製下一張bitmap(參見:http://blog.csdn.net/liubingzhao/article/details/5563113),也能夠另開一個線程,處理除了繪製圖片之外的操做(參見:http://topic.csdn.net/u/20110901/23/e283f805-20dc-40c3-8381-403dd1ca69b0.html),就實現了view的雙緩衝。
爲何動態繪圖surfaceView要比View好?
由於View是在UI主線程中進行繪製的,繪製時會阻塞主線程,若是ontouch事件又處理的比較多的話會致使界面卡。而surfaceView是另開了一個線程繪製的,再加上雙緩衝機制,因此要高效。不會卡。其實如今通常實現view的時候通常都會在其餘出先生成bitmap在給ondraw去畫,因此雙緩衝的做用不是那麼明顯了。