前言
摘自《Android群英傳》
Android提供了View進(jìn)行繪圖處理坤次,View可以滿足大部分的繪圖需求古劲,但在某些時候也會心有余而力不足斥赋。我們知道,View通過刷新來重繪視圖产艾,Android 系統(tǒng)通過發(fā)出VSYNC信號來進(jìn)行屏幕的重繪疤剑,刷新的時間間隔為16ms滑绒。如果在16ms內(nèi)View完成了你所需要執(zhí)行的所有操作,那么用戶在視覺上就不會產(chǎn)生卡頓的感覺隘膘;而如果執(zhí)行的操作邏輯太多疑故,特別是需要頻繁刷新的界面上,例如游戲界面弯菊,就會不斷阻塞主線程纵势,從而導(dǎo)致畫面卡頓。很多情況下管钳,在自定義View的log中會看到如下的警告:
“Skipped 47 frames! The application may be doing too much work on its main thread.”
為了避免這一問題的產(chǎn)生钦铁,Android系統(tǒng)提供了SurfaceView組件。
View 和 SurfaceView 的區(qū)別
- View 主要適用于主動更新的情況下才漆,而 SurfaceView 主要適用于被動更新牛曹,例如頻繁地刷新。
- View 在主線程中對畫面進(jìn)行刷新醇滥,而 SurfaceView 通常會通過一個子線程來進(jìn)行頁面的刷新黎比。
- View 在繪圖時沒有使用雙緩沖機(jī)制,而 SurfaceView 在底層實現(xiàn)機(jī)制中就已經(jīng)實現(xiàn)了雙緩沖機(jī)制鸳玩。
總結(jié)就是阅虫,如果你的自定義View需要頻繁刷新,或者刷新時數(shù)據(jù)處理量比較大不跟,那么你就可以考慮使用 SurfaceView 來取代 View 了书妻。
SurfaceView 的使用
SurfaceView 的使用雖然比 View 復(fù)雜,但是 SurfaceView 在使用時躬拢,有一套使用的模板代碼躲履,大部分的 SurfaceView 繪圖操作都可以套用這樣的模板代碼來進(jìn)行編寫。因此 SurfaceView 的使用會更加簡單聊闯。
創(chuàng)建一個 SurfaceView 的模板
1工猜、 創(chuàng)建 SurfaceView
創(chuàng)建自定義的 MySurfaceView 繼承自 SurfaceView ,并實現(xiàn)兩個接口——SurfaceHolder.Callback, Runnable菱蔬,同時實現(xiàn)其接口方法篷帅,如下:
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
@Override
public void run() {
}
}
2、初始化 SurfaceView
在自定義 SurfaceView 的構(gòu)造方法中拴泌,需要對 SurfaceView 進(jìn)行初始化魏身。通常需要定義以下三個成員變量,如下:
private SurfaceHolder mHolder;
//用于繪圖的canvas
private Canvas mCanvas;
//子線程標(biāo)志位
private boolean mIsDrawing;
初始化方法就是初始化一個 SurfaceHolder 對象并注冊 SurfaceHolder 的回調(diào)方法蚪腐,如下:
mHolder = getHolder();
mHolder.addCallback(this);
另外兩個成員變量——Canvas 和標(biāo)志位箭昵。使用 Canvas 來進(jìn)行繪圖;使用標(biāo)志位來控制之前提到的用于繪制的子線程回季。
3家制、使用 SurfaceView
通過 SurfaceHolder 對象的 lockCanvas() 方法就可以獲得當(dāng)前的 Canvas 繪圖對象正林。接下來就可以與在 View 中進(jìn)行的繪制操作一樣進(jìn)行繪制了。這里需要注意颤殴,獲取到的 Canvas 對象還是繼續(xù)上次的 Canvas 對象觅廓,而不是一個新的 Canvas 對象。因此涵但,之前的繪圖操作都會被保留杈绸。如果需要擦除,則可以在繪制前矮瘟,通過 drawColor() 方法來進(jìn)行清屏操作蝇棉。
繪制時,充分利用 SurfaceView 的三個回調(diào)方法芥永,在 surfaceCreated() 方法里開啟子線程進(jìn)行繪制篡殷,而子線程使用一個 while (mIsDrawing) {} 的循環(huán)來不停地進(jìn)行繪制,而在繪制的具體邏輯中埋涧,通過 lockCanvas() 方法來獲取 Canvas 對象來繪制板辽,并通過 unlockCanvasAndPost(mCanvas) 方法對畫布內(nèi)容進(jìn)行提交。整個 SurfaceView 模板代碼如下:
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
/**
* Created by Deeson on 2017/5/23.
*/
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
private SurfaceHolder mHolder;
//用于繪圖的canvas
private Canvas mCanvas;
//子線程標(biāo)志位
private boolean mIsDrawing;
public MySurfaceView (Context context) {
super(context);
init();
}
public MySurfaceView (Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MySurfaceView (Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mHolder = getHolder();
mHolder.addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
this.setKeepScreenOn(true);
}
@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() {
while (mIsDrawing) {
draw();
}
}
private void draw() {
try {
mCanvas = mHolder.lockCanvas();
//draw something
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != mCanvas) {
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
}
以上代碼基本可以滿足大部分 SurfaceView 的繪圖需求棘催,唯一需要注意的是在繪制方法中劲弦,將 mHolder.unlockCanvasAndPost(mCanvas);
方法放到 finally 代碼塊中,保證每次都能將內(nèi)容提交醇坝。
本文轉(zhuǎn)自 https://juejin.cn/post/6844903487310921741邑跪,如有侵權(quán),請聯(lián)系刪除呼猪。