Android之SurfaceView简介(一)
1. SurfaceView介紹
? ?? ? ? 通常情況程序的View和用戶響應都是在同一個線程中處理的,這也是為什么處理長時間事件(例如訪問網絡)需要放到另外的線程中去(防止阻塞當前UI線程的操作和繪制)。但是在其他線程中卻不能修改UI元素,例如用后臺線程更新自定義View(調用View的在自定義View中的onDraw函數)是不允許的。?
? ? 如果需要在另外的線程繪制界面、需要迅速的更新界面,或者渲染UI界面需要較長的時間,這種情況就要使用SurfaceView了。SurfaceView中包含一個Surface對象,而Surface是可以在后臺線程中繪制的。 ? ? SurfaceView的性質決定了其比較適合一些場景:需要界面迅速更新、對幀率要求較高的情況。 ? ? 使用SurfaceView需要注意以下幾點情況: ? ? ?(1)SurfaceView和SurfaceHolder.Callback函數都從當前SurfaceView窗口線程中調用(一般而言就是程序的主線程)。 ? ? ?(2)有關資源狀態要注意和繪制線程之間的同步。? ? ? ?(3)在繪制線程中必須先合法的獲取Surface才能開始繪制內容,在SurfaceHolder.Callback.surfaceCreated() 和SurfaceHolder.Callback.surfaceDestroyed()之間的狀態為合法的,在Surface類型為SURFACE_TYPE_PUSH_BUFFERS時是不合法的。? ? ? ?(4)額外的繪制線程會消耗系統的資源,在使用SurfaceView的時候要注意這點。?
?
2. 使用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對象了。
? ? 注意:一個SurfaceView只在SurfaceHolder.Callback.surfaceCreated() 和 SurfaceHolder.Callback.surfaceDestroyed()調用之間是可用的,其他時間是得不到它的Canvas對象的(null)。
3. SurfaceView和View的區別
? ??
? ? SurfaceView和View最本質的區別在于,surfaceView是在一個新起的單獨線程中可以重新繪制畫面,而View必須在UI的主線程中更新畫面。
? ? 在UI的主線程中更新畫面可能會引發問題,比如你更新畫面的時間過長,那么你的主UI線程會被你正在畫的函數阻塞。那么將無法響應按鍵,觸屏等消息。當使用SurfaceView時,由于是在新的線程中更新畫面所以不會阻塞UI主線程。但這也帶來了另外一個問題,就是事件同步。比如你觸屏了一下,你需要SurfaceView中的Thread處理,一般就需要有一個event queue的設計來保存touch event,這會稍稍復雜一點,因為涉及到線程同步。
? ? 所以基于以上,根據游戲特點,一般分成兩類:
(1)被動更新畫面的。比如棋類,用View就好了。因為畫面的更新是依賴于 onTouch 來更新,可以直接使用 invalidate。 因為在這種情況下,這一次Touch和下一次的Touch需要的時間比較長些,不會產生影響。
(2)主動更新畫面的。比如一個人在一直跑動。這就需要一個單獨的thread不停的重繪人的狀態,避免阻塞UI主線程。所以顯然view不合適,需要surfaceView來控制。
總結
以上是生活随笔為你收集整理的Android之SurfaceView简介(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android之Camera介绍
- 下一篇: Android之bitmap压缩的几种方