Surface意為表層、表面享言,顧名思義SurfaceView就是指一個在表層的View對象市殷。為什么說是在表層呢,這是因為它有點特殊跟其他View不一樣矮湘,其他View是繪制在“表層”的上面斟冕,而它就是充當“表層”本身。SDK的文檔 說到:SurfaceView就是在窗口上挖一個洞缅阳,它就是顯示在這個洞里磕蛇,其他的View是顯示在窗口上,所以View可以顯式在 SurfaceView之上十办,你也可以添加一些層在SurfaceView之上秀撇。 從API中可以看出SurfaceView屬于View的子類 它是專門為制作游戲而產(chǎn)生的,它的功能非常強大向族,最重要的是它支持OpenGL ES庫呵燕,2D和3D的效果都可以實現(xiàn)。創(chuàng)建SurfaceView的時候需要實現(xiàn)SurfaceHolder.Callback接口件相,它可以用來監(jiān)聽SurfaceView的狀態(tài)再扭,比如:SurfaceView的改變 、SurfaceView的創(chuàng)建 夜矗、SurfaceView 銷毀等霍衫,我們可以在相應的方法中做一些比如初始化的操作或者清空的操作等等。
Android系統(tǒng)提供了View進行繪圖處理侯养,我們通過自定義的View可以滿足大部分的繪圖需求敦跌,但是這有個問題就是我們通常自定義的View是用于主動更新情況的,用戶無法控制其繪制的速度,由于View是通過invalidate方法通知系統(tǒng)去調(diào)用view.onDraw方法進行重繪柠傍,而Android系統(tǒng)是通過發(fā)出VSYNC信號來進行屏幕的重繪麸俘,刷新的時間是16ms,如果在16ms內(nèi)View完成不了執(zhí)行的操作,用戶就會看著卡頓惧笛,比如當draw方法里執(zhí)行的邏輯過多从媚,需要頻繁刷新的界面上,例如游戲界面患整,那么就會不斷的阻塞主線程拜效,從而導致畫面卡頓。而SurfaceView相當于是另一個繪圖線程各谚,它是不會阻礙主線程紧憾,并且它在底層實現(xiàn)機制中實現(xiàn)了雙緩沖機制。
這里說一下雙緩沖機制
雙緩沖甚至是多緩沖昌渤,在許多情況下都很有用赴穗。一般需要使用雙緩沖區(qū)的地方都是由于“生產(chǎn)者”和“消費者”供需不一致所造成的。這樣的情況在很多地方后可能會發(fā)生膀息,使用多緩沖可以很好的解決般眉。我舉幾個常見的例子:
例 1. 在網(wǎng)絡傳輸過程中數(shù)據(jù)的接收,有時可能數(shù)據(jù)來的太快來不及接收導致數(shù)據(jù)丟失潜支。這是由于“發(fā)送者”和“接收者”速度不一致所致甸赃,在他們之間安排一個或多個緩沖區(qū)來存放來不及接收的數(shù)據(jù),讓速度較慢的“接收者”可以慢慢地取完數(shù)據(jù)不至于丟失冗酿。
2.如何使用SurfaceView埠对?
首先SurfaceView也是一個View,它也有自己的生命周期已烤。因為它需要另外一個線程來執(zhí)行繪制操作,所以我們可以在它生命周期的初始化階 段開辟一個新線程妓羊,然后開始執(zhí)行繪制胯究,當生命周期的結束階段我們插入結束繪制線程的操作。這些是由其內(nèi)部一個SurfaceHolder對象完成的躁绸。
SurfaceView它的繪制原理是繪制前先鎖定畫布(獲取畫布)裕循,然后等都繪制結束以后在對畫布進行解鎖 ,最后在把畫布內(nèi)容顯示到屏幕上净刮。
通常情況下剥哑,使用以下步驟來創(chuàng)建一個SurfaceView的模板:(實現(xiàn)實時手繪)
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
private static final String TAG = "SurfaceView";
//SurfaceHolder
private SurfaceHolder mHolder;
//用于繪圖的Canvas
private Canvas mCanvas;
//子線程標志位
private boolean mIsDrawing;
//畫筆
private Paint mPaint;
//路徑
private Path mPath;
public MySurfaceView(Context context) {
super(context);
initView();
}
public MySurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView() {
mHolder = getHolder();
//添加回調(diào)
mHolder.addCallback(this);
mPath = new Path();
//初始化畫筆
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(6);
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
setFocusable(true);
setFocusableInTouchMode(true);
this.setKeepScreenOn(true);
}
//Surface的生命周期
@Override
public void surfaceCreated(SurfaceHolder holder) {
mIsDrawing = true;
new Thread(this).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mIsDrawing = false;
}
@Override
public void run() {
long start = System.currentTimeMillis();
while (mIsDrawing) {
draw();
long end = System.currentTimeMillis();
if (end - start < 100) {
try {
Thread.sleep(100 - end + start);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private void draw() {
try {
//鎖定畫布并返回畫布對象
mCanvas = mHolder.lockCanvas();
//接下去就是在畫布上進行一下draw
mCanvas.drawColor(Color.WHITE);
mCanvas.drawPath(mPath, mPaint);
} catch (Exception e) {
} finally {
//當畫布內(nèi)容不為空時,才post淹父,避免出現(xiàn)黑屏的情況株婴。
if (mCanvas != null)
mHolder.unlockCanvasAndPost(mCanvas);
}
}
/**
* 繪制觸摸滑動路徑
*
* @param event MotionEvent
* @return true
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "onTouchEvent: down");
mPath.moveTo(x, y);
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "onTouchEvent: move");
mPath.lineTo(x, y);
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "onTouchEvent: up");
break;
}
return true;
}
/**
* 清屏
*
* @return true
*/
public boolean reDraw() {
mPath.reset();
return true;
}
}