歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Android SurfaceView 繪圖覆蓋刷新及髒矩形刷新方法

Android SurfaceView 繪圖覆蓋刷新及髒矩形刷新方法

日期:2017/3/1 10:31:19   编辑:Linux編程
SurfaceView在Android中用作游戲開發是最適宜的,本文就將演示游戲開發中常用的兩種繪圖刷新策略在SurfaceView中的實現方法。

首先我們來看一下本例需要用到的兩個素材圖片:

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開發,分享這點心得出來,有寫的不對的歡迎指點一二^^

Copyright © Linux教程網 All Rights Reserved