編輯:Android開發實例
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的雙緩沖機制,我理解就是它會緩沖前兩幀的圖像交替傳遞給後面的幀用作覆蓋,這樣由於我們僅在第一幀繪制了背景,第二幀就是無背景狀態了,且通過雙緩沖機制一直保持下來,解決辦法就是改為在前兩幀都進行背景繪制:
現在就沒有問題了(如果換成個不透明的圖形的話就真沒問題了):
現在雖然還是達不到最大幀頻,但是也算不錯啦,在真機上跑的會更快些,接近最大幀頻了。
==============================以下是上文的修正==================================
今天我在審視上篇示例代碼時猛然發現我犯了個低級錯誤,致使幀頻達不到預期,這個錯誤在這裡:
這裡設置每次繪制之後固定休眠33毫秒,以此來限制每秒幀頻在30幀。
但實際上這裡忽視了繪圖及其他運算所消耗的時間,也就是說除非其他所有操作的總耗時都不足1/3毫秒,否則我們就根本無法達到每秒30幀的期望。
修正方法是在每幀開始處理前獲取系統當前時間值,然後在處理完畢後再獲取一次當前時間值,然後用當前值減去舊值,就得到了處理所消耗的時間,然後用33毫秒減去得到的消耗時間,就是我們此幀接下來應當休眠的時間值了:
現在運行一下程序,就能看到跑得很流暢了。
我想可能從事過游戲開發的人都能一眼看出這個錯誤吧,我是沒經驗在這栽了一下>_<~
在繪圖處理時,還有一點需要注意的就是:
鎖定Canvas之後到解鎖這段時間內,最好僅執行與繪圖有關的操作,其他代碼越少越好,就好像往常我們開發時打開數據庫鏈接一樣。
因為我們鎖定後其他線程如果要使用的話就得等待了,所以盡快釋放會節省其他線程的等待時間,要盡量把無關緊要的代碼放在這個時間段以外執行。
針對此例而言,就是那個有關矩陣部分的操作可以調整到前面去執行,Paint對象也可以挪到前面去創建,而且不需要每次循環都創建一個Paint對象,創建一次就足矣:
經過調整之後,綠色高亮部分就只剩下繪圖相關的代碼了,減負了不少呵。
轉自:http://www.cnblogs.com/SkyD/archive/2010/11/08/1871423.html
Android提供了許多方法來控制播放的音頻/視頻文件和流。其中該方法是通過一類稱為MediaPlayer。Android是提供MediaPlayer類訪問內置的媒體播放
分類 notification有以下幾種: 1>普通notification 1.內容標題 2.大圖標 3.內
簡介 在Android中運用了很多機制,例如:廣播、服務、數據庫、通知、包……等等。 什麼是廣播?Broadcast是一種廣泛運用的在應用
登錄應用程序的屏幕,詢問憑據登錄到一些特定的應用。可能需要登錄到Facebook,微博等本章介紹了,如何創建一個登錄界面,以及如何管理安全問題和錯誤嘗試。首先,必須定義兩