如果做游戲或者視頻相關開發的時候,將會用到SurfaceView。對於SurfaceView,首先需要了解一下它自己的位置:
extends View
java.lang.Object
↳
Android.view.View
↳
android.view.SurfaceView
由手冊可知:
SurfaceView是View類的繼承類,這個View裡內嵌了一個專門用於繪制的Surface,這個可以類似的理解成為一個在View裡的Canvas。你可以控制這個Surface的格式和尺寸。Surfaceview類則控制這個Surface在屏幕上的正確位置。
《Android高級編程》裡這樣說:
在一般情況下,應用程序的View都是在相同的GUI線程中繪制的。這個主應用程序線程同時也用來處理所有的用戶交互(例如按鈕單擊或者文本輸入)。
對於一個View的onDraw()方法,不能夠滿足將其移動到後台線程中去。因為從後台線程修改一個GUI元素會被顯式地禁止的。
當需要快速地更新View的UI,或者當前渲染代碼阻塞GUI線程的時間過長的時候,SurfaceView就是解決上述問題的最佳選擇。SurfaceView封裝了一個Surface對象,而不是Canvas。這一點很重要,因為Surface可以使用後台線程繪制。對於那些資源敏感的操作,或者那些要求快速更新或者高速幀率的地方,例如使用3D圖形,創建游戲,或者實時預覽攝像頭,這一點特別有用。
1. 何時應該使用SurfaceView
SurfaceView使用的方式與任何View所派生的類都是完全相同的。可以像其他View那樣應用動畫,並把它們放到布局中。
SurfaceView封裝的Surface支持所有標准的Canvas方法進行繪圖,同時也支持完全的OpenGL ES 庫。
使用OpenGL,你可以在Surface上繪制任何支持2D或者3D對象,與在2D畫布上模擬相同的效果相比,這種方法可以依靠硬件加速(可用的時候)來極大地提高性能。
對於顯示動態的3D圖像來說,例如,那些使用Google Earth 功能的應用程序,或者那些提供沉浸體驗的交互式游戲,Surface特別有用。它還是實時顯示攝像頭預覽的最佳選擇。
2. 創建一個新的SurfaceView控件
創建一個新的SurfaceView控件需要創建一個新的擴展了SurfaceView的類,並實現SurfaceHolder.Callback。
SurfaceHolder回調可以在底層的Surface被創建和銷毀的時候通知View,並傳遞給它SurfaceHolder對象的引用,其中包含了當前有效的Surface。
一個典型的SurfaceView 設計模型包括一個由Thread所派生的類,它可以接收對當前的SurfaceHolder的引用,並獨立地更新它。
3. 使用SurfaceView創建3D控件
Android完全支持OpenGL ES 3D 渲染框架,其中包含了對設備的硬件加速的支持。SurfaceView控件提供了一個表面,可以在它上面渲染你的OpenGL場景。
那麼我們在使用的時候可以這樣使用:
被動更新畫面的。比如棋類,這種用view就好了。因為畫面的更新是依賴於 onTouch 來更新,可以直接使用 invalidate。 因為這種情況下,這一次Touch和下一次的Touch需要的時間比較長些,不會產生影響。
主動更新。比如一個人在一直跑動。這就需要一個單獨的thread不停的重繪人的狀態,避免阻塞main UI thread。所以顯然view不合適,需要surfaceView來控制。
可以直接從內存或硬件設備比如相機等取得圖像數據,是個非常重要的繪圖容器。
它的特性是:可以在主線程之外的線程中向屏幕繪圖。這樣可以避免畫圖任務繁重的時候造成主線程阻塞,從而提高了程序的反應速度。
如何去使用一個SurfaceView:
首先繼承SurfaceView並實現SurfaceHolder.Callback接口。因為使用SurfaceView 有一個原則,所有的繪圖工作必須得在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)結束鎖定畫圖,並提交改變,將圖形顯示。
關於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);
// 結束鎖定畫圖,並提交改變。
測試代碼如下: