SurfaceView,SurfaceHolder,SurfaceHolder.CallBack

SurfaceView 教程

SurfaceView介紹
      通常情況程序的View和用戶響應都是在同一個線程中處理的,這也是為什麼處理長時間事件(例如訪問網絡)需要放到另外的線程中去(防止阻塞當前UI線程的操作和繪制)。但是在其他線程中卻不能修改UI元素,例如用後臺線程更新自定義View(調用View的在自定義View中的onDraw函數)是不允許的。
 
       如果需要在另外的線程繪制界面、需要迅速的更新界面或則渲染UI界面需要較長的時間,這種情況就要使用SurfaceView瞭。SurfaceView中包含一個Surface對象,而Surface是可以在後臺線程中繪制的。Surface屬於OPhone底層顯示系統,關於這方面的介紹請參考附錄中的資料[1]。SurfaceView的性質決定瞭其比較適合一些場景:需要界面迅速更新、對幀率要求較高的情況。使用SurfaceView需要註意以下幾點情況:
SurfaceView和SurfaceHolder.Callback函數都從當前SurfaceView窗口線程中調用(一般而言就是程序的主線程)。有關資源狀態要註意和繪制線程之間的同步。
 在繪制線程中必須先合法的獲取Surface才能開始繪制內容,在SurfaceHolder.Callback.surfaceCreated() 和SurfaceHolder.Callback.surfaceDestroyed()之間的狀態為合法的,另外在Surface類型為SURFACE_TYPE_PUSH_BUFFERS時候是不合法的。
 額外的繪制線程會消耗系統的資源,在使用SurfaceView的時候要註意這點。
 
使用SurfaceView
        隻要繼承SurfaceView類並實現SurfaceHolder.Callback接口就可以實現一個自定義的SurfaceView瞭,SurfaceHolder.Callback在底層的Surface狀態發生變化的時候通知View,SurfaceHolder.Callback具有如下的接口:
 surfaceCreated(SurfaceHolder holder):當Surface第一次創建後會立即調用該函數。程序可以在該函數中做些和繪制界面相關的初始化工作,一般情況下都是在另外的線程來繪制界面,所以不要在這個函數中繪制Surface。
 surfaceChanged(SurfaceHolder holder, int format, int width,int height):當Surface的狀態(大小和格式)發生變化的時候會調用該函數,在surfaceCreated調用後該函數至少會被調用一次。
 surfaceDestroyed(SurfaceHolder holder):當Surface被摧毀前會調用該函數,該函數被調用後就不能繼續使用Surface瞭,一般在該函數中來清理使用的資源。
 
       通過SurfaceView的getHolder()函數可以獲取SurfaceHolder對象,Surface 就在SurfaceHolder對象內。雖然Surface保存瞭當前窗口的像素數據,但是在使用過程中是不直接和Surface打交道的,由SurfaceHolder的Canvas lockCanvas()或則Canvas lockCanvas(Rect dirty)函數來獲取Canvas對象,通過在Canvas上繪制內容來修改Surface中的數據。如果Surface不可編輯或則尚未創建調用該函數會返回null,在 unlockCanvas() 和 lockCanvas()中Surface的內容是不緩存的,所以需要完全重繪Surface的內容,為瞭提高效率隻重繪變化的部分則可以調用lockCanvas(Rect dirty)函數來指定一個dirty區域,這樣該區域外的內容會緩存起來。在調用lockCanvas函數獲取Canvas後,SurfaceView會獲取Surface的一個同步鎖直到調用unlockCanvasAndPost(Canvas canvas)函數才釋放該鎖,這裡的同步機制保證在Surface繪制過程中不會被改變(被摧毀、修改)。
當在Canvas中繪制完成後,調用函數unlockCanvasAndPost(Canvas canvas)來通知系統Surface已經繪制完成,這樣系統會把繪制完的內容顯示出來。為瞭充分利用不同平臺的資源,發揮平臺的最優效果可以通過SurfaceHolder的setType函數來設置繪制的類型,目前接收如下的參數:
SURFACE_TYPE_NORMAL:用RAM緩存原生數據的普通Surface
SURFACE_TYPE_HARDWARE:適用於DMA(Direct memory access )引擎和硬件加速的Surface
SURFACE_TYPE_GPU:適用於GPU加速的Surface
SURFACE_TYPE_PUSH_BUFFERS:表明該Surface不包含原生數據,Surface用到的數據由其他對象提供,在Camera圖像預覽中就使用該類型的Surface,有Camera負責提供給預覽Surface數據,這樣圖像預覽會比較流暢。如果設置這種類型則就不能調用lockCanvas來獲取Canvas對象瞭。
 
         目前OPhone還不支持GIF動畫圖片的顯示,這裡就通過一個SurfaceView來展示如何定制一個支持GIF動畫的View,同時從該示例(註釋)中也可以看出如何使用SurfaceView。
         首先創建一個GifView繼承在SurfaceView,代碼如下:

 

 

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

android大掃盲之SurfaceView,SurfaceHolder,SurfaceHolder.CallBack.
你hold住瞭嗎

最近接觸到瞭SurfaceView,SurfaceHolder,SurfaceHolder.CallBack,一直不求其解,現在來粗淺認識一下它們。

先看一下官方的定義:

1.SurfaceView

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。

2.SurfaceHolder

