Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 自定義手勢--輸入法手勢技術

Android 自定義手勢--輸入法手勢技術

編輯:關於Android編程

進行軟件開發時,通常我們都喜歡使用較新版本的工具,但這裡我為什麼使用低版本的SDK來開發Android游戲呢?這裡介紹下原因:

       1、Android SDK 屬於向下兼容!那麼低版本可以運行的,高版本基本上更是沒問題!(當然每次SDK的更新也會帶來新功能,或者修改了一些原來的BUG等等,那麼其實對於游戲開發來說,如果你的游戲中不需要更高的SDK版本的支持情況下,完全不必去追求最新的SDK!)

       2、使用低版本進行游戲開發這樣能兼顧更多的機型,獲取更多的用戶!

       3、大家都知道Android SDK 每次版本的更新,底層代碼也會更健壯和優化了!比如我們公司的網游Android版在G2(SDK1.5)上跑起來稍微有些卡,而在我的手機上(SDK2.2)運行起來流暢的沒說的~各種舒坦~~但是這樣也會帶來一些弊端,比如我們自己游戲如果上來就用高版本SDK進行開發,那麼對於性能、內存上到底如何,我們都不會很容易的看出其效果,如果我們用低版本的SDK則會讓我們明顯的感受到性能到底如何~你想想如果你的游戲在1.5 ,1.6上跑起來很流暢,那放在更高版本的SDK機器上更是沒說的啦~

       總結:游戲開發中,如果你游戲不需要更高的API的支持,那麼推薦基於SDK 1.5和1.6來開發!

       在上一篇中我給大家介紹了觸摸屏手勢操作,但是這種觸屏手勢的操作比較有局限性;比如我們都知道Android可以利用手勢來解鎖,類似九宮格形式的,通過自定義的一個單筆畫手勢可以解開屏幕鎖,還可以自定義筆畫手勢來啟動一個應用等,那麼這種所謂的筆畫手勢其實就是今天我要給大家講解的輸入法手勢識別技術!這種手勢是我們可以自己來自定義,而不像之前的觸屏手勢操作只是利用Android 對一些觸屏動作的封裝罷了。下面上幾張手機自定義筆劃手勢解鎖的的截圖:

       OK,那麼既然利用手勢既然能進行解鎖等操作,那麼我們游戲開發中,更是可以加入這一亮點了,比如在游戲中我畫個圓形執行換背景操作,畫個X表示退出游戲等等,等等、哈哈 是不是感覺很有意思了?好的,下面就開始進入講解!

       首先本篇主要學習兩點:

       1、如何創建輸入法手勢、刪除輸入法手勢、從SD卡中讀取出手勢文件!

       2、當輸入法手勢創建後,如何來匹配出我們的自定義手勢!

       下面我們來熟習兩個類和幾個概念:

       1、什麼是 GestureOverlayView ?  簡單點說其實就是一個手寫繪圖區;

       2、什麼是 GestureLibrary ?   這個類是對手勢進行保存、刪除等操作的,一個存放手勢的小倉庫!

       3、筆劃是什麼,字體筆畫?  是的,其實就是跟我們寫字的筆劃一個概念!

       4、什麼是筆劃類型?   輸入法手勢操作中,筆劃類型有兩種;一種是:單一筆劃,另外一種是:多筆劃

       所謂單一筆劃筆劃就是一筆劃畫出一個手勢,從你手指接觸屏幕開始到你離開屏幕筆畫就會立刻形成一個手勢!一氣呵成!

       而多筆劃則是可以在一定緊湊時間內隨意幾筆劃都可,然後超過這個緊湊時間後便會形成一個手勢!

       先出項目截圖,簡單說下其功能和操作:

       圖1界面中分為3塊,從上到下依次是:TextView ,EditText,SurfaceView;然後在SurfaceView後面還有一個覆蓋全屏的GestureOverlayView!

       圖2界面是在創建好的手勢中匹配手勢的界面,這裡很清晰看出來,找的很對 ~嘿嘿~

       先看下main.xml:

XML/HTML代碼

<?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.GestureOverlayView 
      android:id="@+id/himi_gesture" android:layout_width="fill_parent" 
      android:layout_height="fill_parent" android:layout_weight="1.0"/> 
  </RelativeLayout> 
