SurfaceView的一些事
1.為什么Android設(shè)計了SurfaceView
View可以滿足大部分需求,但是在某些特殊時候也力不從心。
因為View是通過刷新來重繪視圖,Android系統(tǒng)通過發(fā)出VSYNC信號來進(jìn)行屏幕繪制,刷新間隔時間為16ms。
如果16ms內(nèi)view完成了繪制過程,那么用戶在視覺上不會感到卡頓。
但是在繪制過程中有太多的邏輯需要處理,16ms內(nèi)無法完成繪制的時候,就需要SurfaceView來解決這一問題。
2.View與SurfaceView的區(qū)別
- View主要適用于主動更新的情況下,SurfaceView適用于被動更新的情況,例如頻繁的刷新
- View在主線程中對畫面進(jìn)行刷新,SurfaceView通常會通過一個子線程來進(jìn)行頁面刷新
- View在繪制時沒有使用雙緩沖機(jī)制,SurfaceView在底層實現(xiàn)機(jī)制中實現(xiàn)了雙緩沖機(jī)制
- 雙緩沖機(jī)制解釋:在一個陽光明媚的日子,你想將水池里的水換掉,而又找不到水管的時候,你就只能用木桶來灌滿水池。當(dāng)木桶被水龍頭注滿的,關(guān)掉水龍頭,走到水池旁邊,將水到進(jìn)去,然后走回到水龍頭旁邊繼續(xù)重復(fù)上述工作,如此往復(fù)直到將水池灌滿。這就類似單緩沖工作過程。當(dāng)你想將木桶里的水倒出的時候,你必須關(guān)掉水龍頭。
- 現(xiàn)在假設(shè)你用兩個木桶來做上面的工作。你會注滿第一個木桶然后將第二個木桶換到水龍頭下面,這樣,在第二個水桶注滿的時間內(nèi),你就可以將第一個木桶里面的水倒進(jìn)水池里面,當(dāng)你回來的時候,你只需要再將第一個木桶換下第二個注滿水木桶,當(dāng)?shù)谝粋€木桶開始注水的時候你就將第二個木桶里面的水倒進(jìn)水池里面。重復(fù)這個過程直到水池被注滿。很容易看得到用這種技術(shù)注滿水池將會更快,同時也節(jié)省了很多等待木桶被注滿的時間,而這段時間里你什么也做不了,而水龍頭也就不用等待從木桶被注滿到你回來的這段時間了。
- 當(dāng)你雇傭另外一個人來搬運(yùn)一個被注滿的木桶時,這就有點(diǎn)類似于三個緩沖區(qū)的工作原理。如果將搬運(yùn)木桶的的時間很長,你可以用更多的木桶,雇傭更多的人,這樣水龍頭就會一直開著注滿木桶了。
- 在計算機(jī)圖形學(xué)中,雙緩沖是一種畫圖技術(shù),使用這種技術(shù)可以使得畫圖沒有(至少是減少)閃爍、撕裂等不良效果,并減少等待時間。
- 雙緩沖機(jī)制的原理大概是:所有畫圖操作將它們畫圖的結(jié)果保存在一塊系統(tǒng)內(nèi)存區(qū)域中,這塊區(qū)域通常被稱作“后緩沖區(qū)(back buffer)”,當(dāng)所有的繪圖操作結(jié)束之后,將整塊區(qū)域復(fù)制到顯示內(nèi)存中,這個復(fù)制操作通常要跟顯示器的光棧束同步,以避免撕裂。雙緩沖機(jī)制必須要求有比單緩沖更多的顯示內(nèi)存和CPU消耗時間,因為“后緩沖區(qū)”需要顯示內(nèi)存,而復(fù)制操作和等待同步需要CPU時間。
3.SurfaceView的使用
模板代碼
//繼承SurfaceView 類,實現(xiàn)SurfaceHolder.Callback 和 Runnable 接口
public abstract class SurfaceViewTemplate extends SurfaceView implements SurfaceHolder.Callback, Runnable {
//定義以下三個成員變量
private SurfaceHolder mHolder; //SurfaceHolder
private Canvas mCanvas; //用于繪制的Canvas
private boolean mIsDrawing; //子線程繪制標(biāo)志位
public SurfaceViewTemplate(Context context) {
super(context);
initView();
}
public SurfaceViewTemplate(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public SurfaceViewTemplate(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
//初始化
private void initView() {
mHolder = getHolder();
mHolder.addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
setKeepScreenOn(true);
onInit(mHolder);
}
//初始化時候回調(diào)
protected abstract void onInit(SurfaceHolder holder);
//當(dāng)SurfaceView創(chuàng)建的時候,開啟子線程進(jìn)行繪制操作
@Override
public void surfaceCreated(SurfaceHolder holder) {
mIsDrawing = true;
new Thread(this).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
//當(dāng)SurfaceViewDestroy時候停止繪制操作
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mIsDrawing = false;
}
private void draw() {
try {
mCanvas = mHolder.lockCanvas();
realDraw(mCanvas);
} catch (Exception e) {
onError(e);
} finally {
if (mCanvas != null) {
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
//循環(huán)進(jìn)行繪制流程
@Override
public void run() {
while (mIsDrawing) {
beforeDraw();
draw();
afterDraw();
}
}
//繪制之前回調(diào)
protected abstract void beforeDraw();
//繪制完畢回調(diào)
protected abstract void afterDraw();
//真正的繪制一些東西
protected abstract void realDraw(Canvas canvas);
//繪制時候發(fā)生異?;卣{(diào)
protected abstract void onError(Exception e);
}
繪畫板Demo
public class DrawingBoardView extends SurfaceViewTemplate {
public DrawingBoardView(Context context) {
super(context);
}
public DrawingBoardView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DrawingBoardView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private Paint mPaint;
private Path mPath;
@Override
protected void onInit(SurfaceHolder holder) {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPath = new Path();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mPath.moveTo(x, y);
break;
case MotionEvent.ACTION_MOVE:
mPath.lineTo(x, y);
break;
}
return true;
}
@Override
protected void beforeDraw() {
}
@Override
protected void afterDraw() {
}
@Override
protected void realDraw(Canvas canvas) {
//canvas默認(rèn)保存了上一次繪制的內(nèi)容
//所以這里清空一下,重新開始繪制
canvas.drawColor(Color.WHITE);
//繪制路徑
canvas.drawPath(mPath, mPaint);
}
@Override
protected void onError(Exception e) {
}
}