吳裕雄--天生自然ANDROID開發學習:3.8 Gestures(手勢)
阿新 • • 發佈:2020-08-29
手勢是:連續觸碰的行為,比如左右上下滑動螢幕,又或者畫一些不規則的幾何圖形! Android對上述兩種手勢行為都提供了支援: Android提供手勢檢測,併為手勢識別提供了相應的監聽器! Android執行開發者自行新增手勢,並且提供了相應的API識別使用者手勢!
官方API文件先:GestureDetector:http://androiddoc.qiniudn.com/reference/android/view/GestureDetector.html
1.Android中手勢互動的執行順序 1.手指觸碰螢幕時,觸發MotionEvent事件! 2.該事件被OnTouchListener監聽,可在它的onTouch()方法中獲得該MotionEvent物件! 3.通過GestureDetector轉發MotionEvent物件給OnGestureListener 4.我們可以通過OnGestureListener獲得該物件,然後獲取相關資訊,以及做相關處理!
2.GestureListener詳解: 按下(onDown): 剛剛手指接觸到觸控式螢幕的那一剎那,就是觸的那一下。 拋擲(onFling): 手指在觸控式螢幕上迅速移動,並鬆開的動作。 長按(onLongPress): 手指按在持續一段時間,並且沒有鬆開。 滾動(onScroll): 手指在觸控式螢幕上滑動。 按住(onShowPress): 手指按在觸控式螢幕上,它的時間範圍在按下起效,在長按之前。 抬起(onSingleTapUp):手指離開觸控式螢幕的那一剎那。 知道了GestureListener的相關方法後,實現手勢檢測也很簡單,步驟如下: Step 1: 建立GestureDetector物件,建立時需實現GestureListener傳入 Step 2: 將Activity或者特定元件上的TouchEvent的事件交給GestureDetector處理即可! 我們寫個簡單的程式碼來驗證這個流程,即重寫對應的方法:
程式碼如下: public class MainActivity extends AppCompatActivity { private MyGestureListener mgListener; private GestureDetector mDetector; private final static String TAG = "MyGesture"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //例項化GestureListener與GestureDetector物件 mgListener = new MyGestureListener(); mDetector = new GestureDetector(this, mgListener); } @Override public boolean onTouchEvent(MotionEvent event) { return mDetector.onTouchEvent(event); } //自定義一個GestureListener,這個是View類下的,別寫錯哦!!! private class MyGestureListener implements GestureDetector.OnGestureListener { @Override public boolean onDown(MotionEvent motionEvent) { Log.d(TAG, "onDown:按下"); return false; } @Override public void onShowPress(MotionEvent motionEvent) { Log.d(TAG, "onShowPress:手指按下一段時間,不過還沒到長按"); } @Override public boolean onSingleTapUp(MotionEvent motionEvent) { Log.d(TAG, "onSingleTapUp:手指離開螢幕的一瞬間"); return false; } @Override public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) { Log.d(TAG, "onScroll:在觸控式螢幕上滑動"); return false; } @Override public void onLongPress(MotionEvent motionEvent) { Log.d(TAG, "onLongPress:長按並且沒有鬆開"); } @Override public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) { Log.d(TAG, "onFling:迅速滑動,並鬆開"); return false; } } }
3.簡單的例子:下滑關閉Activity,上滑啟動新的Activity 這裡就用上述的SimpleOnGestureListener來實現
實現程式碼: public class MainActivity extends AppCompatActivity { private GestureDetector mDetector; private final static int MIN_MOVE = 200; //最小距離 private MyGestureListener mgListener; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //例項化SimpleOnGestureListener與GestureDetector物件 mgListener = new MyGestureListener(); mDetector = new GestureDetector(this, mgListener); } @Override public boolean onTouchEvent(MotionEvent event) { return mDetector.onTouchEvent(event); } //自定義一個GestureListener,這個是View類下的,別寫錯哦!!! private class MyGestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float v, float v1) { if(e1.getY() - e2.getY() > MIN_MOVE){ startActivity(new Intent(MainActivity.this, MainActivity.class)); Toast.makeText(MainActivity.this, "通過手勢啟動Activity", Toast.LENGTH_SHORT).show(); }else if(e1.getY() - e2.getY() < MIN_MOVE){ finish(); Toast.makeText(MainActivity.this,"通過手勢關閉Activity",Toast.LENGTH_SHORT).show(); } return true; } } }
獲得GestureLibraries物件後,就可以使用該物件提供的下述方法來做相應操作了: 相關方法: public void addGesture (String entryName, Gesture gesture):新增一個名為entryName的手勢 public Set<String> getGestureEntries ():獲得手勢庫中所有手勢的名稱 public ArrayList<Gesture> getGestures (String entryName):獲得entryName名稱對應的全部手勢 public ArrayList<Prediction> recognize (Gesture gesture): 從當前手勢庫中識別與gesture匹配的全部手勢 public void removeEntry (String entryName):刪除手勢庫中entryName名稱對應的手勢 public void removeGesture (String entryName, Gesture gesture):刪除手勢庫中entryName和gesture都匹配的手勢 public abstract boolean save ():想手勢庫中新增手勢或從中刪除手勢後呼叫該方法儲存手勢庫 GestureOverlayView手勢編輯元件: Android為GestureOverlayView提供了三種監聽器介面,如下,一般常用的是:OnGesturePerformedListener;用於手勢完成時提供響應!
兩個佈局檔案:activity_main.xml和dialog_save.xml activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="請在下方螢幕中繪製手勢~" android:textSize="20sp"/> <!-- gestureStrokeType控制手勢是否需要一筆完成,multiple表示允許多筆--> <android.gesture.GestureOverlayView android:id="@+id/gesture" android:layout_width="match_parent" android:layout_height="match_parent" android:gestureStrokeType="multiple" /> </LinearLayout> dialog_save.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="8dp" android:text="請填寫手勢名稱:"/> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/edit_name"/> </LinearLayout> <ImageView android:id="@+id/img_show" android:layout_width="128dp" android:layout_height="128dp" android:layout_marginTop="10dp"/> </LinearLayout> MainActivity.java: public class MainActivity extends AppCompatActivity { private EditText editText; private GestureOverlayView gesture; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //獲取手勢編輯元件後,設定相關引數 gesture = (GestureOverlayView) findViewById(R.id.gesture); gesture.setGestureColor(Color.GREEN); gesture.setGestureStrokeWidth(5); gesture.addOnGesturePerformedListener(new GestureOverlayView.OnGesturePerformedListener() { @Override public void onGesturePerformed(GestureOverlayView gestureOverlayView, final Gesture gesture) { View saveDialog = getLayoutInflater().inflate(R.layout.dialog_save,null,false); ImageView img_show = (ImageView) saveDialog.findViewById(R.id.img_show); final EditText edit_name = (EditText) saveDialog.findViewById(R.id.edit_name); Bitmap bitmap = gesture.toBitmap(128,128,10,0xffff0000); img_show.setImageBitmap(bitmap); new AlertDialog.Builder(MainActivity.this).setView(saveDialog) .setPositiveButton("儲存",new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { //獲取檔案對應的手勢庫 GestureLibrary gestureLib = GestureLibraries.fromFile("/mnt/sdcard/mygestures"); gestureLib.addGesture(edit_name.getText().toString(),gesture); gestureLib.save(); } }).setNegativeButton("取消", null).show(); } }); } } 最後還需要在AndroidManifest.xml中新增寫入SD卡的許可權: <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 6.手勢識別示例 實現程式碼: public class MainActivity extends AppCompatActivity { private GestureOverlayView gesture; private GestureLibrary gestureLibrary; private Context mContext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mContext = MainActivity.this; gestureLibrary = GestureLibraries.fromFile("mmt/sdcard/mygestures"); if (gestureLibrary.load()) { Toast.makeText(mContext, "手勢庫載入成功", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(mContext, "手勢庫載入失敗", Toast.LENGTH_SHORT).show(); } //獲取手勢編輯元件後,設定相關引數 gesture = (GestureOverlayView) findViewById(R.id.gesture); gesture.setGestureColor(Color.GREEN); gesture.setGestureStrokeWidth(5); gesture.addOnGesturePerformedListener(new GestureOverlayView.OnGesturePerformedListener() { @Override public void onGesturePerformed(GestureOverlayView gestureOverlayView, final Gesture gesture) { //識別使用者剛繪製的手勢 ArrayList<Prediction> predictions = gestureLibrary.recognize(gesture); ArrayList<String> result = new ArrayList<String>(); //遍歷所有找到的Prediction物件 for (Prediction pred : predictions) { if (pred.score > 2.0) { result.add("與手勢【" + pred.name + "】相似度為" + pred.score); } } if (result.size() > 0) { ArrayAdapter<Object> adapter = new ArrayAdapter<Object>(mContext, android.R.layout.simple_dropdown_item_1line, result.toArray()); new AlertDialog.Builder(mContext).setAdapter(adapter,null).setPositiveButton("確定",null).show(); }else{ Toast.makeText(mContext,"無法找到匹配的手勢!",Toast.LENGTH_SHORT).show(); } } }); } } 另外別忘了在AndroidManifest.xml檔案中加入讀SD卡的許可權: <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>