使用SurfaceView中的Surface對象進行繪圖,其本質就是利用SurfaceHolder的lockCanvas擷取到Canvas對象進行繪製的,對於繪製動畫來說,必須使用雙緩衝,或者採用雙線程,一個線程負責專門的預先處理,比片資料讀取,另外一個線程負責進行專繪製圖形。因為SurfaceView每次繪圖都會鎖定Canvas,也就是說同一片地區這次沒畫完下次就不能畫,因此要提高動畫播放的效率,就得開一條線程專門畫圖,開另外一條線程做預先處理的工作。
下在給出一個例子,講解一下如何利用雙線程提高繪圖速度:
以下可以看到的動畫是一張解碼後的圖片從最螢幕最左邊快速移到右邊,重新開始則清屏進行顯示
package com.test.surfaceview;import java.lang.reflect.Field;import java.util.ArrayList;import android.app.Activity;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Rect;import android.os.Bundle;import android.util.Log;import android.view.SurfaceHolder;import android.view.SurfaceView;public class TestsurfaceviewActivity extends Activity {private final static String TAG = "TestsurfaceviewActivity";/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);// setContentView(new MySurfaceView(this)); // 這裡以MySurfaceView作為顯示ViewonTestInit();}private SurfaceView drawSV = null;private SurfaceHolder drawSH = null;ArrayList<Integer> imgList = new ArrayList<Integer>(); private int mWidth= 0, mHeight = 0;private Bitmap bitmap = null;private LoadImage loadImg = new LoadImage();private DrawImage drawImg = null;private void onTestInit() {drawSV = (SurfaceView) this.findViewById(R.id.SurfaceDrawView);drawSH = drawSV.getHolder();drawSH.addCallback(new MySVCallback());}private int onTestStart(){if(loadImg != null){loadImg.start();}drawImg = new DrawImage(0,mHeight);drawImg.start();return 0;}private void onTestStop(){if(loadImg != null){loadImg.stop();}if(drawImg != null){drawImg.stop();}}private class MySVCallback implements SurfaceHolder.Callback {@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {// TODO Auto-generated method stubLog.i(TAG, "surfaceChanged is called");}@Overridepublic void surfaceCreated(SurfaceHolder holder) {// TODO Auto-generated method stubLog.i(TAG, "surfaceCreated is called");// 用反射機制來擷取資源中的圖片ID和尺寸Field[] fields = R.drawable.class.getDeclaredFields();for (Field field : fields) {// 除了icon及launcher之外的圖片if (!"icon".equals(field.getName())&& !"ic_launcher".equals(field.getName())) {int index = 0;try {index = field.getInt(R.drawable.class);} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();}// 儲存圖片IDimgList.add(index);}}Log.i(TAG,"imglist size = "+imgList.size()); // 取得映像大小 Bitmap bmImg = BitmapFactory.decodeResource(getResources(), imgList.get(0)); mWidth = bmImg.getWidth(); mHeight = bmImg.getHeight(); onTestStart();}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {// TODO Auto-generated method stubLog.i(TAG, "surfaceDestroyed is called");}}private class LoadImage extends Thread {private int imgIndex = 0;public void run() {while (true) {bitmap = BitmapFactory.decodeResource(getResources(),imgList.get(imgIndex));++imgIndex;if (imgIndex == imgList.size()) { // 迴圈取圖片資料imgIndex = 0;}}}}private class DrawImage extends Thread{private int x,y;public DrawImage(int x,int y){this.x = x;this.y = y;}private void ClearScreen(){Canvas canvas = drawSH.lockCanvas(null);canvas.drawColor(Color.BLACK);// 清除畫布drawSH.unlockCanvasAndPost(canvas);}public void run() {while (true) {if (bitmap != null) {/** * 以下兩個有明顯的效率差異,lockCanvas()指定Rect內減少迴圈畫線的次數, * 可以提高繪圖效率,全屏重新整理時會很閃爍 */Canvas c = drawSH.lockCanvas(new Rect(this.x, this.y,this.x + mWidth, this.y + mHeight));//Canvas c = drawSH.lockCanvas();c.drawBitmap(bitmap, this.x, this.y, new Paint());drawSH.unlockCanvasAndPost(c);// 將顯示內容上屏this.x += 10;//如果到了終點,則清屏重來if(this.x > 1280 - mWidth){this.x = 0;ClearScreen();}}}}}}