【Android游戏开发十七】让玩家自定义手势玩转Android游戏!—Android Gesture之【输入法手势技术】...
為什么80%的碼農(nóng)都做不了架構(gòu)師?>>> ??
?李華明Himi?原創(chuàng),轉(zhuǎn)載務(wù)必在明顯處注明:轉(zhuǎn)載自 【黑米GameDev街區(qū)】?原文鏈接:? http://www.himigame.com/android-game/340.html
?
很多童鞋說我的代碼運行后,點擊home或者back后會程序異常,如果你也這樣遇到過,那么你肯定沒有仔細(xì)讀完Himi的博文,第十九篇Himi專門寫了關(guān)于這些錯誤的原因和解決方法,這里我在博客都補充說明下,省的童鞋們總疑惑這一塊;請點擊下面聯(lián)系進入閱讀:
【Android游戲開發(fā)十九】(必看篇)SurfaceView運行機制詳解—剖析Back與Home按鍵及切入后臺等異常處理!
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 有童鞋問我為什么不用SDK2.1 ,2.2來進行游戲開發(fā),那我這里稍微說兩句:
1.Android SDK 屬于向下兼容!那么低版本可以運行的,高版本基本上更是沒問題!(當(dāng)然每次SDK的更新也會帶來新功能,或者修改了一些原來的BUG等等,那么其實對于游戲開發(fā)來說,如果你的游戲中不需要更高的SDK版本的支持情況下,完全不必去追求最新的SDK!)
2.使用低版本進行游戲開發(fā)這樣能兼顧更多的機型,獲取更多的用戶!
3.大家都知道Android SDK 每次版本的更新,底層代碼也會更健壯和優(yōu)化了!比如我們公司的網(wǎng)游Android版在G2(SDK1.5)上跑起來稍微有些卡,而在我的手機上(SDK2.2)運行起來流暢的沒說的~各種舒坦~~但是這樣也會帶來一些弊端,比如我們自己游戲如果上來就用高版本SDK進行開發(fā),那么對于性能、內(nèi)存上到底如何,我們都不會很容易的看出其效果,如果我們用低版本的SDK則會讓我們明顯的感受到性能到底如何~你想想如果你的游戲在1.5 ,1.6上跑起來很流暢,那放在更高版本的SDK機器上更是沒說的啦~
?? ? ? ? ? ? ? ? ? ?總結(jié):游戲開發(fā)中,如果你游戲不需要更高的API的支持,那么推薦基于SDK 1.5和1.6來開發(fā)!
?
?? ? ? ? 在上一篇中我給大家介紹了觸摸屏手勢操作,但是這種觸屏手勢的操作比較有局限性;比如我們都知道Android可以利用手勢來解鎖,比如九宮格形式的,通過自定義的一個單筆畫手勢可以解開屏幕鎖,還可以自定義筆畫手勢來啟動一個應(yīng)用等,那么這種所謂的筆畫手勢其實就是今天我要給大家講解的輸入法手勢識別技術(shù)!這種手勢是我們可以自己來自定義,而不像之前的觸屏手勢操作只是利用Android 對一些觸屏動作的封裝罷了。下面上幾張手機自定義筆劃手勢解鎖的的截圖:
?
?? ? ? ? ?
?? ? ? 左圖中最后一個是自定義解鎖的輸入法手勢~
?
OK,那么既然利用手勢既然能進行解鎖等操作,那么我們游戲開發(fā)中,更是可以加入這一亮點了,比如在游戲中我畫個圓形執(zhí)行換背景操作,畫個X表示退出游戲等等,等等、哈哈 是不是感覺很有意思了?好的,下面就開始進入講解!
?
首先本篇主要學(xué)習(xí)兩點:
?? ? 1. 如何創(chuàng)建輸入法手勢、刪除輸入法手勢、從SD卡中讀取出手勢文件!
?? ? 2.當(dāng)輸入法手勢創(chuàng)建后,如何來匹配出我們的自定義手勢!
?
下面我們來熟習(xí)兩個類和幾個概念:
?
1. 什么是?GestureOverlayView ???簡單點說其實就是一個手寫繪圖區(qū);
?
2. 什么是?GestureLibrary ????這個類是對手勢進行保存、刪除等操作的,一個存放手勢的小倉庫!
?
3. 筆劃是什么,字體筆畫? ?是的,其實就是跟我們寫字的筆劃一個概念!
?
4.什么是筆劃類型????輸入法手勢操作中,筆劃類型有兩種;一種是:單一筆劃,另外一種是:多筆劃
?? ?所謂單一筆劃筆劃就是一筆劃畫出一個手勢,從你手指接觸屏幕開始到你離開屏幕筆畫就會立刻形成一個手勢!一氣呵成!
?? ?而多筆劃則是可以在一定緊湊時間內(nèi)隨意幾筆劃都可!然后超過這個緊湊時間后便會形成一個手勢!
?
先出項目截圖,簡單說下其功能和操作:
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 【圖1】 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?【圖2】
?? ? ? ? ? ? ? ? ? ? ? ?
?
圖1界面中分為3塊,從上到下依次是:TextView ,EditText,SurfaceView;然后在SurfaceView后面還有一個覆蓋全屏的GestureOverlayView!
圖2界面是在創(chuàng)建好的手勢中匹配手勢的界面,這里很清晰看出來,找的很對 ~嘿嘿~
?
先看下main.xml:
?
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="fill_parent"android:layout_height="fill_parent"><TextView android:id="@+id/himi_tv" android:layout_width="fill_parent"android:layout_height="wrap_content" android:text="@string/hello"android:textSize="15sp" android:textColor="#FFFFFF00" /><EditText android:id="@+id/himi_edit" android:layout_width="fill_parent"android:layout_height="wrap_content" /><RelativeLayout android:layout_width="fill_parent"android:layout_height="wrap_content" android:layout_weight="1"><com.himi.MySurfaceView android:id="@+id/view3d"android:layout_width="fill_parent" android:layout_height="fill_parent" /><android.gesture.GestureOverlayViewandroid:id="@+id/himi_gesture" android:layout_width="fill_parent"android:layout_height="fill_parent" android:layout_weight="1.0"/></RelativeLayout> </LinearLayout>
?
xml中注冊的有我們自定義的surfaceview,對此不太熟悉可以去看下【Android2D開發(fā)之六】,不多解釋了。關(guān)于GestureOverlayView這里也只是簡單的定義了寬高,還有一些重要的屬性設(shè)置在代碼中設(shè)置了,當(dāng)然xml也可以設(shè)置的;
?
下面看MainActivity.java
?
***@author Himi*@輸入法手勢識別*@注意: android.gesture這個類在api-4(SDK1.6)才開始支持的!*@提醒:默認(rèn)存到SD卡中,所以別忘記在AndroidMainfest.xml加上SD卡讀寫權(quán)限!*/ public class MainActivity extends Activity {private GestureOverlayView gov;// 創(chuàng)建一個手寫繪圖區(qū)private Gesture gesture;// 手寫實例private GestureLibrary gestureLib;//創(chuàng)建一個手勢倉庫private TextView tv;private EditText et;private String path;//手勢文件路徑private File file;//@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);this.requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.main);tv = (TextView) findViewById(R.id.himi_tv);et = (EditText) findViewById(R.id.himi_edit);gov = (GestureOverlayView) findViewById(R.id.himi_gesture);gov.setGestureStrokeType(GestureOverlayView.GESTURE_STROKE_TYPE_MULTIPLE);//設(shè)置筆劃類型 // GestureOverlayView.GESTURE_STROKE_TYPE_MULTIPLE 設(shè)置支持多筆劃// GestureOverlayView.GESTURE_STROKE_TYPE_SINGLE 僅支持單一筆劃path = new File(Environment.getExternalStorageDirectory(), "gestures").getAbsolutePath();//得到默認(rèn)路徑和文件名/sdcard/gesturesfile = new File(path);//實例gestures的文件對象gestureLib = GestureLibraries.fromFile(path);//實例手勢倉庫gov.addOnGestureListener(new OnGestureListener() { // 這里是綁定手寫繪圖區(qū)@Override// 以下方法是你剛開始畫手勢的時候觸發(fā)public void onGestureStarted(GestureOverlayView overlay, MotionEvent event) {tv.setText("請您在緊湊的時間內(nèi)用兩筆劃來完成一個手勢!西西~");} @Override// 以下方法是當(dāng)手勢完整形成的時候觸發(fā)public void onGestureEnded(GestureOverlayView overlay, MotionEvent event) {gesture = overlay.getGesture();// 從繪圖區(qū)取出形成的手勢if (gesture.getStrokesCount() == 2) {//我判定當(dāng)用戶用了兩筆劃//(強調(diào):如果一開始設(shè)置手勢筆畫類型是單一筆畫,那你這里始終得到的只是1!)if (event.getAction() == MotionEvent.ACTION_UP) {//判定第兩筆劃離開屏幕//if(gesture.getLength()==100){}//這里是判定長度達(dá)到100像素if (et.getText().toString().equals("")) {tv.setText("由于您沒有輸入手勢名稱,so~保存失敗啦~");} else {tv.setText("正在保存手勢...");addGesture(et.getText().toString(), gesture);//我自己寫的添加手勢函數(shù) }}} else {tv.setText("請您在緊湊的時間內(nèi)用兩筆劃來完成一個手勢!西西~");}} @Overridepublic void onGestureCancelled(GestureOverlayView overlay, MotionEvent event) {} @Overridepublic void onGesture(GestureOverlayView overlay, MotionEvent event) {}});//----這里是在程序啟動的時候進行遍歷所有手勢!------if (!gestureLib.load()) {tv.setText("Himi提示:手勢超過9個我做了刪除所有手勢的操作,為了界面整潔一些!"+ " 輸入法手勢練習(xí)~(*^__^*)~ 嘻嘻!/n操作介紹:(畫手勢我設(shè)置必須畫兩筆劃才行哦~)/n1." +"添加手勢:先EditText中輸入名稱,然后在屏幕上畫出手勢!/n2.匹配手勢:" + "在EditText輸入/"himi/",然后輸入手勢即可! ");} else {Set<String> set = gestureLib.getGestureEntries();//取出所有手勢Object ob[] = set.toArray();loadAllGesture(set, ob);}} }??
這個就是MainActivity主要代碼了,其中添加手勢、匹配手勢、遍歷手勢、將手勢轉(zhuǎn)成圖片這些我都單獨寫成了函數(shù),這樣讓各位童鞋更清晰思路一些。
?
從以上代碼中我們看出在創(chuàng)建手勢之前,手寫繪圖區(qū)(GestureOverlayView)肯定先被創(chuàng)建出來,然后我們就可以在其區(qū)域中進行筆劃繪畫手勢了,當(dāng)然繪畫手勢前,我們也需要設(shè)置了筆劃類型,也就是我一開始給大家介紹的~其后最重要的就是手寫繪圖區(qū)的手勢監(jiān)聽器綁定,增加OnGestureListener這個監(jiān)聽器重寫了四個函數(shù),這里最重要的就兩個函數(shù):
?? ? ?onGestureStarted ? 和 ?onGestureEnded ?; 手勢開始和手勢結(jié)束的監(jiān)聽函數(shù)!
尤其是手勢結(jié)束監(jiān)聽這個函數(shù)尤為重要,在其中我設(shè)置好幾個條件語句,這么幾個條件一方面是讓大家了解Gesture中一些比較重要常用的方法,另一方面我要提醒各位童鞋:
如果你設(shè)置筆劃類型是多筆劃類型的,那么理想狀態(tài)下,應(yīng)該是在一段緊湊時間內(nèi),不管你使用了幾筆劃來繪制手勢,系統(tǒng)都應(yīng)該在判定你在一定短暫時間內(nèi)沒有再進行筆劃的時候才應(yīng)該創(chuàng)建手勢,并且系統(tǒng)響應(yīng)此函數(shù);
?? ? ?其實錯了,一開始我也這么想,但是發(fā)現(xiàn),不管你設(shè)置的筆劃類型是單一的還是多筆劃當(dāng)你手指離開屏幕,不管你當(dāng)前是第幾筆,Android都會去響應(yīng)這個完成函數(shù),so~ 我在這里調(diào)用手勢Gesture類中的getStrokesCount()函數(shù),這個函數(shù)會記錄在緊湊時間內(nèi)你繪制手勢的筆劃數(shù),那么根據(jù)這個函數(shù)我們就可以解決手指離開屏幕總被響應(yīng)的問題了,因為單一筆劃類型永遠(yuǎn)這個值不會大于1!
而 if (event.getAction() == MotionEvent.ACTION_UP) {}寫這個只是給大家演示第二個參數(shù)按鍵動作該怎么用;
?
那么我們下面就來看如何創(chuàng)建一個手勢:
?
public void addMyGesture(String name, Gesture gesture) { try {if (name.equals("himi")) {findGesture(gesture);} else {// 關(guān)于兩種方式創(chuàng)建模擬器的SDcard在【Android2D游戲開發(fā)之十】有詳解if (Environment.getExternalStorageState() != null) {// 這個方法在試探終端是否有sdcard!if (!file.exists()) {// 判定是否已經(jīng)存在手勢文件// 不存在文件的時候我們?nèi)ブ苯影盐覀兊氖謩菸募嫒雊estureLib.addGesture(name, gesture);if (gestureLib.save()) {保存到文件中g(shù)ov.clear(true);//清除筆畫// 注意保存的路徑默認(rèn)是/sdcard/gesture ,so~別忘記AndroidMainfest.xml加上讀寫權(quán)限!// 這里抱怨一下,咳咳、其實昨天就應(yīng)該出這篇博文的,就是因為這里總是異常,今天仔細(xì)看了// 才發(fā)現(xiàn)不是沒寫權(quán)限,而是我雖然在AndroidMainfest.xml中寫了權(quán)限,但是寫錯了位置..哭死!tv.setText("保存手勢成功!因為不存在手勢文件," + "所以第一次保存手勢成功會默認(rèn)先創(chuàng)" +"建了一個手勢文件!然后將手勢保存到文件中.");et.setText("");gestureToImage(gesture);} else {tv.setText("保存手勢失敗!");}} else {//當(dāng)存在此文件的時候我們需要先刪除此手勢然后把新的手勢放上//讀取已經(jīng)存在的文件,得到文件中的所有手勢if (!gestureLib.load()) {//如果讀取失敗tv.setText("手勢文件讀取失敗!");} else {//讀取成功 Set<String> set = gestureLib.getGestureEntries();//取出所有手勢Object ob[] = set.toArray();boolean isHavedGesture = false;for (int i = 0; i < ob.length; i++) {//這里是遍歷所有手勢的name if (((String) ob[i]).equals(name)) {//和我們新添的手勢name做對比isHavedGesture = true;}}if (isHavedGesture) {//如果此變量為true說明有相同name的手勢 //----備注1-------------------//gestureLib.removeGesture(name, gesture);//刪除與當(dāng)前名字相同的手勢 /*----備注2-----------------*/gestureLib.removeEntry(name);gestureLib.addGesture(name, gesture);} else {gestureLib.addGesture(name, gesture);}if (gestureLib.save()) {gov.clear(true);//清除筆畫 gestureToImage(gesture);tv.setText("保存手勢成功!當(dāng)前所有手勢一共有:" + ob.length + "個");et.setText("");} else {tv.setText("保存手勢失敗!");}------- --以下代碼是當(dāng)手勢超過9個就全部清空 操作--------if (ob.length > 9) {for (int i = 0; i < ob.length; i++) {//這里是遍歷刪除手勢gestureLib.removeEntry((String) ob[i]);}gestureLib.save();if (MySurfaceView.vec_bmp != null) {MySurfaceView.vec_bmp.removeAllElements();//刪除放置手勢圖的容器}tv.setText("手勢超過9個,已全部清空!");et.setText("");}ob = null;set = null;}}} else {tv.setText("當(dāng)前模擬器沒有SD卡 - -。");}}} catch (Exception e) {tv.setText("操作異常!");}} ?
這里也都很好理解,套路類似之前File文件存儲的套路,先判斷SD是否存在,然后是文件是否存在:
如果文件不存在就先直接添加到手勢到手勢倉庫中,然后手勢倉調(diào)用gestureLib.save()才算把手勢存到SD卡的手勢文件中。?
文件存在的話還要去判定是否文件中包含了相同名字的手勢;當(dāng)然這里可以不判定是否有相同手勢名存在,然后進行刪除操作!其實也可不刪除,直接添加進去當(dāng)前新建的手勢;原因看了下面的備注解釋就明白了;
?
?
備注 1:因為gestureLib保存的手勢是個HashMap, key=手勢的名字,value=手勢,所以gestureLib.removeGesture(name, gesture);這種刪除方式只是刪除了手勢,該手勢名字依舊保存在hashmap中,下次還有相同的name手勢存入的時候Hashmap就直接覆蓋本條目了。所以根據(jù)Hashmap的特征,我們可以不進行刪除操作,可以直接gestureLib.addGesture(name, gesture);因為如果出現(xiàn)相同的手勢名字的手勢,Hashmap就會根據(jù)key(手勢的名字)直接覆蓋其條目的value(手勢)滴~
備注2 :這里也是一種刪除手勢的方式,但是這種方式跟備注1的不同,這里是將Hashmap中的條目刪除,也就是說key和value都被刪去!
?
下面看下如何把手勢轉(zhuǎn)成bitmap!
public void gestureToImage(Gesture ges) {//將手勢轉(zhuǎn)換成Bitmap//把手勢轉(zhuǎn)成圖片,存到我們SurfaceView中定義的Image容器中,然后都畫出來~if (MySurfaceView.vec_bmp != null) {MySurfaceView.vec_bmp.addElement(ges.toBitmap(100, 100, 12, Color.GREEN));}}??
?
下面是如何遍歷手勢!
public void loadAllGesture(Set<String> set, Object ob[]) { //遍歷所有的手勢 if (gestureLib.load()) {//讀取最新的手勢文件set = gestureLib.getGestureEntries();//取出所有手勢ob = set.toArray();for (int i = 0; i < ob.length; i++) {//把手勢轉(zhuǎn)成BitmapgestureToImage(gestureLib.getGestures((String) ob[i]).get(0));//這里是把我們每個手勢的名字也保存下來MySurfaceView.vec_string.addElement((String) ob[i]);}}}??
下面最后來看看手勢的匹配!(超重要的!自己也搞了許久才找到解決的方法)
?
public void findGesture(Gesture gesture) {try {// 關(guān)于兩種方式創(chuàng)建模擬器的SDcard在【Android2D游戲開發(fā)之十】有詳解if (Environment.getExternalStorageState() != null) {// 這個方法在試探終端是否有sdcard!if (!file.exists()) {// 判定是否已經(jīng)存在手勢文件tv.setText("匹配手勢失敗,因為手勢文件不存在!!");} else {//當(dāng)存在此文件的時候我們需要先刪除此手勢然后把新的手勢放上//讀取已經(jīng)存在的文件,得到文件中的所有手勢if (!gestureLib.load()) {//如果讀取失敗tv.setText("匹配手勢失敗,手勢文件讀取失敗!");} else {//讀取成功 List<Prediction> predictions = gestureLib.recognize(gesture);//recognize()的返回結(jié)果是一個prediction集合,//包含了所有與gesture相匹配的結(jié)果。//從手勢庫中查詢匹配的內(nèi)容,匹配的結(jié)果可能包括多個相似的結(jié)果, if (!predictions.isEmpty()) {Prediction prediction = predictions.get(0);//prediction的score屬性代表了與手勢的相似程度//prediction的name代表手勢對應(yīng)的名稱 //prediction的score屬性代表了與gesture得相似程度(通常情況下不考慮score小于1的結(jié)果)。 if (prediction.score >= 1) {tv.setText("當(dāng)前你的手勢在手勢庫中找到最相似的手勢:name =" + prediction.name);}}}}} else {tv.setText("匹配手勢失敗,,當(dāng)前模擬器沒有SD卡 - -。");}} catch (Exception e) {e.printStackTrace();tv.setText("由于出現(xiàn)異常,匹配手勢失敗啦~");}}??
?
那么最后給各位童鞋說一下,其實輸入法手勢操作很是適合游戲中使用,不管是觸摸屏手勢操作還是今天講的輸入法手勢操作如果加到游戲中那都是相當(dāng)贊的!但是我們公司網(wǎng)游引擎和框架不適合插入手勢 - -、唉~
其實前兩天應(yīng)該發(fā)這篇的,但是因為工作忙了幾天,讓大家久等了,挺不好意思的,所以今天熬夜給大家寫了出來,現(xiàn)在都凌晨 7:00 了~
?
源碼下載地址:?http://www.himigame.com/android-game/340.html
?
?
?
.
原文鏈接: http://blog.csdn.net/xiaominghimi/article/details/6137136
轉(zhuǎn)載于:https://my.oschina.net/liux/blog/49879
總結(jié)
以上是生活随笔為你收集整理的【Android游戏开发十七】让玩家自定义手势玩转Android游戏!—Android Gesture之【输入法手势技术】...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: APUE读书笔记-04文件和目录(1)
- 下一篇: 构建高性能ASP.NET站点 第六章—性