顯示具有 Android 標籤的文章。 顯示所有文章
顯示具有 Android 標籤的文章。 顯示所有文章

2012年8月17日 星期五

ANDROID 最新版ADT-17:ADT-17.0.0


  • ·   
    ·  Eclipse中開發android應用必備工具。
    Eclipse編譯IDE環境中,需安裝ADT(Android Development Tools)Plug-in,這是AndroidEclipse上的開發工具。
    目前這個是最新版。

  • 2012年8月6日 星期一

    針對iPhone開發人員的WP7開發指南(上)(下)

    中文手冊,幫助iPhone開發者快速掌握Windows Phone應用開發技巧。
    免費下載,馬上學習,立即開發!

    上册:  針對iPhone開發人員的WP7開發指南(上).doc

    關於此開發指南



    如果您曾開發過 iPhone 應用程式並對開發 Windows Phone 7 應用程式感興趣,那麼對於您來說,本指南很適合。
    本指南將涵蓋掌握 Windows Phone 7 開發技能並利用已掌握的技能開發 iPhone 應用程式所需瞭解的知識。

    新的開始

    10 11 日,。Microsoft 宣佈在來自全球不同製造商的 10 台設備上發佈 Windows Phone 7Windows Phone 7 軟體商店上已有將近 2000 個應用程式。
    對於 Windows Phone 7Microsoft 重新確定了手機用戶的真正需求,從頭開始構建了一個新的手機平臺。OS、使用者體驗和應用程式開發平臺的工程設計都以用戶為中心。Windows Phone 軟體商店中的收入機會,輔以大量的開發工具,使 WP7 成為了開發人員開發應用程式和遊戲的一個極具吸引力的目標平臺。

    開發人員工具

    在九月初,Microsoft 發佈了一組 Windows Phone 7 工具。這個工具集是免費的,可從此處下載。該工具集包含:
    ·  IDE(供開發人員使用):Visual Studio Express for Windows Phone
    ·  使用者介面設計工具(供設計人員使用):Express Blend for Windows Phone
    ·  框架:Silverlight for Windows Phone XNA Game Studio for Windows Phone
    ·  以及測試和調試應用程式的 Windows Phone 7 模擬器。
    這些工具旨在讓你開發使用者應用程式、業務應用程式或遊戲。




    Windows Phone 7 體系結構

    Windows Phone 7 利用了一個如下所示的分層體系結構。與 iPhone OS 相反,WP7 將在多種手機上運行。為了提供一致的用戶體驗和開發人員可信賴的功能,它定義了所有手機都必須滿足的一個極小的硬體規範集。
    它們包括 ARM7 CPU、支援 DirectX GPU、攝像頭和支持多點觸控的顯示幕。標準感測器包括:A-GPS、加速計、羅盤、接近感測器和光感測器。手機上有 3 個標準的物理按鈕 後退、啟動和搜索。在後續章節中將會看到,這些按鈕為使用者提供了一種輕鬆、自然的導航模型。
    WP7 中,Microsoft 提供了大部分設備驅動程式代碼。設備製造商只需編寫特定於其設備的非常少的代碼。這有望改善各種設備的一致性和品質。WP7 通過封裝層(比如 DirectX XNA)來利用硬體加速。

    2012年8月5日 星期日

    【Android遊戲開發之十】(優化處理)詳細剖析Android Traceview 效率檢視工具


    Android遊戲開發之十】(優化處理)詳細剖析Android Traceview 效率檢視工具
    現在需要使用一些方法對現運營的網遊代碼進行精簡和優化,那麼就要使用到Android sdk中提供的一款很好的檢視工具—Android TraceView、下面先給出對此的解釋:然後講解實現的詳細步驟和需要特別注意的一點!

    什麼是TraceView?先看下百度出來的解釋吧:
    Traceview是android平臺配備一個很好的性能分析的工具。它可以通過圖形化的方式讓我們瞭解我們要跟蹤的程式的性能,並且能具體到method。
    關於Traceview的使用
    首先,必須在程式當中加入代碼,以便生成trace檔,有了這個trace檔才可以將其轉化為圖形。
    要添加的代碼如下:
    Java代碼
    // start tracing to "/sdcard/yourActivityTrace.trace"
    Debug.startMethodTracing("yourActivityTrace");
    // ... // stop tracing Debug.stopMethodTracing();
    // start tracing to "/sdcard/yourActivityTrace.trace" Debug.startMethodTracing("yourActivityTrace");
    // ... // stop tracing Debug.stopMethodTracing();
    Google Dev Guide當中說可以在activity的onCreate()中添加Debug.startMethodTracing(), 而在onDestroy()中添加Debug.stopMethodTracing(),但是在實際的測試時發現這種方式其實並不好用,因為通常情況下我們的activity的onDestroy()是由系統決定何時調用的,因此可能等了很長時間都不會得到這個trace檔。因此決定在onStop()中來調用Debug.stopMethodTracing()。這樣當我們切換到其它activity或者點擊home鍵的時候onStop()就會被調用,我們也就可以得到完整的trace file。
    在運行程式之前,首先要保證我們的AVD是一個帶有SD card的AVD,這樣才能使trace檔保存到/sdcard/...當中。運行後可以任意做一些操作,然後點擊home鍵。這是通過DDMS file explore就可以看到/sdcard/目錄下有一個trace檔,現在把這個檔copy到電腦上指定的目錄,假設是C:/tracefile 目錄下。
    可以通過命令列來執行traceview,進入tools目錄後,執行
    traceview C:/tracefile/yourActivityTrace.trace
    之後就可以看到圖形了,接下來就是按照Google Dev Guide中的解釋去分析圖形就OK了。

    下面來看如何實現以及需要注意的地方:
    實現的步驟分為三步:1.必須先在我們的模擬器中創建sdCard ;2.將我們的調試代碼嵌入工程;3.利用TraceView來觀察和分析代碼情況;
    1.對於創建模擬器的sdCard這裡寫出兩種方式:
    第一種:我們在eclipse中創建avd的時候的時候 在選擇api下面有個 Sd Card 的選項,第一項填入創建sdcard的大小即可。


    第二種:cmd 命令! 打開cmd 並且cd 到android sdk tool 路徑下;(或者在環境變數Path中將sdk tool路徑配置上,然後重新打開cmd)
    使用 mksdcard -l mycard 1024M F:/mysdcard.img 創建了一個1G的sdcard;
    使用 emulator -avd my_android -sdcard F:/mysdcard.img 啟動sdcard!
    最後在eclipse Preferences-->Android-->Launch加入 -sdcard F:/mysdcard.img (此步驟就是在第一種創建方式中添加sdcard的支援)
    備註1:
    如果sdcard分配的空間太小,則程式追蹤檔就一直記錄到sd儲蓄卡容量慢為止,所以調試前,要為程式生成一個適當的SD存儲卡也較為重要,因為程式執行時間越長,這個追蹤文件也就越大。


    備註2;
    (如果第二種創建方式中的第二部啟動出現 emulator: ERROR: the user data image is used by another emulator. aborting,請關閉模擬器,或者進入目錄: /Documents and Settings / 用戶 / .android /的AVD / *設備* / (比如我的目錄是:C:/Documents and Settings/Administrator/.android/avd/android2.0.avd)然後刪去以.lock結尾的資料夾就行(我簡單解釋下為什麼要刪除這些檔呢,其實.lock是加鎖,如果程式崩潰等原因導致無法清除這些以.lock結尾的資料夾,就會出現這個問題,也就是這個avd的鎖沒有被釋放,導致avd manager以為這個avd正在使用當中。))

    2.將我們的調試代碼嵌入工程
    正如我們百度到的說明一樣,在程式運行的開端加上 Debug.startMethodTracing("yourActivityTrace"); 然後在onPause()中調用Debug.stopMethodTracing(); 為什麼要將結束寫在onPause()中而不寫在onStop(),那麼如果你去看api的話,你會看到,Api中介紹onPause()會在你返回和點擊home按鍵後觸發,而onStop()一般是由系統來觸發,當該程式處於後臺的時候,而且當記憶體緊張的時候,可能會調用,但是可能永遠不會調用到!
    備註:要記住當把調試代碼加入專案中以後不要立即運行專案,而是必須在AndroidMainfest.xml中定義一條"寫入SD卡的許可權"那麼添加許可權的代碼如下:
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
    因為咱們的調試代碼會在SD卡中生成一個追蹤檔,也就是往SD卡中寫入了資料,所以需要聲明一條許可權。這裡必須注意哦!


    1. <?xml version="1.0" encoding="utf-8"?> 
    2. <manifest xmlns:android="http://schemas.android.com/apk/res/android" 
    3.       package="com.himi" 
    4.       android:versionCode="1" 
    5.       android:versionName="1.0"> 
    6.     <application android:icon="@drawable/icon" android:label="@string/app_name"> 
    7.         <activity android:name=".MainActivity" 
    8.                   android:label="@string/app_name"> 
    9.             <intent-filter> 
    10.                 <action android:name="android.intent.action.MAIN" /> 
    11.                 <category android:name="android.intent.category.LAUNCHER" /> 
    12.             </intent-filter> 
    13.         </activity> 
    14.     </application> 
    15.     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> 
    16.     <uses-sdk android:minSdkVersion="4" /> 
    17. </manifest>  

       3.運行項目並且退出項目從而得到的追蹤檔,利用TraceView來進行分析代碼運行狀況:
    打當正常運行了項目並且點擊返回或者home按鍵就會在 sdcard中生成一個.trace的文件。sdcard 目錄 在eclipse下,點擊:
    windows-show view-other-android-File explorer






    右上角的兩個箭頭,第一個表示從模擬器sdcard匯出檔,第二個表示從PC上導入檔到sdcard中、“—”代表刪除 .....
    然後我們通過cmd來運行生成的追蹤檔 traceview C:/name 追蹤檔所在的路徑放在C盤,放在C盤以外別的盤的話我這裡是無法正常打開traceview的不知道什麼原因。 name 表示生成的.trace檔,cmd的時候不需要輸入“.trace”尾碼 ;然後會出現TraceView的分析視窗;
    【cmd 命令! 打開cmd 並且cd 到android sdk tools 路徑下;(或者在環境變數Path中將sdk tool路徑配置上,然後重新打開cmd)】

    注意1:如果出現一下圖片這種記憶體溢出的問題;

    解決方法:到SDK 下的tools 下 找到 traceview.bat 檔,滑鼠右鍵-編輯(或者記事本打開),最後一行替換成這樣:
    call java -Xms128m -Xmx512m -Djava.ext.dirs=%javaextdirs% -jar %jarpath% %*
    注意2:如果出現路徑不對的問題:
    例如:我的 himi.trace 放在了C盤,那麼我的cmd命令是: traceview c:/himi 然後回車!
    但是這裡要小心,因為 /h 這樣可能被認為是轉義字元!!!為了避免可以盡可能不要使用h,n,r,t,等等成為名字的頭字母,當然還有一種就可以完全避免這種問題,例如還是我的C盤 himi.trace 檔,可以寫cmd命令的時候寫成: traceview c://himi 嘿嘿~要注意細節。
    下面是運行起來的TranceView:



    最右上角表示運行程式總共用了多少時間,從traceview畫面中我們看到有各種顏色,每種顏色代表不同的函數和步驟,那麼同一顏色的區域越大,就代表這個步驟執行時間越長,或者看到下面的統計表,明顯可以看出除了序列 0 1 是系統函數外,2. 3.函數 佔用的時間比較長,那麼序列4是個自訂的函數名為 “hot”這個佔用了幾乎與主執行緒 主draw的時間一樣了,那麼肯定有問題。當然其實這個方法是我故意寫的,就是為了來演示traceview。這個hot函數的代碼如下:
    1. /**
    2. * @author Himi
    3. * @param canvas
    4. */
    5. public void hot(Canvas canvas) {
    6. for (int i = 1; i < 100; i++) {
    7. Bitmap bmp = BitmapFactory.decodeResource(getResources(),
    8. R.drawable.icon);
    9. canvas.drawBitmap(bmp, i += 2, i += 2, paint);
    10. }
    11. }
    很明顯我在故意消耗記憶體和時間。
    那麼,在traceview的右半部統計欄位中:
    Exclusive: 同級函數本身運行的時間
    Inclusive 就是說除統計函數本身運行的時間外再加上調用子函數所運行的時間
    Name:列出的是所有的調用項,前面的數位是編號,展開可以看到有的有Parent 和Children子項,就是指被調用和調用。
    Incl: inclusive時間占總時間的白分比
    Excl: 執行占總時間的白分比。
    Calls+Recur Calls/Total: 調用和重複調用的次數

    Time/Call: 總的時間。(ms)


    所以traceview是個非常好的程式監視工具,可以説明找出程式運行緩慢時的函數,讓我們的代碼不斷完善和改進!

    2012年8月4日 星期六

    【Android遊戲開發之九】(細節處理)觸屏事件中的Bug解決方案以及禁止橫屏和豎屏切換!

    我們先講解在觸屏事件處理中我們需要改進的bug!然後再給出如何禁止橫屏和豎屏切換!以及切換的過程在android os 中是怎樣的。
    先看一段代碼:
    1. @Override  
    2.     public boolean onTouchEvent(MotionEvent event) {  
    3.         Log.v("test", "onTouchEvent");  
    4.         bmp_y++;  
    5.         if (event.getAction() == MotionEvent.ACTION_MOVE) {  
    6.             Log.v("Himi", "ACTION_MOVE");  
    7.         } else if (event.getAction() == MotionEvent.ACTION_DOWN) {  
    8.             Log.v("Himi", "ACTION_DOWN");  
    9.         } else if (event.getAction() == MotionEvent.ACTION_UP) {  
    10.             Log.v("Himi", "ACTION_UP");  
    11.         }  
    12.         return true;  
    13.         //return super.onTouchEvent(event);//备注1  
    14.     }  
    15.     public boolean onKeyDown(int keyCode, KeyEvent event) {  
    16.         Log.v("test", "onKeyDown");  
    17.         bmp_x++;  
    18.         return super.onKeyDown(keyCode, event);  
    19.     } 
        代碼很簡單,一個是處理實體按鍵的回應時間,另一個是觸屏的回應事件、那麼這裡要說的有兩點:
    第一點:
    在surfaceview中我們的onKeyDown 雖然是重寫了view的函數,但是仍然需要在初始化的時候去聲明獲取焦點,setFocusable(true); 如果不調用此方法,那麼會造成按鍵無效。原因是因為如果是自己定義一個繼承自View的類,重新實現onKeyDown方法後,只有當該View獲得焦點時才會調用onKeyDown方法,Actvity中的onKeyDown方法是當所有控制項均沒有處理該按鍵事件時,才會調用.

    第二點:
    也是今天主要需要講得的觸屏回應的函數,onTouchEvent()! 重寫此函數的時候預設最後一句是依照基類的返回方式,return super.onTouchEvent(event); 然後我們在其中去判定 MotionEvent.ACTION_MOVE、MotionEvent.ACTION_DOWN、MotionEvent.ACTION_UP 相對應觸屏操作的 拖動、按下、抬起;對此一切都是正確的,但是真正的的運行起項目的時候發現 Log.v("Himi", "ACTION_MOVE"); 這裡log的"ACTION_MOVE",永遠不會執行!!!為此我找到了解決方法,那麼先解釋下為什麼會出現此類情況。
    解釋:
    onTouchEvent(),預設使用Oeverride這個方法,通常情況下去呼叫super.onTouchEvent()並傳回布林值。但是這裡要注意一點,預設如果去呼叫super.onTouchEvent()則很有可能super裡面並沒做任何事,並且回傳false回來,一旦回傳false回來,很可能後面的event (例如:Action_Move、Action_Up) 都會收不到了,所以為了確保保後面event能順利收到,要注意是否要直接呼super.TouchEvent()。

    例如:
    1. @Override  
    2. public boolean onTouchEvent(MotionEvent event) {  
    3. Log.i("ConanLog", "Event"+event.getAction());  
    4. return super.onTouchEvent(event);  
       這個例子是當你Touch Down的時候會送event進來,接著印出Log,然後呼叫super的onTouchEvent()並回傳佈林值。此時會回傳false,並且之後再也收不到Touch Move或Touch Up的event,為了要確保能收到event,必須要回傳true,所以在這裡要注意一下。
    這個問題也是當時用到此函數的時候發現的,找了很多資料才找到其解釋、所以以後使用onTouchEvent()函數的時候最後的
    return super.onTouchEvent(event);
    一定要改
    return true;

    最後還要注意一點:在初始化的時候不要忘記setFocusableInTouchMode(true);觸屏模式獲取焦點,比較類似 setFocusable(true);
    ——setFocusable(true);//此方法是用來回應按鍵!如果是自己定義一個繼承自View的類,重新實現onKeyDown方法後,只有當該View獲得焦點時才會調用onKeyDown方法,Actvity中的onKeyDown方法是當所有控制項均沒有處理該按鍵事件時,才會調用.

    這裡講下如何禁止橫屏和豎屏切換!
    在某些遊戲中我們可能需要禁止橫屏和豎屏切換,其實實現這個要求很簡單,只要在AndroidManifest.xml 裡面加入這一行 android :screenOrientation="landscape "(landscape 是橫向,portrait 是縱向)。
    在android中每次螢幕的切換動會重啟Activity,所以應該在Activity銷毀前保存當前活動的狀態,在Activity再次Create的時候載入配置。在activity加上android:configChanges="keyboardHidden|orientation"屬性,就不會重啟activity.而是去調用onConfigurationChanged(Configuration newConfig). 這樣就可以在這個方法裡調整顯示方式.
     MainActivity中:
    1. public void onConfigurationChanged(Configuration newConfig) {  
    2.         try {  
    3.             super.onConfigurationChanged(newConfig);  
    4.             if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {  
    5.                 Log.v("Himi", "onConfigurationChanged_ORIENTATION_LANDSCAPE");  
    6.             } else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {  
    7.                 Log.v("Himi", "onConfigurationChanged_ORIENTATION_PORTRAIT");  
    8.             }  
    9.         } catch (Exception ex) {  
    10.         }  
    11.     } 
    AndroidManifest.xml中:
    1. <?xml version="1.0" encoding="utf-8"?> 
    2. <manifest xmlns:android="http://schemas.android.com/apk/res/android" 
    3.     package="com.himi" android:versionCode="1" android:versionName="1.0"> 
    4.     <application android:icon="@drawable/icon" android:label="@string/app_name"> 
    5.         <activity android:name=".MainActivity" android:label="@string/app_name" 
    6.             android:screenOrientation="landscape" android:configChanges="keyboardHidden|orientation"> 
    7.             <intent-filter> 
    8.                 <action android:name="android.intent.action.MAIN" /> 
    9.                 <category android:name="android.intent.category.LAUNCHER" /> 
    10.             </intent-filter> 
    11.         </activity> 
    12.     </application> 
    13.     <uses-sdk android:minSdkVersion="4" /> 
    14. </manifest>  

    2012年8月3日 星期五

    【Android遊戲開發之八】遊戲中添加音訊-詳解MediaPlayer與SoundPool的利弊以及各個在遊戲中的用途!


    遊戲開發中,通過資料和書籍瞭解到在有兩種播放音訊形式可以用在我們的遊戲開發中,第一個:MediaPlayer 類 ;第二個:SoundPool 類!
    PS:當然還有一個JetPlayer 但是 播放的檔案格式比較麻煩,所以這裡拋開不解釋,有興趣的可以去自己研究下、呵呵; 
    運行效果圖:


    MediaPlayer 和:SoundPool 類!那麼他們之間的利弊各是什麼呢?或者說,我們遊戲開發到底用哪一個更佳呢?
    答案就是:兩者都必須要!!!分析利弊與各自的用途後,等各位童鞋熟習每個播放形式實現之後我會詳細道來! 
    下面仍然是先上代碼:(先看代碼 然後我講解兩個播放形式的利弊關係和各個用途以及其中解釋代碼中的幾個備註!) 
    1. package com.himi;  
    2. import java.util.HashMap;  
    3. import android.content.Context;  
    4. import android.graphics.Canvas;  
    5. import android.graphics.Color;  
    6. import android.graphics.Paint;  
    7. import android.media.AudioManager;  
    8. import android.media.MediaPlayer;  
    9. import android.media.SoundPool;  
    10. import android.view.KeyEvent;  
    11. import android.view.MotionEvent;  
    12. import android.view.SurfaceHolder;  
    13. import android.view.SurfaceView;  
    14. import android.view.SurfaceHolder.Callback;  
    15. public class MySurfaceView extends SurfaceView implements Callback, Runnable {  
    16.     private Thread th;  
    17.     private SurfaceHolder sfh;  
    18.     private Canvas canvas;  
    19.     private MediaPlayer player;  
    20.     private Paint paint;  
    21.     private boolean ON = true;  
    22.     private int currentVol, maxVol;  
    23.     private AudioManager am;   
    24.     private HashMap<Integer, Integer> soundPoolMap;//备注1  
    25.     private int loadId;  
    26.     private SoundPool soundPool;  
    27.     public MySurfaceView(Context context) {  
    28.         super(context);  
    29. // 获取音频服务然后强转成一个音频管理器,后面方便用来控制音量大小用  
    30.         am = (AudioManager) MainActivity.instance  
    31.                 .getSystemService(Context.AUDIO_SERVICE);  
    32.         maxVol = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);  
    33.         // 获取最大音量值(15最大! .不是100!)  
    34.         sfh = this.getHolder();  
    35.         sfh.addCallback(this);  
    36.         th = new Thread(this);  
    37.         this.setKeepScreenOn(true);  
    38.         setFocusable(true);  
    39.         paint = new Paint();  
    40.         paint.setAntiAlias(true);  
    41.         //MediaPlayer的初始化  
    42.         player = MediaPlayer.create(context, R.raw.himi);   
    43.         player.setLooping(true);//设置循环播放  
    44.         //SoundPool的初始化  
    45.         soundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 100);  
    46.         soundPoolMap = new HashMap<Integer, Integer>();  
    47.         soundPoolMap.put(1, soundPool.load(MainActivity.content,  
    48.                 R.raw.himi_ogg, 1));  
    49.         loadId = soundPool.load(context, R.raw.himi_ogg, 1);  
    50. //load()方法的最后一个参数他标识优先考虑的声音。目前没有任何效果。使用了也只是对未来的兼容性价值。  
    51.     }  
    52.     public void surfaceCreated(SurfaceHolder holder) {  
    53.         /*  
    54.          * Android OS中,如果你去按手机上的调节音量的按钮,会分两种情况,  
    55.          * 一种是调整手机本身的铃声音量,一种是调整游戏,软件,音乐播放的音量  
    56.          * 当我们在游戏中的时候 ,总是想调整游戏的音量而不是手机的铃声音量,  
    57.          * 可是烦人的问题又来了,我在开发中发现,只有游戏中有声音在播放的时候  
    58.          * ,你才能去调整游戏的音量,否则就是手机的音量,有没有办法让手机只要是  
    59.          * 在运行游戏的状态就只调整游戏的音量呢?试试下面这段代码吧!  
    60.          */  
    61.         MainActivity.instance.setVolumeControlStream(AudioManager.STREAM_MUSIC);  
    62.         // 设定调整音量为媒体音量,当暂停播放的时候调整音量就不会再默认调整铃声音量了,娃哈哈  
    63.           
    64.         player.start();  
    65.         th.start();  
    66.     }  
    67.     public void draw() {  
    68.         canvas = sfh.lockCanvas();  
    69.         canvas.drawColor(Color.WHITE);  
    70.         paint.setColor(Color.RED);  
    71.         canvas.drawText("当前音量: " + currentVol, 100, 40, paint);  
    72.         canvas.drawText("当前播放的时间" + player.getCurrentPosition() + "毫秒", 100,  
    73.                 70, paint);  
    74.         canvas.drawText("方向键中间按钮切换 暂停/开始", 100, 100, paint);  
    75.         canvas.drawText("方向键←键快退5秒 ", 100, 130, paint);  
    76.         canvas.drawText("方向键→键快进5秒 ", 100, 160, paint);  
    77.         canvas.drawText("方向键↑键增加音量 ", 100, 190, paint);  
    78.         canvas.drawText("方向键↓键减小音量", 100, 220, paint);  
    79.         sfh.unlockCanvasAndPost(canvas);  
    80.     }  
    81.     private void logic() {  
    82.         currentVol = am.getStreamVolume(AudioManager.STREAM_MUSIC);// 不断获取当前的音量值  
    83.     }  
    84.     @Override  
    85.     public boolean onKeyDown(int key, KeyEvent event) {  
    86.         if (key == KeyEvent.KEYCODE_DPAD_CENTER) {  
    87.             ON = !ON;  
    88.             if (ON == false)  
    89.                 player.pause();  
    90.             else  
    91.                 player.start();   
    92.         } else if (key == KeyEvent.KEYCODE_DPAD_UP) {// 按键这里本应该是RIGHT,但是因为当前是横屏模式,以下雷同  
    93.             player.seekTo(player.getCurrentPosition() + 5000);  
    94.         } else if (key == KeyEvent.KEYCODE_DPAD_DOWN) {  
    95.             if (player.getCurrentPosition() < 5000) {  
    96.                 player.seekTo(0);  
    97.             } else {  
    98.                 player.seekTo(player.getCurrentPosition() - 5000);  
    99.             }  
    100.         } else if (key == KeyEvent.KEYCODE_DPAD_LEFT) {  
    101.             currentVol += 1;  
    102.             if (currentVol > maxVol) {  
    103.                 currentVol = 100;  
    104.             }  
    105.             am.setStreamVolume(AudioManager.STREAM_MUSIC, currentVol,// 备注2  
    106.                     AudioManager.FLAG_PLAY_SOUND);  
    107.         } else if (key == KeyEvent.KEYCODE_DPAD_RIGHT) {  
    108.             currentVol -= 1;  
    109.             if (currentVol <= 0) {  
    110.                 currentVol = 0;  
    111.             }  
    112.             am.setStreamVolume(AudioManager.STREAM_MUSIC, currentVol,  
    113.                     AudioManager.FLAG_PLAY_SOUND);  
    114.         }  
    115.         soundPool.play(loadId, currentVol, currentVol, 1, 0, 1f);// 备注3  
    116. //      soundPool.play(soundPoolMap.get(1), currentVol, currentVol, 1, 0, 1f);//备注4  
    117. //      soundPool.pause(1);//暂停SoundPool的声音   
    118.         return super.onKeyDown(key, event);  
    119.     }   
    120.     @Override  
    121.     public boolean onTouchEvent(MotionEvent event) {  
    122.         return true;  
    123.     }   
    124.     public void run() {  
    125.         // TODO Auto-generated method stub  
    126.         while (true) {  
    127.             draw();  
    128.             logic();  
    129.             try {  
    130.                 Thread.sleep(100);  
    131.             } catch (Exception ex) {  
    132.             }  
    133.         }  
    134.     }   
    135.     public void surfaceChanged(SurfaceHolder holder, int format, int width,  
    136.             int height) {    
    137.     }   
    138.     public void surfaceDestroyed(SurfaceHolder holder) {    
    139.     }   
    140. }  
    一、 MediaPlayer 播放音訊的實現步驟:
    1. 調用MediaPlayer.create(context, R.raw.himi); 利用MediaPlayer類調用create方法並且傳入通過id索引的資源音訊檔,得到實例;
    2. 得到的實例就可以調用 MediaPlayer.star();
    簡單吧、其實MediaPlayer還有幾個構造方法,大家有興趣可以去嘗試和實現,這裡主要是簡單的向大家介紹基本的,畢竟簡單實用最好!

    二、 SoundPlayer 播放音訊的實現步驟:
    1. new出一個實例 ; new SoundPool(4, AudioManager.STREAM_MUSIC, 100);第一個參數是允許有多少個聲音流同時播放,2個參數是聲音類型,第三個參數是聲音的品質;
    2.loadId = soundPool.load(context, R.raw.himi_ogg, 1);
    3. 使用實例調用play方法傳入對應的音訊檔id即可! 
    下面講下兩個播放形式的利弊: 
    使用MediaPlayer來播放音訊檔存在一些不足:
    例如:資源占用量較高、延遲時間較長、不支援多個音訊同時播放等。
    這些缺點決定了MediaPlayer在某些場合的使用情況不會很理想,例如在對時間精准度要求相對較高的遊戲開發中。
    最開始我使用的也是普通的MediaPlayer的方式,但這個方法不適合用於遊戲開發,因為遊戲裡面同時播放多個音效是常有的事,用過MediaPlayer的朋友都該知道,它是不支持即時播放多個聲音的,會出現或多或少的延遲,而且這個延遲是無法讓人忍受的,尤其是在快速連續播放聲音(比如連續猛點按鈕),會非常明顯,長的時候會出現3~5秒的延遲,【使用MediaPlayer.seekTo() 這個方法來解決此問題】;

    相對於使用SoundPool存在的一些問題:
    1. SoundPool最大只能申請1M的記憶體空間,這就意味著我們只能使用一些很短的聲音片段,而不是用它來播放歌曲或者遊戲背景音樂(背景音樂可以考慮使用JetPlayer來播放)。
    2. SoundPool提供了pausestop方法,但這些方法建議最好不要輕易使用,因為有些時候它們可能會使你的程式莫名其妙的終止。還有些朋友反映它們不會立即中止播放聲音,而是把緩衝區裡的資料播放完才會停下來,也許會多播放一秒鐘。
    3. 音訊格式建議使用OGG格式。使用WAV格式的音訊檔存放遊戲音效,經過反復測試,在音效播放間隔較短的情況下會出現異常關閉的情況(有說法是SoundPool目前只對16bitWAV檔有較好的支援)。後來將文件轉成OGG格式,問題得到了解決。
    4.在使用SoundPool播放音訊的時候,如果在初始化中就調用播放函數進行播放音樂那麼根本沒有聲音,不是因為沒有執行,而是SoundPool需要一準備時間!囧。當然這個準備時間也很短,不會影響使用,只是程式一運行就播放會沒有聲音罷了,所以我把SoundPool播放寫在了按鍵中處理了、備註4的地方

    大概看完了利弊解釋,那麼來看我的代碼備註的地方:
    備註1
    這裡我定義了一個 HashMap ,這個是雜湊表,如果大家不是很瞭解這個類,那建議百度 google學習下,它與Hashtable很常用的,它倆的主要區別是: HashMap 不同步、空鍵值、效率高; Hashtable 同步、非空鍵值、效率略低 ;而在J2ME中不支持HashMap ,因為me中不支持空鍵值,所以在me中只能使用hashtable、咳咳、言歸正傳,我這裡使用hashmap主要是為了存入多個音訊的ID,播放的時候可以同時播放多個音訊。
    上面也介紹了,SoundPool可以支援多個音訊同時播放,而且SoundPool在播放的時候調用的這個方法(備註3)soundPool.play(loadId, currentVol, currentVol, 1, 0, 1f); 第一個參數指的就是之前的loadId !是通過 soundPool.load(context, R.raw.himi_ogg, 1);方法取出來的,
    那麼除此之外還要注意一點的就是定義hashmap的時候一定要定義成這種形式HashMap<Integer, Integer> hm = new Hash<Integer, Integer>,聲明此雜湊表就是一個keyvolue值都是Integer的雜湊表! 為什麼要這麼做,因為如果你只是簡單的定義成 HashMap hm =new HashMap(),那麼當你在播放的時候,也就是備註4方法這裡的第一個id參數使用Hashmap.get()這個方法的時候總會出現錯誤的提示! 
    SoundPool最大只能申請1M的記憶體空間,這就意味著我們只能使用一些很短的聲音片段》為什麼只能使用一些很短的聲音呢?
    大家還是看備註4方法的第一個參數,這裡要求傳入的Id類型是個int值,那麼這個int其實對應的是通過load()方法返回的音訊id,而且這個id會因音訊檔的大小而變大變小,那麼一旦我們的音訊檔超過int最大值,那麼就會報記憶體錯誤的異常。所以為什麼用SoundPool只能播放一些簡短的音訊這就是其原因了。當然os 裡為什麼這麼定義 我也無從查證和說明。
    備註4 :此方法中參數的解釋
    第一個參數是我通過SoundPool.load()方法返回的音訊對應id,第二個第三個參數表示左右聲道大小,第四個參數是優先順序,第五個參數是迴圈次數,最後一個是播放速率(1.0 =正常播放,範圍是0.52.0

    備註2:
    這裡是通過媒體服務得到一個音訊管理器,從而來對音量大小進行調整。這裡要強調一下,調整音訊是用這個音訊管理器調用setStreamVolume()的方式去調整,而不是MediaPlayer.setVolue(int LeftVolume,int RightVolume);這個方法的兩個參數也是調正左右聲道而不是調節聲音大小。 
    好了,對此我們對遊戲開發中到底需要用什麼來做進行了分析,總結就是SoundPool適合做特效聲,其實播放背景音樂我感覺還是用MediaPlayer比較好,當然啦,用什麼都看大家喜好和選擇啦!下麵附上項目下載位址:(專案10+MB因為含有res音訊檔
    有人問 怎麼才知道一首歌曲播放完了,那麼這裡給說下: 
    PlaybackCompleted狀態:檔正常播放完畢,而又沒有設置迴圈播放的話就進入該狀態,並會觸發OnCompletionListeneronCompletion()方法。此時可以調用start()方法重新從頭播放檔,也可以stop()停止MediaPlayer,或者也可以seekTo()來重新定位播放位置。
    注意:1 別忘記綁定操作! mp.setOnCompletionListener(this);
    2、如果你設置了迴圈播放 mp.setLooping(true); 的話,那麼永遠都不會監聽到播放完成的狀態!!!!這裡一定要注意!

    【Android遊戲開發之七】(遊戲開發中需要的樣式)再次剖析遊戲開發中對SurfaceView中添加元件方案!


    各位童鞋請你們注意:surfaceview中確實有 onDraw這個方法,但是surfaceview不會自己去調用!!!
    而我代碼中的ondraw 也好 draw 也好,都是我自己定義的一個方法。。。放在執行緒中不斷調用的,一定要注意!! 
    上一篇我們已經可以在同一介面中既顯示我們的surfaceview和button、textview等組件,那麼基本算是成功了,但是身為遊戲開發人員,如果不是想故意要這種類似電影形式的展現方式(我們的surfaceview在中間 - -.很想播放電影似的。。),是絕對不允許遊戲的分量只是占了中間那麼一部分,肯定需要全屏顯示,別的元件只是一個配角的角色。那麼下面先上一張截圖看下為什麼修改與調整。


    看到我們畫出來的字體了吧,很悲劇被覆蓋了!只要有button就會有一塊長條,即使我們修改button中佈局的顏色也只是把長條的顏色變成白色,當然好看是好看了,但是仍舊遮擋我們的字體!這可不是我們想要的結果。我們想要的效果應該是下圖這樣的:


    娃哈哈,這效果就對啦,我們的view占滿全屏,而組件本身才會對我們的view中的內容有遮擋,不會多出一些無用的長條遮擋.... 
    當時雖然想的方法就是佈局xml的問題,我一開始想在我們xml中定義的surfaceview中直接添加按鈕,但是view不能添加view!所以沒辦法,就想到是否是佈局的問題。經過多次嘗試才終於成功做到。
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3. android:orientation="vertical"
    4. android:layout_width="fill_parent"
    5. android:layout_height="fill_parent"
    6. >
    7. <RelativeLayout
    8. android:layout_width="fill_parent"
    9. android:layout_height="wrap_content"
    10. android:layout_weight="1" >
    11. <com.himi.MySurfaceView android:id="@+id/view3d"
    12. android:layout_width="fill_parent"
    13. android:layout_height="fill_parent"/>
    14. <Button
    15. android:layout_width="wrap_content"
    16. android:layout_height="wrap_content"
    17. android:layout_alignParentBottom="true"
    18. android:text="Himi Button_1"
    19. android:id="@+id/button1"/>
    20. <Button android:layout_width="wrap_content"
    21. android:layout_height="wrap_content"
    22. android:layout_alignParentBottom="true"
    23. android:layout_toRightOf="@id/button1"
    24. android:text="Himi Button_2"
    25. android:id="@+id/button2"/>
    26. <TextView
    27. android:id="@+id/textview"
    28. android:layout_width="fill_parent"
    29. android:layout_height="fill_parent"
    30. android:text="This is Himi"
    31. android:textSize="32sp"
    32. android:textColor="#00FF00"
    33. android:gravity="center_horizontal"/>
    34. </RelativeLayout>
    35. </LinearLayout>
      xml 修改的不大,主要將之前的線性佈局改成了相對佈局。雖然改動不大,但是也真的費了不少時間去調整、這樣一來大家就可以在自己的遊戲Surfaceview中隨意添加元件啦,娃哈哈~~~
    源碼在上一篇已經給出下載位址,這裡也只是對xml的修改大家需要可以去下載上一篇的源碼,將xml調整即可

    2012年7月6日 星期五

    【Android遊戲開發之一】設置全屏以及繪畫簡單的圖形



    直接上代碼:
    1. package com.himi;
    2. import android.app.Activity;
    3. import android.os.Bundle;
    4. import android.view.Window;
    5. import android.view.WindowManager;
    6. public class MainActivity extends Activity {
    7. /** Called when the activity is first created. */
    8. @Override
    9. public void onCreate(Bundle savedInstanceState) {
    10. super.onCreate(savedInstanceState);
    11. this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
    12. //隱去電池等圖示和一切修飾部分(狀態列部分)
    13. this.requestWindowFeature(Window.FEATURE_NO_TITLE);
    14. // 隱去標題列(程式的名字)
    15. setContentView(new MyView(this));
    16. }
    17. }
    注意 隱去標題(應用的名字) 此設定必須要寫在setContentView之前,否則會有異常!
    對於設置全屏,主要就兩點:
    第一點是設置隱去狀態列部分,包括電池等圖示,第二點無疑就是把我們應用的名字也隱去不顯示,這樣一來就全屏了。
    1. package com.himi;
    2. import android.content.Context;
    3. import android.graphics.Canvas;
    4. import android.graphics.Color;
    5. import android.graphics.Paint;
    6. import android.graphics.Rect;
    7. import android.graphics.RectF;
    8. import android.view.View;
    9. public class MyView extends View {
    10. private Paint paint ;
    11. public MyView(Context context) {
    12. super(context);
    13. paint = new Paint();
    14. paint.setAntiAlias(true);//設置畫筆無鋸齒(如果不設置可以看到效果很差)
    15. this.setKeepScreenOn(true);//設置背景常亮
    16. paint.setColor(Color.RED);
    17. }
    18. @Override
    19. public void onDraw(Canvas canvas) {
    20. canvas.drawColor(Color.WHITE);//設置刷屏顏色
    21. Rect rect = new Rect(30,30,50,50); //這裡最後兩個參數不是寬高、而是矩形右下角的座標
    22. canvas.drawRect(rect, paint);
    23. RectF rectF = new RectF(70f,30f,90f,90f);//RectF 只是矩形 float形式 只是跟Rect精確度不一樣
    24. canvas.drawArc(rectF, 0, 360, true, paint);
    25. canvas.drawCircle(150, 30, 20, paint);//這也是畫圓 第三個參數為半徑
    26. float[] points =new float[]{200f,10f,200f,40f,300f,30f,400f,70f};
    27. canvas.drawLines(points, paint);
    28. // canvas.drawLines(points, 1, 4, paint);//選取特定點陣列中兩點來畫出一條直線
    29. canvas.drawText("Himi", 230, 30, paint);
    30. }
    31. }
    設置橫豎屏也可以在AndroidManifest.xml中定義:
    android:theme="@android:style/Theme.NoTitleBar" 隱去標題列
    android:theme="@android:style/Theme.NoTitleBar.Fullscreen" 隱去狀態列