</LinearLayout> 

       xml中注冊的有我們自定義的surfaceview,對此不太熟悉可以去看下Android游戲開發6,不多解釋了。關於GestureOverlayView這裡也只是簡單的定義了寬高,還有一些重要的屬性設置在代碼中設置了,當然xml也可以設置的。

       下面看MainActivity.java:

Java代碼

/** 
 *@author Himi 
 *@輸入法手勢識別 
 *@注意: android.gesture這個類在api-4(SDK1.6)才開始支持的! 
 *@提醒:默認存到SD卡中,所以別忘記在AndroidMainfest.xml加上SD卡讀寫權限! 
 */ 
public class MainActivity extends Activity {  
  private GestureOverlayView gov;// 創建一個手寫繪圖區  
  private Gesture gesture;// 手寫實例  
  private GestureLibrary gestureLib;//創建一個手勢倉庫  
  private TextView tv;  
  private EditText et;  
  private String path;//手勢文件路徑  
  private File file;//  
  @Override 
  public 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);//設置筆劃類型  
    // GestureOverlayView.GESTURE_STROKE_TYPE_MULTIPLE 設置支持多筆劃  
    // GestureOverlayView.GESTURE_STROKE_TYPE_SINGLE 僅支持單一筆劃  
    path = new File(Environment.getExternalStorageDirectory(), "gestures").getAbsolutePath();  
    //得到默認路徑和文件名/sdcard/gestures  
    file = new File(path);//實例gestures的文件對象  
    gestureLib = GestureLibraries.fromFile(path);//實例手勢倉庫  
    gov.addOnGestureListener(new OnGestureListener() { // 這裡是綁定手寫繪圖區  
          @Override 
          // 以下方法是你剛開始畫手勢的時候觸發  
          public void onGestureStarted(GestureOverlayView overlay, MotionEvent event) {  
            tv.setText("請您在緊湊的時間內用兩筆劃來完成一個手勢!西西~");  
          }  
          @Override 
          // 以下方法是當手勢完整形成的時候觸發  
          public void onGestureEnded(GestureOverlayView overlay, MotionEvent event) {  
            gesture = overlay.getGesture();// 從繪圖區取出形成的手勢  
            if (gesture.getStrokesCount() == 2) {//我判定當用戶用了兩筆劃  
              //(強調:如果一開始設置手勢筆畫類型是單一筆畫,那你這裡始終得到的只是1!)  
              if (event.getAction() == MotionEvent.ACTION_UP) {//判定第兩筆劃離開屏幕  
                //if(gesture.getLength()==100){}//這裡是判定長度達到100像素  
                if (et.getText().toString().equals("")) {  
                  tv.setText("由於您沒有輸入手勢名稱,so~保存失敗啦~");  
                } else {  
                  tv.setText("正在保存手勢...");  
                  addGesture(et.getText().toString(), gesture);//我自己寫的添加手勢函數  
                }  
              }  
            } else {  
              tv.setText("請您在緊湊的時間內用兩筆劃來完成一個手勢!西西~");  
            }  
          }  
          @Override 
          public void onGestureCancelled(GestureOverlayView overlay, MotionEvent event) {  
          }  
          @Override 
          public void onGesture(GestureOverlayView overlay, MotionEvent event) {  
          }  
        });  
    //----這裡是在程序啟動的時候進行遍歷所有手勢!------  
    if (!gestureLib.load()) {  
      tv.setText("Himi提示:手勢超過9個我做了刪除所有手勢的操作,為了界面整潔一些!" 
          + " 輸入法手勢練習~(*^__^*)~ 嘻嘻!/n操作介紹:(畫手勢我設置必須畫兩筆劃才行哦~)/n1." +  
              "添加手勢:先EditText中輸入名稱,然後在屏幕上畫出手勢!/n2.匹配手勢:" 
          + "在EditText輸入/"himi/",然後輸入手勢即可! ");  
    } else {  
      Set<String> set = gestureLib.getGestureEntries();//取出所有手勢  
      Object ob[] = set.toArray();  
      loadAllGesture(set, ob);  
    }  
  }  
} 

       這個就是MainActivity主要代碼了,其中添加手勢、匹配手勢、遍歷手勢、將手勢轉成圖片這些我都單獨寫成了函數,這樣讓各位童鞋更清晰思路一些。

       從以上代碼中我們看出在創建手勢之前,手寫繪圖區(GestureOverlayView)肯定先被創建出來,然後我們就可以在其區域中進行筆劃繪畫手勢了,當然繪畫手勢前,我們也需要設置了筆劃類型,也就是我一開始給大家介紹的,其後最重要的就是手寫繪圖區的手勢監聽器綁定,增加OnGestureListener這個監聽器重寫了四個函數,這裡最重要的就兩個函數:

       onGestureStarted   和  onGestureEnded  :手勢開始和手勢結束的監聽函數。

       尤其是手勢結束監聽這個函數尤為重要,在其中我設置好幾個條件語句,這麼幾個條件一方面是讓大家了解Gesture中一些比較重要常用的方法,另一方面我要提醒各位童鞋:

       如果你設置筆劃類型是多筆劃類型的,那麼理想狀態下,應該是在一段緊湊時間內,不管你使用了幾筆劃來繪制手勢,系統都應該在判定你在一定短暫時間內沒有再進行筆劃的時候才應該創建手勢,並且系統響應此函數;

       其實錯了,一開始我也這麼想,但是發現,不管你設置的筆劃類型是單一的還是多筆劃當你手指離開屏幕,不管你當前是第幾筆,Android都會去響應這個完成函數,so~ 我在這裡調用手勢Gesture類中的getStrokesCount()函數,這個函數會記錄在緊湊時間內你繪制手勢的筆劃數,那麼根據這個函數我們就可以解決手指離開屏幕總被響應的問題了,因為單一筆劃類型永遠這個值不會大於1!

       而 if (event.getAction() == MotionEvent.ACTION_UP) {}寫這個只是給大家演示第二個參數按鍵動作該怎麼用。

       那麼我們下面就來看如何創建一個手勢:

