想要學習SurfaceView,必須瞭解SurfaceView、Surface、SurfaceHolder。
一、Surface
下面是Google提供的關於Surface的API的概述:
Handle onto a raw buffer that is being managed by the screen compositor.
直接從raw緩衝區中讀取資料到螢幕。
surface的意思就是介面,可以理解為我們要顯示的畫面,不過知識先將它儲存到記憶體中,完成後,才顯示到螢幕中,也就是SurfaceView中。
幾個重要的方法
二、SurfaceHolder
下面是Google提供的關於SurfaceHolder的API的概述:
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 theSurfaceView
class.
When using this interface from a thread other than the one running its SurfaceView, you will want to carefully read the methodslockCanvas() andCallback.surfaceCreated().
SurfaceHolder是一個抽象的介面,用來支援顯示一個surface。它允許你控制這個surface的大小和格式,允許你編輯這個surface,並且監測這個surface的變化。這個介面
的典型應用是通過SurfaceView類來實現的。
當你在一個線程中使用這個介面,而不是通過運行一個SurfaceView得到,那麼,你就要自己掌握lockCanvas()和Callback.surfaceCreated().
也就是說SurfaceHolder是操作Surface的介面。
SurfaceHolder的方法
關於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對象了。
雖然setType()方法已經被聲明deprecated,但是必要的時候還是設定下比較好。
幾個需要注意的方法:
(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);
// 結束鎖定畫圖,並提交改變。
三、SurfaceView
下面是Google提供的關於SurfaceView的API的概述:
Provides a dedicated drawing surface embedded inside of a view hierarchy. You can control the format of this surface and, if you like, its size; the SurfaceView takes care of placing the surface at the correct location on the screen
The surface is Z ordered so that it is behind the window holding its SurfaceView; the SurfaceView punches a hole in its window to allow its surface to be displayed. 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. This can be used to place overlays such as buttons on top of the Surface, though note however that it can have an impact on performance since a full alpha-blended composite will be performed each
time the Surface changes.
Access to the underlying surface is provided via the SurfaceHolder interface, which can be retrieved by callinggetHolder().
The Surface will be created for you while the SurfaceView's window is visible; you should implementsurfaceCreated(SurfaceHolder) andsurfaceDestroyed(SurfaceHolder) to discover when the Surface is created and destroyed
as the window is shown and hidden.
One of the purposes of this class is to provide a surface in which a secondary thread can render into the screen. If you are going to use it this way, you need to be aware of some threading semantics:
- All SurfaceView and
SurfaceHolder.Callback methods will be called from the thread running the SurfaceView's window (typically the main thread of the application). They thus need to correctly synchronize with any state that is also touched
by the drawing thread.
- You must ensure that the drawing thread only touches the underlying Surface while it is valid -- between
SurfaceHolder.Callback.surfaceCreated() andSurfaceHolder.Callback.surfaceDestroyed().
翻譯(Copy別人的,唉,英語太差了,傷不起啊!,努力學習英文!)
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)結束鎖定畫圖,並提交改變,將圖形顯示。
下面是SurfaceView的一個簡單測試,特別提醒,最好設定SurfaceHolder.addCallBack,重寫裡面的方法,然後在public void surfaceCreated(SurfaceHolder holder) 裡面,在再SurfaceView中畫圖,防止SurfaceView還沒有建立好,而導致的異常。
import java.util.Timer;import java.util.TimerTask;import android.app.Activity;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Rect;import android.os.Bundle;import android.util.Log;import android.view.SurfaceHolder;import android.view.SurfaceView;/** * 畫一個布滿螢幕的格子 * */public class SurfaceViewTest2Activity extends Activity {/** Called when the activity is first created. */private SurfaceView surfaceView;private SurfaceHolder holder;private int screenWidth;private int screenHeight;private Timer timer;private MyTimerTask1 task1;private float currentX = 0.0f;// 記錄當前的橫向繪製直線的進度private float currentY = 0.0f;// 記錄當前的縱向繪製直線的進度private Paint paint;// 畫筆private boolean reversion = false;// 控制繪製線條的反轉繪製private int countLine = 0;// 記錄畫的X是第幾條線@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);// 計算螢幕的寬高screenWidth = getWindowManager().getDefaultDisplay().getWidth();screenHeight = getWindowManager().getDefaultDisplay().getHeight();// SurfaceView和holder的初始化surfaceView = (SurfaceView) findViewById(R.id.sv);holder = surfaceView.getHolder();holder.addCallback(new SurfaceCallBack());// 計時器和任務的初始化timer = new Timer();task1 = new MyTimerTask1();// 畫筆的設定paint = new Paint();paint.setColor(Color.GREEN);// 畫筆為綠色...paint.setStrokeWidth(2);// 設定畫筆粗細}public class SurfaceCallBack implements SurfaceHolder.Callback {public void surfaceCreated(SurfaceHolder holder) {timer.schedule(task1, 1000, 5);}public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {}public void surfaceDestroyed(SurfaceHolder holder) {}}class MyTimerTask1 extends TimerTask {@Overridepublic void run() {// 開始繪製drawGrid(currentX, currentY);// 如果不反轉的話,那麼currentX++,否側--if (!reversion)currentX++;elsecurrentX--;// 保持繪製x和y的同步,不過不知道為什麼,在模擬器上,X到頂了,而Y沒有// 不過從輸出上看,是同步的,可能是模擬器上的問題currentY = ((float) screenHeight / screenWidth) * currentX;Log.i("test", currentX + " " + currentY);// 如果超過螢幕,那麼線條就要反轉,並且線條的個數要加1if (currentX >= screenWidth) {reversion = true;countLine++;}if (currentX <= 0) {reversion = false;countLine++;}}}public void drawGrid(float x, float y) {Canvas canvas = holder.lockCanvas(new Rect(0, 0, screenWidth,screenHeight));if (!reversion) {//沒有反轉的繪製情況canvas.drawLine(0, countLine * 15, x, countLine * 15, paint);canvas.drawLine(countLine * 15, 0, countLine * 15, y, paint);} else {//反轉的繪製情況canvas.drawLine(screenWidth, countLine * 15, x, countLine * 15,paint);canvas.drawLine(countLine * 15, screenHeight, countLine * 15, y,paint);}holder.unlockCanvasAndPost(canvas);}}
參考學習的文章:點擊開啟連結
參考學習的文章:點擊開啟連結