顯示一個surface的抽象接口,使你可以控制surface的大小和格式, 以及在surface上編輯像素,和監視surace的改變。這個接口通常通過SurfaceView類實現。

3. SurfaceHolder.Callback

用戶可以實現此接口接收surface變化的消息。當用在一個SurfaceView 中時, 它隻在SurfaceHolder.Callback.surfaceCreated()和SurfaceHolder.Callback.surfaceDestroyed()之間有效。設置Callback的方法是SurfaceHolder.addCallback.

 

看完官方的介紹,明白瞭許多,surfaceView帶z-order的,還可以在上面操作像素有木有,還有單獨的渲染線程哦親。如果做遊戲開發,當然選SurfaceView瞭。

 

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

android中用SurfaceHolder處理SurfaceView的畫圖

 

在用SurfaceView進行遊戲開發過程中,用到SurfaceHolder來處理它的Canvas上畫的效果和動畫是必不可少的。用於控制表面,大小,像素等。
Abstract interface to someone holding a display surface. Allows you to control the surface size and format,
edit the pixels in the surface, and monitor changes to the surface. This interface is typically available
through the SurfaceView class.
其中特別要註意以下的幾個函數:
abstract void addCallback(SurfaceHolder.Callback callback);
// 給SurfaceView當前的持有者一個回調對象。
abstract Canvas lockCanvas();
// 鎖定畫佈,一般在鎖定後就可以通過其返回的畫佈對象Canvas,在其上面畫圖等操作瞭。
abstract Canvas lockCanvas(Rect dirty);
// 鎖定畫佈的某個區域進行畫圖等..因為畫完圖後,會調用下面的unlockCanvasAndPost來改變顯示內容。
// 相對部分內存要求比較高的遊戲來說,可以不用重畫dirty外的其它區域的像素,可以提高速度。
abstract void unlockCanvasAndPost(Canvas canvas);
// 結束鎖定畫圖,並提交改變。

例子:

 // 請參考上一篇文章:android中用SurfaceView做遊戲開發http://cg3a20.mail.163.com/a/f/js3/editor/0904231044/androidsurfaceview.htmlclass DrawThread extends Thread { private SurfaceHolder holder; private boolean running = true; protected DrawThread(SurfaceHolder holder) {this.holder = holder;} protected void doStop() { running = false; } public void run() { Canvas c = null; while( running ) { c = holder.lockCanvas(null); // 鎖定整個畫佈,在內存要求比較高的情況下,建議參數不要為null try { synchronized(holder) { bGrid.drawGrid(c);//畫遊戲中的網格 BBoom.drawBooms(c, booms); //畫遊戲中的炸彈 bFairy.drawFairy(c);//畫遊戲中的主角 // 畫的內容是z軸的,後畫的會覆蓋前面畫的。 } } catch(Exception ex) {} finally { holder.unlockCanvasAndPost(c); //更新屏幕顯示內容 } } } }; 在android中開發遊戲,一般來說,或想寫一個復雜一點的遊戲,是必須用到SurfaceView來開發的。 經過這一陣子對android的學習,我找到瞭自已在android中遊戲開發的誤區,不要老想著用Layout和view去實現,不要將某個遊戲 中的對象做成一個組件來處理。應該盡量想著在Canvas(畫佈)中畫出遊戲戲中的背景、人物、動畫等… SurfaceView提供直接訪問一個可畫圖的界面,可以控制在界面頂部的子視圖層。SurfaceView是提供給需要直接畫像素而不是 使用窗體部件的應用使用的。Android圖形系統中一個重要的概念和線索是surface。View及其子類(如TextView, Button) 要畫在surface上。每個surface創建一個Canvas對象(但屬性時常改變),用來管理view在surface上的繪圖操作,如畫點畫線。 還要註意的是,使用它的時候,一般都是出現在最頂層的:The view hierarchy will take care of correctly compositing with the Surface any siblings of the SurfaceView that would normally appear on top of it. 使用的SurfaceView的時候,一般情況下還要對其進行創建,銷毀,改變時的情況進行監視,這就要用到SurfaceHolder.Callback. class BBatt extends SurfaceView implements SurfaceHolder.Callback {     public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){} //看其名知其義,在surface的大小發生改變時激發     public void surfaceCreated(SurfaceHolder holder){} //同上,在創建時激發,一般在這裡調用畫圖的線程。     public void surfaceDestroyed(SurfaceHolder holder) {} //同上,銷毀時激發,一般在這裡將畫圖的線程停止、釋放。 } 例子: public class BBatt extends SurfaceView implements SurfaceHolder.Callback, OnKeyListener { private BFairy bFairy; private DrawThread drawThread; public BBatt(Context context) { super(context); this.setLayoutParams( new ViewGroup.LayoutParams( Global.battlefieldWidth, Global.battlefieldHeight)); this.getHolder().addCallback( this ); this.setFocusable( true ); this.setOnKeyListener( this ); bFairy = new BFairy(this.getContext()); } public void surfaceChanged(SurfaceHolder holder, int format,int width,int height) { drawThread = new DrawThread(holder); drawThread.start(); } public void surfaceDestroyed(SurfaceHolder holder) { if( drawThread != null ) { drawThread.doStop(); while (true) try { drawThread.join(); break ; } catch(Exception ex) {} } } public boolean onKey(View view, int keyCode, KeyEvent event) {} }

 

摘自 博雅Grant@PKU專欄

發佈留言