Java代碼

public void addMyGesture(String name, Gesture gesture) {  
    try {  
      if (name.equals("himi")) {  
        findGesture(gesture);  
      } else {  
        // 關於兩種方式創建模擬器的SDcard在【Android2D游戲開發之十】有詳解  
        if (Environment.getExternalStorageState() != null) {// 這個方法在試探終端是否有sdcard!  
          if (!file.exists()) {// 判定是否已經存在手勢文件  
            // 不存在文件的時候我們去直接把我們的手勢文件存入  
            gestureLib.addGesture(name, gesture);  
            if (gestureLib.save()) {////保存到文件中  
              gov.clear(true);//清除筆畫  
              // 注意保存的路徑默認是/sdcard/gesture ,so~別忘記AndroidMainfest.xml加上讀寫權限!  
              // 這裡抱怨一下,咳咳、其實昨天就應該出這篇博文的,就是因為這裡總是異常,今天仔細看了  
              // 才發現不是沒寫權限,而是我雖然在AndroidMainfest.xml中寫了權限,但是寫錯了位置..哭死!  
              tv.setText("保存手勢成功!因為不存在手勢文件," + "所以第一次保存手勢成功會默認先創" +  
                  "建了一個手勢文件!然後將手勢保存到文件中.");  
              et.setText("");  
              gestureToImage(gesture);  
            } else {  
              tv.setText("保存手勢失敗!");  
            }  
          } else {//當存在此文件的時候我們需要先刪除此手勢然後把新的手勢放上  
            //讀取已經存在的文件,得到文件中的所有手勢  
            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);//刪除與當前名字相同的手勢  
/*----備注2-----------------*/gestureLib.removeEntry(name);  
                gestureLib.addGesture(name, gesture);  
              } else {  
                gestureLib.addGesture(name, gesture);  
              }  
              if (gestureLib.save()) {  
                gov.clear(true);//清除筆畫  
                gestureToImage(gesture);  
                tv.setText("保存手勢成功!當前所有手勢一共有:" + ob.length + "個");  
                et.setText("");  
              } else {  
                tv.setText("保存手勢失敗!");  
              }  
              ////------- --以下代碼是當手勢超過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("當前模擬器沒有SD卡 - -。");  
        }  
      }  
    } catch (Exception e) {  
      tv.setText("操作異常!");  
    }  
  } 

       這裡也都很好理解,套路類似之前File文件存儲的套路,先判斷SD是否存在,然後是文件是否存在:

       如果文件不存在就先直接添加到手勢到手勢倉庫中,然後手勢倉調用gestureLib.save()才算把手勢存到SD卡的手勢文件中。

       文件存在的話還要去判定是否文件中包含了相同名字的手勢;當然這裡可以不判定是否有相同手勢名存在,然後進行刪除操作!其實也可不刪除,直接添加進去當前新建的手勢;原因看了下面的備注解釋就明白了;

       備注1:因為gestureLib保存的手勢是個HashMap, key=手勢的名字,value=手勢,所以gestureLib.removeGesture(name, gesture);這種刪除方式只是刪除了手勢,該手勢名字依舊保存在hashmap中,下次還有相同的name手勢存入的時候Hashmap就直接覆蓋本條目了。所以根據Hashmap的特征,我們可以不進行刪除操作,可以直接gestureLib.addGesture(name, gesture);因為如果出現相同的手勢名字的手勢,Hashmap就會根據key(手勢的名字)直接覆蓋其條目的value(手勢)滴~

       備注2:這裡也是一種刪除手勢的方式,但是這種方式跟備注1的不同,這裡是將Hashmap中的條目刪除,也就是說key和value都被刪去!

       下面看下如何把手勢轉成bitmap:

Java代碼

public void gestureToImage(Gesture ges) {//將手勢轉換成Bitmap  
   //把手勢轉成圖片,存到我們SurfaceView中定義的Image容器中,然後都畫出來~  
   if (MySurfaceView.vec_bmp != null) {  
     MySurfaceView.vec_bmp.addElement(ges.toBitmap(100, 100, 12, Color.GREEN));  
   }  
} 

       下面是如何遍歷手勢:

Java代碼

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++) {  
        //把手勢轉成Bitmap  
        gestureToImage(gestureLib.getGestures((String) ob[i]).get(0));  
        //這裡是把我們每個手勢的名字也保存下來  
        MySurfaceView.vec_string.addElement((String) ob[i]);  
      }  
    }  
  } 

       下面最後來看看手勢的匹配:

Java代碼

public void findGesture(Gesture gesture) {  
  try {  
    // 關於兩種方式創建模擬器的SDcard在【Android2D游戲開發之十】有詳解  
    if (Environment.getExternalStorageState() != null) {// 這個方法在試探終端是否有sdcard!  
      if (!file.exists()) {// 判定是否已經存在手勢文件  
        tv.setText("匹配手勢失敗,因為手勢文件不存在!!");  
      } else {//當存在此文件的時候我們需要先刪除此手勢然後把新的手勢放上  
        //讀取已經存在的文件,得到文件中的所有手勢  
        if (!gestureLib.load()) {//如果讀取失敗  
          tv.setText("匹配手勢失敗,手勢文件讀取失敗!");  
        } else {//讀取成功  
          List<Prediction> predictions = gestureLib.recognize(gesture);  
          //recognize()的返回結果是一個prediction集合,  
          //包含了所有與gesture相匹配的結果。  
          //從手勢庫中查詢匹配的內容,匹配的結果可能包括多個相似的結果,  
          if (!predictions.isEmpty()) {  
            Prediction prediction = predictions.get(0);  
            //prediction的score屬性代表了與手勢的相似程度  
            //prediction的name代表手勢對應的名稱  
            //prediction的score屬性代表了與gesture得相似程度(通常情況下不考慮score小於1的結果)。  
            if (prediction.score >= 1) {  
              tv.setText("當前你的手勢在手勢庫中找到最相似的手勢:name =" + prediction.name);  
            }  
          }  
        }  
      }  
    } else {  
      tv.setText("匹配手勢失敗,,當前模擬器沒有SD卡 - -。");  
    }  
  } catch (Exception e) {  
    e.printStackTrace();  
    tv.setText("由於出現異常,匹配手勢失敗啦~");  
  }  
} 

       那麼最後給各位童鞋說一下,其實輸入法手勢操作很是適合游戲中使用,不管是觸摸屏手勢操作還是今天講的輸入法手勢操作如果加到游戲中那都是相當贊的!

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved