范例說明 自SDK 1.6開始,Android手機已支持內置Gesture Builder程序,若是被Google簽署(Signed)過出廠的手機應會內置此程序,如果是程序開發人員,可在SDK文件夾里的“\android\platforms\ android\samples\GestureBuilder”找到這支程序的源代碼,編譯后即可生成各SDK版本可使用的Gesture Builder程序(apk)自行安裝使用。 Gesture Builder提供了一手寫識別的功能,讓用戶以類似于涂鴉的方式繪制一個手寫符號,使之對應一個字符串名稱,然而GestureBuilder功能雖完整,但在手寫字符串的創建上卻有些限制,如:制式化的建立方式、無法自行配置涂鴉區、查看手寫(Gesture)以ListView來呈現等,在實際開發上稍顯“復雜”了些。 本范例程序練習,是編寫一個迷你版的Gesture Builder,可以通過全屏(GestureOverlayView)接收User的手寫,并讓我們“共享”系統預設使用的Gesture Library,在日后的項目開發中,便可依據此范例作為開發模板,創建、保存手寫筆畫圖案。 <!--[endif]--> ▲ 圖5-28? 創建一個名為RICK的手寫符號 范例程序 res/layout/main.xml 我們可以看到在res/layout/main.xml版面配置中,其中有一個TAG為<android.gesture. GestureOverlayView>的Widget,可稱為“手寫繪圖區”,當中有兩項較重要的屬性,分別為android:layout_width設置為“fill_parent”以及android:gestureStrokeType設置為“multiple”,這表示為支持多筆畫,若設置為“single”則僅支持單一筆畫。 ? <android.gesture.GestureOverlayView ? android:id="@+id/myGestures1" ? android:layout_width="fill_parent" ? android:layout_height="0dip" ? android:layout_weight="1.0" ? android:gestureStrokeType="multiple" /> src/irdc.ex05_25/EX05_25.java Gesture對象是自GestureOverlayView.getGesture() 所取得的手寫對象;GestureLibraries為保存手寫背后所包含的意義(String),本范例利用GestureLibraries.fromFile()方法來加載預設的Gesture文件,倘若默認手機的SD存儲卡中尚未創建Gesture手寫數據文件,此程序也會處理創建新文件的工作。此外,程序中舉例應用了GestureLibraries.addGesture()新建手寫數據、GestureLibraries.save()保存寫入手寫數據GestureLibraries.load()加載手寫數據、GestureLibraries. removeGesture()刪除手寫數據等方法。 ? /* import程序略 */ import android.gesture.Gesture; import android.gesture.GestureLibraries; import android.gesture.GestureLibrary; import android.gesture.GestureOverlayView; import android.view.MotionEvent; ? public class EX05_25 extends Activity { ? private Gesture ges; ? private GestureLibrary lib; ? private GestureOverlayView overlay; ? private Button button01,button02; ? private EditText et; ? private String gesPath; ? ? /** Called when the activity is first created. */ ? @Override ? public void onCreate(Bundle savedInstanceState) ? { ??? super.onCreate(savedInstanceState); ??? /* 加載main.xml Layout */ ??? setContentView(R.layout.main); ???? ??? /* 查看SDCard是否存在 */ ??? if(!Environment.MEDIA_MOUNTED.equals ??????? (Environment.getExternalStorageState())) ??? { ????? /* SD卡不存在,顯示Toast信息 */ ????? Toast.makeText(EX05_25.this,"SD卡不存在!程序無法執行", ???????????????????? Toast.LENGTH_LONG).show(); ????? finish(); ??? } ??? /* 以findViewById()取得對象 */ ??? et = (EditText)this.findViewById(R.id.myEditText1); ??? button01 = (Button)this.findViewById(R.id.myButton1); ??? button02 = (Button)this.findViewById(R.id.myButton2); ??? overlay = (GestureOverlayView) findViewById(R.id.myGestures1); ? ??? /* 取得系統默認的GestureLibrary文件路徑 */ ??? gesPath = new File ??? ( ????? Environment.getExternalStorageDirectory(),"gestures" ??? ).getAbsolutePath(); ? ??? /* 設置EditText的OnKeyListener */ ??? et.setOnKeyListener(new EditText.OnKeyListener() ??? { ????? @Override ????? public boolean onKey(View v, int keyCode, KeyEvent event) ????? { ??????? /* 名稱與手寫都均設置好時將新建的Button enable */ ??????? if(ges!=null&&et.getText().length()!=0) ??????? { ????????? button01.setEnabled(true); ??????? } ??????? else ??????? { ????????? button01.setEnabled(false); ??????? } ??????? return false; ????? } ??? }); ??? ??? /* 設置Overlay的OnGestureListener */ ??? overlay.addOnGestureListener ??? (new GestureOverlayView.OnGestureListener() ??? { ????? @Override ????? public void onGesture ????? (GestureOverlayView overlay,MotionEvent event) ????? { ????? } ????? /* 開始畫手寫時將添加的Button disable,并清除Gesture */ ????? @Override ????? public void onGestureStarted ????? (GestureOverlayView overlay,MotionEvent event) ????? { ??????? button01.setEnabled(false); ??????? ges = null; ????? } ????? /* 手寫畫完時判斷名稱與手寫是否完整建立 */ ????? @Override ????? public void onGestureEnded ????? (GestureOverlayView overlay, MotionEvent event) ????? { ??????? ges = overlay.getGesture(); ??????? if (ges!=null&&et.getText().length()!=0) ??????? { ????????? button01.setEnabled(true); ??????? } ????? } ????? @Override ????? public void onGestureCancelled ????? (GestureOverlayView overlay, MotionEvent event) ????? { ????? } ??? }); ??? ??? /* 設定button01的OnClickListener */ ??? button01.setOnClickListener(new Button.OnClickListener() ??? { ????? @Override ????? public void onClick(View v) ????? { ??????? String gesName=et.getText().toString(); ??????? try ??????? { ????????? File file = new File(gesPath); ????????? lib = GestureLibraries.fromFile(gesPath); ????????? ????????? if(!file.exists()) ????????? { ??????????? /* 文件不存在就直接寫入 */ ??????????? lib.addGesture(gesName,ges); ??????????? if(lib.save()) ??????????? { ????????????? /* 將設置畫面數據清除 */ ????????????? et.setText(""); ????????????? button01.setEnabled(false); ????????????? overlay.clear(true); ????????????? /* 保存成功,顯示Toast信息 */ ????????????? Toast.makeText ????????????? ( ??????????????? EX05_25.this,getString(R.string.save_success)+":"+ ??????????????? gesPath,Toast.LENGTH_LONG ????????????? ).show(); ??????????? } ??????????? else ??????????? { ????????????? /* 保存失敗,顯示Toast信息 */ ????????????? Toast.makeText ????????????? ( ??????????????? EX05_25.this, getString(R.string.save_failed)+":"+ ??????????????? gesPath,Toast.LENGTH_LONG ????????????? ).show(); ??????????? } ????????? } ????????? else ????????? { ??????????? /* 文件存在時先讀取已保存的Gesture */ ??????????? if (!lib.load()) ??????????? { ????????????? /* Library讀取失敗,顯示Toast信息 */ ??????? ??????Toast.makeText ????????????? ( ??????????????? EX05_25.this, getString(R.string.load_failed)+":"+ ??????????????? gesPath,Toast.LENGTH_LONG ????????????? ).show(); ??????????? } ??????????? else ??????????? { ????????????? /* 如果Library中存在相同名稱,則先將其移除再寫入 */ ????????????? Set<String> en=lib.getGestureEntries(); ????????????? if(en.contains(gesName)) ????????????? { ??????????????? ArrayList<Gesture> al=lib.getGestures(gesName); ??????????????? for(int i=0;i<al.size();i++) ??????????????? { ????????????? ????lib.removeGesture(gesName,al.get(i)); ??????????????? } ????????????? } ????????????? lib.addGesture(gesName,ges); ????????????? if(lib.save()) ????????????? { ??????????????? /* 將設置畫面數據清除 */ ??????????????? et.setText(""); ??????????????? button01.setEnabled(false); ??????????????? overlay.clear(true); ??????????????? /* 保存成功,顯示Toast信息 */ ??????????????? Toast.makeText ??????????????? ( ????????????????? EX05_25.this,getString(R.string.save_success)+":"+ ????????????????? gesPath,Toast.LENGTH_LONG ? ??????????????).show(); ????????????? } ????????????? else ????????????? {? ??????????????? /* 保存失敗,顯示Toast信息 */ ??????????????? Toast.makeText ??????????????? ( ????????????????? EX05_25.this, getString(R.string.save_failed)+":"+ ????????????????? gesPath,Toast.LENGTH_LONG ??????????????? ).show(); ????????????? } ??????????? } ????????? } ??????? } ??????? catch(Exception e) ??????? { ????????? e.printStackTrace(); ??????? } ????? } ??? }); ??? ??? /* 設置button02的OnClickListener,清除建立的Overlay */ ? ??button02.setOnClickListener(new Button.OnClickListener() ??? { ????? @Override ????? public void onClick(View v) ????? { ??????? et.setText(""); ??????? button01.setEnabled(false); ??????? overlay.clear(true); ????? } ??? }); ? } } AndroidManifest.xml 由于Gesture手寫Library文件“/sdcard/gestures”默認保存在SD存儲卡中,故需要寫入External Storage的相關權限。 ? <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> ? 擴展學習 如果要加載Gesture手寫的Library文件,主程序中所使用的方法是GestureLibraries.fromFile(),除此方法外,還有其他方法,分別為從gesture路徑(String)、File對象、從私有的gesture文件路徑加載和從資源文件里加載gesture等,如表5-12所示。 表5-12??????????????????????????????????????????????????? gesture相關列表 | static GestureLibrary | fromFile(String path) | | static GestureLibrary | fromFile(File path) | | static GestureLibrary | fromPrivateFile(Context context, String name) | | static GestureLibrary | fromRawResource(Context context, int resourceId) | ? 另一個擴展練習,通過讀取/sdcard/gestures里方才建立的手寫,并顯示在ListView中,此擴展學習范例的關鍵程序,提示如下(詳細解答可參考范例光盤): ? /* 讀取gesture信息的method */ private void loadGestures() { ? gesNames=new ArrayList<String>(); ? gesPics=new ArrayList<Bitmap>(); ? ? try ? { ??? lib = GestureLibraries.fromFile(gesFile); ???? ??? if(gesFile.exists()) ??? { ????? /* 文件存在時先讀取已存儲的Gesture */ ????? if (!lib.load()) ????? { ??????? /* Library讀取失敗 */ ??????? mEmpty.setText(getString(R.string.load_failed)); ??????? mEmpty.setVisibility(EditText.VISIBLE); ????? } ????? else ????? { ??????? /* 讀取gesture信息 */ ??????? Object[] en=lib.getGestureEntries().toArray(); ??????? for(int i=0;i<en.length;i++) ??????? { ????????? ArrayList<Gesture> al=lib.getGestures(en[i].toString()); ????????? for(int j=0;j<al.size();j++) ????????? { ??????????? /* 手寫名稱 */ ??????????? gesNames.add(en[i].toString()); ??????????? Gesture gs=(Gesture)al.get(j); ??????????? /* 將手寫轉成Bitmap圖片 */ ??????????? gesPics.add(gs.toBitmap(64,64,12,Color.GREEN)); ????????? } ??????? } ????? } ??? } ? } ? catch(Exception e) ? { ??? e.printStackTrace(); ? } } <!--[endif]--> ▲ 圖5-29? 讀取/sdcard/gestures中已經建立的手寫筆畫并顯示在ListView中 |