首先我們來看一下本例需要用到的兩個素材圖片:
bj.jpg就是一個漸變圖,用作背景。
question.png是一個半透明的圖像,我們希望將它放在上面,圍繞其圓心不斷旋轉。
實現代碼如下:
package SkyD.SurfaceViewTest;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class Main extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MySurfaceView(this));
}
// 自定義的SurfaceView子類
class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {
// 背景圖
private Bitmap BackgroundImage;
// 問號圖
private Bitmap QuestionImage;
SurfaceHolder Holder;
public MySurfaceView(Context context) {
super(context);
BackgroundImage = BitmapFactory.decodeResource(getResources(),
R.drawable.bg);
QuestionImage = BitmapFactory.decodeResource(getResources(),
R.drawable.question);
Holder = this.getHolder();// 獲取holder
Holder.addCallback(this);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// 啟動自定義線程
new Thread(new MyThread()).start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
// 自定義線程類
class MyThread implements Runnable {
@Override
public void run() {
Canvas canvas = null;
int rotate = 0;// 旋轉角度變量
while (true) {
try {
canvas = Holder.lockCanvas();// 獲取畫布
Paint mPaint = new Paint();
// 繪制背景
canvas.drawBitmap(BackgroundImage, 0, 0, mPaint);
// 創建矩陣以控制圖片旋轉和平移
Matrix m = new Matrix();
// 設置旋轉角度
m.postRotate((rotate += 48) % 360,
QuestionImage.getWidth() / 2,
QuestionImage.getHeight() / 2);
// 設置左邊距和上邊距
m.postTranslate(47, 47);
// 繪制問號圖
canvas.drawBitmap(QuestionImage, m, mPaint);
// 休眠以控制最大幀頻為每秒約30幀
Thread.sleep(33);
} catch (Exception e) {
} finally {
Holder.unlockCanvasAndPost(canvas);// 解鎖畫布,提交畫好的圖像
}
}
}
}
}
}
模擬器中的運行效果:
(注:圖中的問號圖形是在不斷旋轉中的)
這看起來不錯,但是有一個問題:我們在代碼中設置的幀頻最大值是每秒30幀,而實際運行時的幀頻根據目測就能看出是到不了30幀的,這是因為程序在每一幀都要對整個畫面進行重繪,過多的時間都被用作繪圖處理,所以難以達到最大幀頻。
接下來我們將采取髒矩形刷新的方法來優化性能,所謂髒矩形刷新,意為僅刷新有新變化的部分所在的矩形區域,而其他沒用的部分就不去刷新,以此來減少資源浪費。
我們可以通過在獲取Canvas畫布時,為其指派一個參數來聲明我們需要畫布哪個局部,這樣就可以只獲得這個部分的控制權:
在這裡為了便於觀察,我將矩形區域設定為問號圖形的1/4區域,也就是說在整個畫面中我們僅僅更新問號圖形的1/4大小那麼點區域,其執行效果為:
可以看到,僅有那1/4區域在快速刷新,其他部分都是靜止不動的了,現在的刷新幀頻差不多已經能達到最大幀頻了,我們的優化起作用了:)
不過別高興的太早,實際上如果把刷新區域擴大到整個問號圖形所在的矩形區域的話,你會發現優化作用變得微乎其微了,還是沒法達到最大幀頻的,因為更新區域增大了3倍,帶來的資源消耗也就大幅增加。
這種情況下就應當考慮結合覆蓋刷新方法再進一步優化了。
試想一下,我們每次刷新時最大的消耗在哪?
沒錯,在背景圖繪制上,這個繪制區域非常大,會消耗我們很多資源,但實際上背景圖在此例中是從不變化的,也就是說我們浪費了很多資源在無用的地方。
那麼可不可以只繪制一次背景,以後每次都只繪制會動的���號圖形呢?
完全可以,嘗試修改一下代碼,再前面加一個幀計數器,然後我們僅在第一幀的時候繪制背景:
這樣很簡單,但是改後直接運行的話你會發現一個奇怪的狀況:
問號圖案會變得有殘影了。
啊哈,這正是我使用半透明圖案做范例的目的,通過這個重影,我們就能看出,覆蓋刷新其實就是將每次的新的圖形繪制到上一幀去,所以如果圖像是半透明的,就要考慮重復疊加導致的問題了,而如果是完全不透明的圖形則不會有任何問題。
背景會在背景圖和黑色背景之間來回閃。
這個問題其實是源於SurfaceView的雙緩沖機制,我理解就是它會緩沖前兩幀的圖像交替傳遞給後面的幀用作覆蓋,這樣由於我們僅在第一幀繪制了背景,第二幀就是無背景狀態了,且通過雙緩沖機制一直保持下來,解決辦法就是改為在前兩幀都進行背景繪制:
現在就沒有問題了(如果換成個不透明的圖形的話就真沒問題了):
現在雖然還是達不到最大幀頻,但是也算不錯啦,在真機上跑的會更快些,接近最大幀頻了。
我這也是剛接觸Android開發,分享這點心得出來,有寫的不對的歡迎指點一二^^