Gestures(手勢(shì))
本節(jié)引言:
周六不休息,剛剪完了個(gè)大平頭回來(lái),繼續(xù)碼字~
好的,本節(jié)給大家?guī)?lái)點(diǎn)的是第三章的最后一節(jié)——Gestures(手勢(shì)), 用過(guò)魅族手機(jī)的朋友相信對(duì)手勢(shì)肯定是不陌生的,在home鍵兩側(cè)像屏幕內(nèi)滑動(dòng), 可以打開(kāi)后臺(tái)任務(wù)列表等等~在應(yīng)用中通過(guò)手勢(shì)來(lái)操作會(huì)大大提升用戶(hù)體驗(yàn), 比如Scroll手勢(shì)在瀏覽器中個(gè)滾屏,F(xiàn)ling在瀏覽器中的換頁(yè)等!
當(dāng)然,有利也有弊,比如不當(dāng)?shù)氖謩?shì)操作引起APP Carsh,經(jīng)常這樣可是會(huì)引起用戶(hù)不滿(mǎn)的! 所以是否要為你的應(yīng)用增加手勢(shì),可要考慮清楚哦!另外手勢(shì)要和前面學(xué)的單指/多指觸碰相區(qū)分哦!
手勢(shì)是:連續(xù)觸碰的行為,比如左右上下滑動(dòng)屏幕,又或者畫(huà)一些不規(guī)則的幾何圖形! Android對(duì)上述兩種手勢(shì)行為都提供了支持:
- Android提供手勢(shì)檢測(cè),并為手勢(shì)識(shí)別提供了相應(yīng)的監(jiān)聽(tīng)器!
- Android運(yùn)行開(kāi)發(fā)者自行添加手勢(shì),并且提供了相應(yīng)的API識(shí)別用戶(hù)手勢(shì)!
如果你的手機(jī)是Android 4.x的原生Android系統(tǒng)的話(huà),你可能可以在你的手機(jī)或者平板上看到谷歌 提供的一個(gè)Gesture Builder的APP,該應(yīng)用允許用戶(hù)以類(lèi)似于涂鴉的方式繪制一個(gè)手寫(xiě)符號(hào),使之 對(duì)應(yīng)一個(gè)字符串名稱(chēng)!當(dāng)然,沒(méi)有這樣的手機(jī)也沒(méi)關(guān)系,我們有模擬器嘛,自己開(kāi)個(gè)4.0的系統(tǒng)試試 就知道了,另外,我們可以到\mmt\sdcard\gestures獲取到保存手勢(shì)的文件! 好了,嘮嘮叨叨那么多,開(kāi)始講正題吧!
對(duì)了,貼下官方API文檔先:GestureDetector
1.Android中手勢(shì)交互的執(zhí)行順序
- 1.手指觸碰屏幕時(shí),觸發(fā)MotionEvent事件!
- 2.該事件被OnTouchListener監(jiān)聽(tīng),可在它的onTouch()方法中獲得該MotionEvent對(duì)象!
- 3.通過(guò)GestureDetector轉(zhuǎn)發(fā)MotionEvent對(duì)象給OnGestureListener
- 4.我們可以通過(guò)OnGestureListener獲得該對(duì)象,然后獲取相關(guān)信息,以及做相關(guān)處理!
我們來(lái)看下上述的三個(gè)類(lèi)都是干嘛的:MotionEvent: 這個(gè)類(lèi)用于封裝手勢(shì)、觸摸筆、軌跡球等等的動(dòng)作事件。 其內(nèi)部封裝了兩個(gè)重要的屬性X和Y,這兩個(gè)屬性分別用于記錄橫軸和縱軸的坐標(biāo)。GestureDetector: 識(shí)別各種手勢(shì)。OnGestureListener: 這是一個(gè)手勢(shì)交互的監(jiān)聽(tīng)接口,其中提供了多個(gè)抽象方法, 并根據(jù)GestureDetector的手勢(shì)識(shí)別結(jié)果調(diào)用相對(duì)應(yīng)的方法。
——上述資料摘自:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1020/448.html
2.GestureListener詳解:
從1中我們知道了監(jiān)聽(tīng)手勢(shì)的關(guān)鍵是:GestureListener 他給我們提供了下述的回調(diào)方法:
- 按下(onDown): 剛剛手指接觸到觸摸屏的那一剎那,就是觸的那一下。
- 拋擲(onFling): 手指在觸摸屏上迅速移動(dòng),并松開(kāi)的動(dòng)作。
- 長(zhǎng)按(onLongPress): 手指按在持續(xù)一段時(shí)間,并且沒(méi)有松開(kāi)。
- 滾動(dòng)(onScroll): 手指在觸摸屏上滑動(dòng)。
- 按?。╫nShowPress): 手指按在觸摸屏上,它的時(shí)間范圍在按下起效,在長(zhǎng)按之前。
- 抬起(onSingleTapUp):手指離開(kāi)觸摸屏的那一剎那。
知道了GestureListener的相關(guān)方法后,實(shí)現(xiàn)手勢(shì)檢測(cè)也很簡(jiǎn)單,步驟如下:
- Step 1: 創(chuàng)建GestureDetector對(duì)象,創(chuàng)建時(shí)需實(shí)現(xiàn)GestureListener傳入
- Step 2: 將Activity或者特定組件上的TouchEvent的事件交給GestureDetector處理即可! 我們寫(xiě)個(gè)簡(jiǎn)單的代碼來(lái)驗(yàn)證這個(gè)流程,即重寫(xiě)對(duì)應(yīng)的方法:
代碼如下:
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);
//實(shí)例化GestureListener與GestureDetector對(duì)象
mgListener = new MyGestureListener();
mDetector = new GestureDetector(this, mgListener);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return mDetector.onTouchEvent(event);
}
//自定義一個(gè)GestureListener,這個(gè)是View類(lèi)下的,別寫(xiě)錯(cuò)哦?。?!
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:手指按下一段時(shí)間,不過(guò)還沒(méi)到長(zhǎng)按");
}
@Override
public boolean onSingleTapUp(MotionEvent motionEvent) {
Log.d(TAG, "onSingleTapUp:手指離開(kāi)屏幕的一瞬間");
return false;
}
@Override
public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
Log.d(TAG, "onScroll:在觸摸屏上滑動(dòng)");
return false;
}
@Override
public void onLongPress(MotionEvent motionEvent) {
Log.d(TAG, "onLongPress:長(zhǎng)按并且沒(méi)有松開(kāi)");
}
@Override
public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
Log.d(TAG, "onFling:迅速滑動(dòng),并松開(kāi)");
return false;
}
}
}
對(duì)應(yīng)操作截圖:
1.按下后立即松開(kāi):
2.長(zhǎng)按后松開(kāi):
3.輕輕一滑,同時(shí)松開(kāi):
4.按住后不放持續(xù)做滑動(dòng)操作:
PS:從上述結(jié)果來(lái)看,我們發(fā)現(xiàn)了一個(gè)問(wèn)題: 我們實(shí)現(xiàn)OnGestureListener需要實(shí)現(xiàn)所有的手勢(shì),可能我針對(duì)的僅僅是滑動(dòng),但是你還是要去重載, 這顯得很逗逼,是吧,官方肯定會(huì)給出解決方法滴,官方另外給我們提供了一個(gè)SimpleOnGestureListener類(lèi) 只需把上述的OnGestureListener替換成SimpleOnGestureListener即可!
3.簡(jiǎn)單的例子:下滑關(guān)閉Activity,上滑啟動(dòng)新的Activity
這里就用上述的SimpleOnGestureListener來(lái)實(shí)現(xiàn)吧:
運(yùn)行效果圖:
實(shí)現(xiàn)代碼:
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);
//實(shí)例化SimpleOnGestureListener與GestureDetector對(duì)象
mgListener = new MyGestureListener();
mDetector = new GestureDetector(this, mgListener);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return mDetector.onTouchEvent(event);
}
//自定義一個(gè)GestureListener,這個(gè)是View類(lèi)下的,別寫(xiě)錯(cuò)哦?。?!
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, "通過(guò)手勢(shì)啟動(dòng)Activity", Toast.LENGTH_SHORT).show();
}else if(e1.getY() - e2.getY() < MIN_MOVE){
finish();
Toast.makeText(MainActivity.this,"通過(guò)手勢(shì)關(guān)閉Activity",Toast.LENGTH_SHORT).show();
}
return true;
}
}
}
結(jié)果分析:從上面的對(duì)比就可以知道,相比起SimpleOnGestureListener使用SimpleOnGestureListener 顯得更加的簡(jiǎn)單,想重寫(xiě)什么方法就重寫(xiě)什么方法,另外例子比較簡(jiǎn)單,大家可以自己試試 其他玩法,比如通過(guò)手勢(shì)縮放圖片~
4.手勢(shì)添加與識(shí)別:
除了上面講解的手勢(shì)檢測(cè)外,Android還運(yùn)行我們將手勢(shì)進(jìn)行添加,然后提供了相關(guān)的識(shí)別API; Android中使用GestureLibrary來(lái)代表手勢(shì)庫(kù),提供了GestureLibraries工具類(lèi)來(lái)創(chuàng)建手勢(shì)庫(kù)!
四個(gè)加載手勢(shì)庫(kù)的靜態(tài)方法:
獲得GestureLibraries對(duì)象后,就可以使用該對(duì)象提供的下述方法來(lái)做相應(yīng)操作了:
相關(guān)方法:
- public void addGesture (String entryName, Gesture gesture):添加一個(gè)名為entryName的手勢(shì)
- public Set<String> getGestureEntries ():獲得手勢(shì)庫(kù)中所有手勢(shì)的名稱(chēng)
- public ArrayList<Gesture> getGestures (String entryName):獲得entryName名稱(chēng)對(duì)應(yīng)的全部手勢(shì)
- public ArrayList<Prediction> recognize (Gesture gesture): 從當(dāng)前手勢(shì)庫(kù)中識(shí)別與gesture匹配的全部手勢(shì)
- public void removeEntry (String entryName):刪除手勢(shì)庫(kù)中entryName名稱(chēng)對(duì)應(yīng)的手勢(shì)
- public void removeGesture (String entryName, Gesture gesture):刪除手勢(shì)庫(kù)中entryName和gesture都匹配的手勢(shì)
- public abstract boolean save ():想手勢(shì)庫(kù)中添加手勢(shì)或從中刪除手勢(shì)后調(diào)用該方法保存手勢(shì)庫(kù)
GestureOverlayView手勢(shì)編輯組件:
Android為GestureOverlayView提供了三種監(jiān)聽(tīng)器接口,如下,一般常用的是:OnGesturePerformedListener;用于手勢(shì)完成時(shí)提供響應(yīng)!
5.手勢(shì)添加示例:
PS:例子引用的是——李剛《Android瘋狂講義》的代碼
運(yùn)行效果圖:
好吧,下面貼下實(shí)現(xiàn)代碼:
兩個(gè)布局文件:activity_main.xml和dialog_save.xml
activity_main.xml
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="請(qǐng)?jiān)谙路狡聊恢欣L制手勢(shì)~"
android:textSize="20sp"/>
<!-- gestureStrokeType控制手勢(shì)是否需要一筆完成,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
<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="請(qǐng)?zhí)顚?xiě)手勢(shì)名稱(chēng):"/>
<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:
private EditText editText;
private GestureOverlayView gesture;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//獲取手勢(shì)編輯組件后,設(shè)置相關(guān)參數(shù)
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) {
//獲取文件對(duì)應(yīng)的手勢(shì)庫(kù)
GestureLibrary gestureLib = GestureLibraries.fromFile("/mnt/sdcard/mygestures");
gestureLib.addGesture(edit_name.getText().toString(),gesture);
gestureLib.save();
}
}).setNegativeButton("取消", null).show();
}
});
}
}
最后還需要在A(yíng)ndroidManifest.xml中添加寫(xiě)入SD卡的權(quán)限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
6.手勢(shì)識(shí)別示例
實(shí)現(xiàn)代碼:
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, "手勢(shì)庫(kù)加載成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(mContext, "手勢(shì)庫(kù)加載失敗", Toast.LENGTH_SHORT).show();
}
//獲取手勢(shì)編輯組件后,設(shè)置相關(guān)參數(shù)
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) {
//識(shí)別用戶(hù)剛繪制的手勢(shì)
ArrayList<Prediction> predictions = gestureLibrary.recognize(gesture);
ArrayList<String> result = new ArrayList<String>();
//遍歷所有找到的Prediction對(duì)象
for (Prediction pred : predictions) {
if (pred.score > 2.0) {
result.add("與手勢(shì)【" + 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,"無(wú)法找到匹配的手勢(shì)!",Toast.LENGTH_SHORT).show();
}
}
});
}
另外別忘了在A(yíng)ndroidManifest.xml文件中加入讀SD卡的權(quán)限:
本節(jié)小結(jié):
好的,本節(jié)介紹了Android中的Gesture手勢(shì),講解了手勢(shì)判斷,手勢(shì)添加,手勢(shì)識(shí)別三個(gè)內(nèi)容 ,大部分例子來(lái)自于李剛老師的Android瘋狂講義,有興趣的可以看看該書(shū)~謝謝