SurfaceView可以說是View的孿生兄弟了,其實在Android系統提供的View就可以滿足大部分的繪圖需求了,但是在某些 時候,View也有些心有餘而力不足。我們知道,View通過刷新來重回視圖,android系統通過發出VSYNC信號進行視圖的重 繪,刷新間隔爲16ms,然後對於操作邏輯太多,需要頻繁刷新頁面(如:遊戲界面)時,就會不斷的阻塞主線程,從而導致頁面 卡頓。爲了避免這一問題,Android系統提供了SurfaceView組件來解決這一問題,下面一起來看一下SurfaceView的簡單使用。
SurfaceView與View的區別主要體現在
- View主要適用於主動更新、刷新情況,SurfaceView主要適用於被動更新、刷新情況;
- View在繪製時沒有適用雙緩存機制,SurfaceView採用的雙緩存機制;
SurfaceView的使用
SurfaceView的使用比View要複雜,但是在使用SurfaceView時,按照如下幾部來使用,會讓SurfaceView的使用更加簡單。
1、創建SurfaceView
創建自定義的SurfaceView集成SurfaceView,並實現兩個接口——SurfaceHolder.Callback、Runnable,代碼如下所示:
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback,Runnable{
當然也可以通過內部類實現這兩個接口。對於SurfaceHolder.Callback需要實現如下三個方法:
//創建
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
//改變
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
//銷燬
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
分別對應SurfaceView的創建、改變、銷燬的過程。對於Runnable接口,需要實現run()。
2、初始化SurfaceView
在自定義的SurfaceView中,通常需要定義以下幾個成員變量,代碼如下:
private SurfaceHolder mHolder;//SurfaceHolder
private Canvas mCanvas;//用於繪製的Canvas
private boolean mIsDrawing;//子線程標誌位
初始化方法就是對SurfaceHolder進行初始化,初始化一個SurfaceHolder對象,並註冊SurfaceHolder的回調方法。Canvas與View的onDraw()方法中使用的Canvas一樣,進行繪製用的。標誌位則是用來控制子線程的,SurfaceView通常會起一個子線程來進行繪製,而這個標誌位就可以可以子線程。
3、使用SurfaceView
通過SurfaceHolder對象的lockCanvas()方法,就可以獲得當前的Canvas繪圖對象。接下來,就可以與在View中進行繪製操作一樣進行繪製了,獲取到的Canvas對象還是繼續上次的Canvas對象,因此,之前的繪圖操作都將被保留,如果需要擦除,可以在繪製前調用drawColor()方法來進行清屏。繪製的時候,充分利用SurfaceView的三個回調方法,在surfaceCreated方法中開啓子線程進行繪製,而子線程使用一個while(mIsDrawing)的循環來不停地進行繪製,而在繪製的具體邏輯中,通過lockCanvas()方法獲取Canvas對象進行繪製,並通過unlockCanvasAndPost(mCanvas)方法對畫布進行提交。整個SurfaceView的模版代碼如下所示:
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback,Runnable{
private SurfaceHolder mHolder;//SurfaceHolder
private Canvas mCanvas;//用於繪製的Canvas
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);
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);//提交畫布內容
}
}
}
}
標題例子——餘弦曲線繪製
看一個類似示波器的例子,要繪製一個餘弦曲線,只需要不斷的修改橫縱座標的值,並讓它們滿足餘弦函數即可。主要代碼如下:
@Override
public void run() {
while(mIsDrawing){
draw();
x+=1;
y=(int)(100*Math.cos(x*2*Math.PI/180)+200);
mPath.lineTo(x,y);
}
}
private void draw(){
try{
mCanvas=mHolder.lockCanvas();
//這裏寫要繪製的內容
mCanvas.drawColor(Color.WHITE);
mCanvas.drawPath(mPath,mPaint);
}catch (Exception e){
}finally {
if(mCanvas!=null){
mHolder.unlockCanvasAndPost(mCanvas);//提交畫布內容
}
}
}