SurfaceView與View的區(qū)別
如果在16ms內(nèi)View完成了你所需要執(zhí)行的所有操作读处,那么用戶在視覺上挑格,就不會產(chǎn)生卡頓的感覺;而如果操作的邏輯太多植酥,特別是需要頻繁刷新的界面商行漱挚,那么慢就不會不斷阻塞主線程翔烁,從而導(dǎo)致畫面卡頓。為了避免這一問題的產(chǎn)生旨涝,Android系統(tǒng)提供了SurfaceView組價來解決這個問題蹬屹,SurfaceView可以說是View的孿生兄弟,但它與View還是有所不同的颊糜,它們的區(qū)別主要變現(xiàn)在以下幾點哩治。
- View 主要適用于主動更新的情況下,而SurfaceView主要適用于被動更新衬鱼,例如頻繁刷新业筏。
- View 在主線程中對畫面進行刷新,而SurfaceView通常會通過一個子線程來進行頁面的刷新鸟赫。
- View 在繪圖時沒有使用緩沖機制蒜胖,而SurfaceView的底層實現(xiàn)機制中就已經(jīng)實現(xiàn)了雙緩沖機制消别。
SurfaceView的使用
1.創(chuàng)建SurfaceView
創(chuàng)建自定義的SurfaceView繼承自SurfaceView,并實現(xiàn)兩個接口——SurfaceHolder
.Callback和Runnable,并重寫相關(guān)的方法台谢。其中SurfaceHolder.Callback的三個方法分別對應(yīng)SurfaceView的創(chuàng)建寻狂,改變和銷毀的過程。
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback,Runnable {
public MySurfaceView(Context context) {
super(context);
}
public MySurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@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進行初始化蛇券。在自定義的SurfaceView中,通常需要定義以下三個成員變量:
private SurfaceHolder mHolder;
//畫布樊拓,用于畫圖
private Canvas mCanvas;
//子線程標志位
private boolean mIsDrawing;
初始化方法就是對SurfaceHolder進行初始化纠亚,通過以下代碼來初始化一個SurfaceView對象,并注冊SurfaceViewHolder的回調(diào)方法筋夏。
mHolder=getHolder();
mHolder.addCallback(this);
另外兩個成員變量——Canvas和標志位蒂胞。對Canvas我們已經(jīng)非常熟悉了,與在View的onDraw()方法中使用Canvas繪圖一樣条篷,在SurfaceView中骗随,我們也使用Canvas進行繪圖,另一個標志位赴叹,則是用來控制子線程鸿染,SurfaceView通常會掛起一個子線程來進行繪制,而這個標志位就是可以控制子線程乞巧。
3.使用SurfaceView
通過SurfaceHolder對象的lockCanvas()方法牡昆,就可以獲得當前的Canvas繪圖對象。接下來摊欠,就可以在View中進行的繪制操作一下進行繪制了。不過需要注意的是柱宦,在獲取到的Canvas對象還是繼續(xù)上次的Canvas對象些椒,而不是一個新的對象。因此掸刊,之前的繪圖操作都是將保留免糕,如果需要擦除,則可以在繪制前忧侧,通過drawColor()方法進行清屏的操作石窑。
繪制的時候,充分利用SurfaceView的三個回調(diào)方法蚓炬,在surfaceCreate()方法中個開啟子線程進行繪制松逊,而子線程使用一個while(mIsDrawing)的循環(huán)來不停地進行繪制,在繪制的具體邏輯中肯夏,通過lockCanvas()方法獲得的Canvas對象進行繪制经宏,并通過unlockCanvasAndPost(mCanvas)方法對畫布內(nèi)容進行提交犀暑。
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback,Runnable {
private SurfaceHolder mHolder;
//畫布,用于畫圖
private Canvas mCanvas;
//子線程標志位
private boolean mIsDrawing;
public MySurfaceView(Context context) {
this(context,null);
}
public MySurfaceView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr,0);
}
public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
intView();
}
private void intView() {
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();
//進行繪制
}catch (Exception e){
}finally {
if (mCanvas!=null){
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
}
以上的代碼基本上可以滿足大部分的SurfaceView繪制需求烁兰,唯一需要注意的是在繪制方法中耐亏,將mHolder.unlockCanvasAndPost(mCavas)方法放到finally代碼中,來保證每次都能將內(nèi)容提交沪斟。