什么是SurfaceView?Surface的意思是表層,表面的意思,那么SurfaceView就是指一個在表層的View對象。為什么 說是在表層呢,這是因為它有點特殊跟其他View不一樣,其他View是繪制在表層外,而它就是充當表層對象。假設你要在一個球上畫畫,那么球的表層就當 做你的畫布對象,你畫的東西會擋住它的表層,我們默認沒使用SurfaceView,那么球的表層就是空白的,如果我們使用了SurfaceView,我 們可以理解為我們拿來的球本身表面就具有紋路,你是畫再紋路之上的,如果你畫的是半透明的,那么你將可以透過你畫的東西看到球面本身的紋路。SDK的文檔 說到:SurfaceView就是在Window上挖一個洞,它就是顯示在這個洞里,其他的View是顯示在Window上,所以View可以顯式在 SurfaceView之上,你也可以添加一些層在SurfaceView之上。
SurfaceView還有其他的特性,上面我們講了它可以控制幀數,那它是什么控制的呢?這就需要了解它的使用機制。一般在很多游戲設計中,我們都是開辟一個后台線程計算游戲相關的數據,然后根據這些計算完的新數據再刷新視圖對象,由於對View執行繪制操作只能在UI線程上, 所以當你在另外一個線程計算完數據后,你需要調用View.invalidate方法通知系統刷新View對象,所以游戲相關的數據也需要讓UI線程能訪 問到,這樣的設計架構比較復雜,要是能讓后台計算的線程能直接訪問數據,然后更新View對象那改多好。我們知道View的更新只能在UI線程中,所以使 用自定義View沒辦法這么做,但是SurfaceView就可以了。它一個很好用的地方就是允許其他線程(不是UI線程)繪制圖形(使用Canvas),根據它這個特性,你就可以控制它的幀數,你如果讓這個線程1秒執行50次繪制,那么最后顯示的就是50幀。
如何使用SurfaceView?
首先SurfaceView也是一個View,它也有自己的生命周期。因為它需要另外一個線程來執行繪制操作,所以我們可以在它生命周期的初始化階 段開辟一個新線程,然后開始執行繪制,當生命周期的結束階段我們插入結束繪制線程的操作。這些是由其內部一個SurfaceHolder對象完成的。 SurfaceHolder,顧名思義,它里面保存了一個隊Surface對象的引用,而我們執行繪制方法就是操作這個 Surface,SurfaceHolder因為保存了對Surface的引用,所以使用它來處理Surface的生命周期,說到底 SurfaceView的生命周期其實就是Surface的生命周期,因為SurfaceHolder保存對Surface的引用,所以使用 SurfaceHolder來處理生命周期的初始化。
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。
接下來呢,說說自己對它的理解
1、定義
可以直接從內存或者DMA等硬件接口取得圖像數據,是個非常重要的繪圖容器。
它的特性是:可以在主線程之外的線程中向屏幕繪圖上。這樣可以避免畫圖任務繁重的時候造成主線程阻塞,從而提高了程序的反應速度。在游戲開發中多用到SurfaceView,游戲中的背景、人物、動畫等等盡量在畫布canvas中畫出。
2、實現
首先繼承SurfaceView並實現SurfaceHolder.Callback接口
使用接口的原因:因為使用SurfaceView 有一個原則,所有的繪圖工作必須得在Surface 被創建之后才能開始(Surface—表面,這個概念在 圖形編程中常常被提到。基本上我們可以把它當作顯存的一個映射,寫入到Surface 的內容
可以被直接復制到顯存從而顯示出來,這使得顯示速度會非常快),而在Surface 被銷毀之前必須結束。所以Callback 中的surfaceCreated 和surfaceDestroyed 就成了繪圖處理代碼的邊界。
需要重寫的方法
(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)結束鎖定畫圖,並提交改變,將圖形顯示。
3、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);
// 結束鎖定畫圖,並提交改變。
4、實例
這里的例子實現了一個矩形和一個計時器
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去畫,所以雙緩沖的作用不是那么明顯了。