轉載請註明:http://blog.csdn.net/zhangshuaizaxia/article/details/50476869,來自Davencheung的博客!
什麼是SurfaceView?
其實在去年學習Android的APIDEMO的時候,有接觸到SurfaceView, 但是卻一直不是很明白他的作用,只知道他和View有一些區別。事實上,SurfaceView是繼承View。我們知道Android系統通過發出VSYNC信號來刷新屏幕,刷新的時間間隔爲16ms,也就是在有屏幕刷新的情況下,view的draw函數中的內容必須在16ms內完成,如果超出這個時間,就會出現掉幀。而SurfaceView何時刷新,則是可以控制的。
下面是surfaceView的API的譯文(摘自網絡)
SurfaceView是視圖(View)的繼承類,這個視圖裏內嵌了一個專門用於繪製的Surface。你可以控制這個Surface的格式和尺寸。Surfaceview控制這個Surface的繪製位置,surface是縱深排序(Z-ordered)的,這表明它總在自己所在窗口的後面。surfaceview提供了一個可見區域,只有在這個可見區域內 的surface部分內容纔可見,可見區域外的部分不可見。surface的排版顯示受到視圖層級關係的影響,它的兄弟視圖結點會在頂端顯示。這意味者
surface的內容會被它的兄弟視圖遮擋,這一特性可以用來放置遮蓋物(overlays)(例如,文本和按鈕等控件)。注意,如果surface上面 有透明控件,那麼它的每次變化都會引起框架重新計算它和頂層控件的透明效果,這會影響性能。你可以通過SurfaceHolder接口訪問這個surface,getHolder()方法可以得到這個接口。surfaceview變得可見時,surface被創建;surfaceview隱藏前,surface被銷燬。這樣能節省資源。如果你要查看 surface被創建和銷燬的時機,可以重載surfaceCreated(SurfaceHolder)和
surfaceDestroyed(SurfaceHolder)。
surfaceview的核心在於提供了兩個線程:UI線程和渲染線程。這裏應注意:
1. 所有SurfaceView和SurfaceHolder.Callback的方法都應該在UI線程裏調用,一般來說就是應用程序主線程。渲染線程所要訪問的各種變量應該作同步處理。
2. 由於surface可能被銷燬,它只在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()之間有效,所以要確保渲染線程訪問的是合法有效的surface。
另外,通過下面幾點,你可以確定何時選擇使用SurfaceView:
1. View適用於主動更新的情況下,而SurfaceView適應於被動刷新,頻繁的刷新,例如遊戲界面;
2. View在主線程中對畫面進行刷新,而SurfaceView通常會通過一個子線程來進行頁面的刷新;(可以在子線程中做大量的處理)
3. View在繪圖的時候沒有使用雙緩存的機制,而SurfaceView在底層使用雙緩存的機制。
SurfaceView使用模板
SurfaceView的使用是有規矩可以遵循,在《Android羣英會》一書中,作者給出了一個使用模板,使用起來很簡單!
package com.cheungshuai.surfaceview;
import java.util.Calendar;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
/**
*
* @author davencheung
*
*/
public class SurfaceViewTemplate extends SurfaceView implements SurfaceHolder.Callback, Runnable{
private SurfaceHolder mHolder;
private Canvas mCanvas;
private boolean mIsDrawing;//whether is drawing
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);
//do something here!
mPaint = new Paint();
mPaint.setTextSize(50);
mPaint.setColor(Color.RED);
mPaint.setTextAlign(Align.CENTER);
}
private Calendar mCalendar;
private Paint mPaint;
private String mTime;
@Override
public void run() {
while(mIsDrawing){
draw();
}
}
private void draw(){
try{
mCanvas = mHolder.lockCanvas();
//do something here!
mCanvas.drawColor(Color.WHITE);
mCalendar = Calendar.getInstance();
mTime = mCalendar.get(Calendar.HOUR_OF_DAY)+"時"+
mCalendar.get(Calendar.MINUTE)+"分"+
mCalendar.get(Calendar.SECOND)+"秒";
mCanvas.drawText(mTime, getWidth()/2, getHeight()/2, mPaint);
}catch(Exception e){
}finally{
if(mCanvas != null){
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mIsDrawing = true;
new Thread(this).start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mIsDrawing = false;
}
}
基本上按照上面的使用方法,就可以完成很多事情。我們要做的就是在下面這行代碼下面添加一些自己的內容即可! //do something here!
而在MainActiivity中只需要如下的處理:
SurfaceViewTemplate sv = new SurfaceViewTemplate(this);
setContentView(sv);
控制刷新間隔時間
另外,我們可以在線程的run函數中控制線程的運行,然後是的刷新間隔時間改變。
例如,修改下面的代碼,就可以讓界面每5秒刷新一次!
while(mIsDrawing){
draw();
try{
Thread.sleep(5000);
} catch (InterruptedException e){
e.printStackTrace();
}
}
記住,這個sleep需要在while循環調用,因爲,第一次運行之後,界面就一直通過這個循環來刷新,不會再走其他的流程,代碼無法執行到(Android羣英會中的示例應該有問題),直到退出應用!
運行的效果圖如下: