Android之——Surface、SurfaceView與SurfaceHolder.Callback初探
一、Surface Surface在SDK的文件中的描述是這樣的:Handle onto a raw buffer that is being managed by the screen compositor,Android中的Surface就是一個用來畫圖形(graphics)或影象(image)的地方,對於View及其子類,都是畫在Surface上,各Surface物件通過Surfaceflinger合成到frameBuffer,每個Surface都是雙緩衝,它有一個backBuffer和一個frontBuffer,Surface中建立了Canvas物件,用來管理Surface繪圖操作,Canvas對應Bitmap,儲存Surface中的內容。流程為:
1:建立一個Bitmap物件。
2:建立一個Canvas物件關聯建立的Bitmap物件。
3:在Canvas上進行繪製。
4:鎖定Canvas畫布。
5:將Bitmap內容繪製到backBuffer中去。
6:解鎖Canvas畫布。
二、SurfaceView SurfaceView是檢視類View的子類,且實現了Parcelable介面且實現了Parcelable介面,其中內嵌了一個專門用於繪製的Surface,SurfaceView可以控制這個Surface的格式和尺寸,以及Surface的繪製位置。可以理解為Surface就是管理資料的地方,SurfaceView就是展示資料的地方。
三、SurfaceHolder SurfaceHolder是一個介面,類似於一個surace的監聽器。通過下面三個回撥方法監聽Surface的建立、銷燬或者改變。
SurfaceView中呼叫getHolder方法,可以獲得當前SurfaceView中的surface對應的SurfaceHolder,SurfaceHolder中重要的方法有:
1: abstract void addCallback(SurfaceHolder.Callback callback );為SurfaceHolder新增一個SurfaceHolder.Callback回撥介面。
2: abstract Canvas lockCanvas() ;獲取Surface中的Canvas物件,並鎖定之。所得到的Canvas物件。
3:abstract void unlockCanvasAndPost(Canvas canvas);當修改Surface中的資料完成後,釋放同步鎖,並提交改變,然後將新的資料進行展示。
四、SurfaceHolder.Callback SurfaceHolder.Callback是SurfaceHolder介面內部的靜態子介面,SurfaceHolder.Callback中定義了三個介面方法: 1:public void sufaceChanged(SurfaceHolder holder,int format,int width,int height){}//Surface的大小發生改變時呼叫。 2: public void surfaceCreated(SurfaceHolder holder){}//Surface建立時激發,一般在這裡呼叫畫面的執行緒。 3: public void surfaceDestroyed(SurfaceHolder holder){}//銷燬時激發,一般在這裡將畫面的執行緒停止、釋放。
SurfaceView和View最本質的區別在於:SurfaceView是在一個新起的單獨執行緒中可以重新繪製畫面而View必須在UI的主執行緒中更新畫面。下面是SurfaceView的例子:
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.drawable.BitmapDrawable;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder.Callback;
public class MySurfaceView extends SurfaceView implements Runnable, Callback {
private SurfaceHolder mHolder; // 用於控制SurfaceView
private Thread t; // 宣告一條執行緒
private volatile boolean flag; // 執行緒執行的標識,用於控制執行緒
private Canvas mCanvas; // 宣告一張畫布
private Paint p; // 宣告一支畫筆
float m_circle_r = 10;
public MySurfaceView(Context context) {
super(context);
mHolder = getHolder(); // 獲得SurfaceHolder物件
mHolder.addCallback(this); // 為SurfaceView新增狀態監聽
p = new Paint(); // 建立一個畫筆物件
p.setColor(Color.WHITE); // 設定畫筆的顏色為白色
setFocusable(true); // 設定焦點
}
/**
* 當SurfaceView建立的時候,呼叫此函式
*/
@Override
public void surfaceCreated(SurfaceHolder holder) {
t = new Thread(this); // 建立一個執行緒物件
flag = true; // 把執行緒執行的標識設定成true
t.start(); // 啟動執行緒
}
/**
* 當SurfaceView的檢視發生改變的時候,呼叫此函式
*/
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
/**
* 當SurfaceView銷燬的時候,呼叫此函式
*/
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
flag = false; // 把執行緒執行的標識設定成false
mHolder.removeCallback(this);
}
/**
* 當螢幕被觸控時呼叫
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
return true;
}
/**
* 當用戶按鍵時呼叫
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
surfaceDestroyed(mHolder);
return super.onKeyDown(keyCode, event);
}
@Override
public void run() {
while (flag) {
try {
synchronized (mHolder) {
Thread.sleep(100); // 讓執行緒休息100毫秒
Draw(); // 呼叫自定義畫畫方法
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (mCanvas != null) {
// mHolder.unlockCanvasAndPost(mCanvas);//結束鎖定畫圖,並提交改變。
}
}
}
}
/**
* 自定義一個方法,在畫布上畫一個圓
*/
protected void Draw() {
mCanvas = mHolder.lockCanvas(); // 獲得畫布物件,開始對畫布畫畫
if (mCanvas != null) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLUE);
paint.setStrokeWidth(10);
paint.setStyle(Style.FILL);
if (m_circle_r >= (getWidth() / 10)) {
m_circle_r = 0;
} else {
m_circle_r++;
}
Bitmap pic = ((BitmapDrawable) getResources().getDrawable(
R.drawable.qq)).getBitmap();
mCanvas.drawBitmap(pic, 0, 0, paint);
for (int i = 0; i < 5; i++)
for (int j = 0; j < 8; j++)
mCanvas.drawCircle(
(getWidth() / 5) * i + (getWidth() / 10),
(getHeight() / 8) * j + (getHeight() / 16),
m_circle_r, paint);
mHolder.unlockCanvasAndPost(mCanvas); // 完成畫畫,把畫布顯示在螢幕上
}
}
}