手勢識别GestureDetector
關于手勢識别是Android為了友善開發人員處理螢幕上的觸摸、拖動、單輕按兩下、滑動等提供的一組接口。用這個我們可以很友善的在螢幕上做出想要的效果,比如滑動翻頁、觸摸不同區域采用不同處理等。
在日常生活中,我們常用的手機浏覽器等,都有這樣的應用。比如,當你用手機浏覽器看小說時,點選螢幕下方,會翻到下一頁;點選螢幕上方,會翻到上一頁;當你點選螢幕中央時,出現目錄選擇等;
現在就看下如何實作以上我們提到的效果。
手勢識别涉及的接口或者類
手勢識别涉及的接口有:OnGestureListener、OnDoubleTapListener;涉及的類有:SimpleOnGestureListener。
OnGestureListener接口
這裡我們建立一個類,來實作該接口。類中實作所有方法,代碼如下:
package com.example.androiddetector_csdn;
import android.content.Context;
import android.util.Log;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
public class GuestureImp implements OnGestureListener{
Context context;
View view;
String tag="me";
public GuestureImp(Context ct,View vw) {
// TODO Auto-generated constructor stub
context=ct;
view=vw;
}
@Override
public boolean onDown(MotionEvent arg0) {
// TODO Auto-generated method stub
Log.e(tag, "down-"+"x:"+arg0.getX()+"y:"+arg0.getY());
return true;
}
@Override
public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2,
float arg3) {
// TODO Auto-generated method stub
return true;
}
@Override
public void onLongPress(MotionEvent arg0) {
// TODO Auto-generated method stub
Log.e(tag, "onLongPress-"+"x:"+arg0.getX()+"y:"+arg0.getY());
}
@Override
public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2,
float arg3) {
// TODO Auto-generated method stub
Log.e(tag, "onScroll-"+"x:"+arg0.getX()+"y:"+arg0.getY());
return false;
}
@Override
public void onShowPress(MotionEvent arg0) {
// TODO Auto-generated method stub
Log.e(tag, "onShowPress-"+"x:"+arg0.getX()+"y:"+arg0.getY());
}
@Override
public boolean onSingleTapUp(MotionEvent arg0) {
// TODO Auto-generated method stub
Log.e(tag, "onSingleTapUp-"+"x:"+arg0.getX()+"y:"+arg0.getY());
return false;
}
}
這裡的方法解釋,引用網上的解釋如下:
按下(onDown): 剛剛手指接觸到觸摸屏的那一刹那,就是觸的那一下。
抛擲(onFling): 手指在觸摸屏上迅速移動,并松開的動作。
長按(onLongPress): 手指按在持續一段時間,并且沒有松開。
滾動(onScroll): 手指在觸摸屏上滑動。
按住(onShowPress): 手指按在觸摸屏上,它的時間範圍在按下起效,在長按之前。
擡起(onSingleTapUp):手指離開觸摸屏的那一刹那。
看解釋就能了解我們可以用這個接口,做哪些操作。如果你要做滑動的控制,那麼,你可以把代碼寫到onFling中,如果你要做拖動的操作,代碼寫到onScroll中。
一般情況下,運作順序有以下幾種:
onDown-onSingleTapUp;
onDown-onShowPress-onLongPress;
網上也有總結一個規律:
任何手勢動作都會先執行一次按下(onDown)動作。
長按(onLongPress)動作前一定會執行一次按住(onShowPress)動作。
按住(onShowPress)動作和按下(onDown)動作之後都會執行一次擡起(onSingleTapUp)動作。
長按(onLongPress)、滾動(onScroll)和抛擲(onFling)動作之後都不會執行擡起(onSingleTapUp)動作。
在這裡有個要注意的地方,就是onDown的傳回值,如果你設為false,經測試,它就一直隻執行onDown-onShowPress-onLongPress;其他的并不會執行。
如果設為true,則正常。
OnDoubleTapListener接口
這個接口主要是用于處理螢幕輕按兩下以及單擊的。(其實,如果單單處理單擊,用OnGestureListener接口就已足夠,這裡主要還是做輕按兩下的處理) 同樣的建立一個類,實作該接口。如下:
package com.example.androiddetector_csdn;
import android.util.Log;
import android.view.GestureDetector.OnDoubleTapListener;
import android.view.MotionEvent;
public class DoubleTabImp implements OnDoubleTapListener{
String tag="me";
public DoubleTabImp() {
// TODO Auto-generated constructor stub
}
@Override
public boolean onSingleTapConfirmed(MotionEvent paramMotionEvent) {
// TODO Auto-generated method stub
Log.e(tag, "onSingleTapConfirmed");
return false;
}
@Override
public boolean onDoubleTap(MotionEvent paramMotionEvent) {
// TODO Auto-generated method stub
Log.e(tag, "onDoubleTap");
return false;
}
@Override
public boolean onDoubleTapEvent(MotionEvent paramMotionEvent) {
// TODO Auto-generated method stub
Log.e(tag, "onDoubleTapEvent");
return false;
}
}
這個方法的實作,需要首先實作了OnGestureListener才能進行。 我們用寫log的形式,來看它們的執行順序。
12-04 15:00:34.434: E/me(25274): down
12-04 15:00:34.524: E/me(25274): onSingleTapUp
12-04 15:00:34.614: E/me(25274): onDoubleTap
12-04 15:00:34.614: E/me(25274): onDoubleTapEvent
12-04 15:00:34.624: E/me(25274): down
12-04 15:00:34.684: E/me(25274): onDoubleTapEvent
如果是單擊,順序如下:
12-04 15:15:33.664: E/me(25274): down
12-04 15:15:33.764: E/me(25274): onSingleTapUp
12-04 15:15:33.964: E/me(25274): onSingleTapConfirmed
SimpleOnGestureListener類
這個類,實際上是實作了以上兩個接口的一個類。使用的時候,可以繼承這個類,選擇你要的方法來實作相應的操作。 也就是說,你可以直接用這個,不用上面的兩個接口。 例如:
package com.example.androiddetector_csdn;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
public class SimpleGuestureImp extends SimpleOnGestureListener{
public SimpleGuestureImp() {
// TODO Auto-generated constructor stub
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
// TODO Auto-generated method stub
return super.onSingleTapUp(e);
}
@Override
public void onLongPress(MotionEvent e) {
// TODO Auto-generated method stub
super.onLongPress(e);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
// TODO Auto-generated method stub
return super.onScroll(e1, e2, distanceX, distanceY);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
// TODO Auto-generated method stub
return super.onFling(e1, e2, velocityX, velocityY);
}
@Override
public void onShowPress(MotionEvent e) {
// TODO Auto-generated method stub
super.onShowPress(e);
}
@Override
public boolean onDown(MotionEvent e) {
// TODO Auto-generated method stub
return super.onDown(e);
}
@Override
public boolean onDoubleTap(MotionEvent e) {
// TODO Auto-generated method stub
return super.onDoubleTap(e);
}
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
// TODO Auto-generated method stub
return super.onDoubleTapEvent(e);
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
// TODO Auto-generated method stub
return super.onSingleTapConfirmed(e);
}
}
這裡很齊全,什麼都不缺了。
手勢識别——滑動的使用
這裡我們開始用例子來說明如何實作滑動效果,步驟如下: 1、建立工程,在新的工程中有預設的MainActivity,這個類要實作接口OnTouchListener; 2、定義接口GestureDetector mGestureDetector,并将接口實作傳入; 3、綁定view與ontouchlistener; 3、截取OnTouchListener的event,将它傳入gesturedetector中。 如果我們要将OnDoubleTapListener的接口實作也放入,那麼用mGestureDetector.setOnDoubleTapListener(new DoubleTabImp());綁定這個實作。 如下:
package com.example.androiddetector_csdn;
import android.os.Bundle;
import android.app.Activity;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.RelativeLayout;
import android.widget.TextView;
public class MainActivity extends Activity implements OnTouchListener {
GestureDetector mGestureDetector;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView=(TextView)findViewById(R.id.mytext);
GuestureImp imp=new GuestureImp(MainActivity.this,textView);
mGestureDetector=new GestureDetector(MainActivity.this, imp);
mGestureDetector.setOnDoubleTapListener(new DoubleTabImp());
textView.setOnTouchListener(this);
}
@Override
public boolean onTouch(View arg0, MotionEvent arg1) {
// TODO Auto-generated method stub
boolean temp= mGestureDetector.onTouchEvent(arg1);
return temp;
}
}
另外,重點在于,在OnGestureListener接口的實作中,寫入以下代碼:
@Override
public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2,
float arg3) {
// TODO Auto-generated method stub
int mini_width=120;
int mini_speed=0;
float distance_right=arg1.getX()-arg0.getX();
float distance_left=arg0.getX()-arg1.getX();
float distance_down=arg1.getY()-arg0.getY();
float distance_up=arg0.getY()-arg1.getY();
if(distance_right>mini_width && Math.abs(arg2)>mini_speed)
{
Log.e(tag, "onFling-"+"向右滑動");
}
else if(distance_left>mini_width && Math.abs(arg2)>mini_speed)
{
Log.e(tag, "onFling-"+"向左滑動");
}
else if(distance_down>mini_width && Math.abs(arg2)>mini_speed)
{
Log.e(tag, "onFling-"+"向下滑動");
}
else if(distance_up>mini_width && Math.abs(arg2)>mini_speed)
{
Log.e(tag, "onFling-"+"向上滑動");
}
return true;
}
第一個參數MotionEvent,是指首次觸摸螢幕時的狀态;第二個參數MotionEvent是最後一次觸摸螢幕時的狀态;第三個參數,是在X軸上滑動的速度,機關是像素/s;第四個參數,是在Y軸上滑動的速度,機關是像素/s。 解釋了以上參數,就能看懂代碼意思,主要就是對比X或者Y方向的滑動距離,滑動距離超過120并且速度大于0的時候,會做出滑動提示。這裡你可以把提示換成你想要實作的方法。
以上就是,我們在view上滑動操作的實作;
螢幕上分區域點選實作不同操作
這個其實與手勢識别沒有什麼關系了。 先看一張圖:

假設這是一個螢幕,那麼我們如果想在點選螢幕不同區域,實作不同的效果,比如翻頁。那麼我們需要做哪些定義和操作呢? 操作步驟如下: 1、擷取整個螢幕的長寬; 2、擷取中心點的坐标(可以根據長寬來計算,也可以根據API擷取); 3、制定規則,我們制定的規則是與中心坐标距離不超過1/4的,都算是中心區域; 4、其他的在各個角落的分别定義; 代碼實作如下:
package com.example.androiddetector_csdn;
import android.content.Context;
import android.graphics.Point;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
public class MatchXY {
String tag="me";
Context context;
View view;
public MatchXY(Context ct,View vw) {
// TODO Auto-generated constructor stub
context=ct;
view=vw;
}
public ResultStatus GetWhereAreYou(float x,float y)
{
WindowManager wm =(WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
//過時的方法
int width = wm.getDefaultDisplay().getWidth();
int height = wm.getDefaultDisplay().getHeight();
Log.e(tag, "deprecated--width:"+width+" height:"+height);
//level 13以上可用的方法
Point point=new Point();
wm.getDefaultDisplay().getSize(point);
int width_here=point.x;
int height_here=point.y;
Log.e(tag, "now--width:"+width_here+" height:"+height_here);
//取螢幕中心點的坐标
int center_x=width_here/2;
int center_y=height_here/2;
Log.e(tag, "center_x:"+center_x+" center_y:"+center_y);
//以與中心四分之一距離作為臨界點
int min_center_x=center_x-center_x/4;
int max_center_x=center_x+center_x/4;
int min_center_y=center_y-center_y/4;
int max_center_y=center_y+center_y/4;
//根據以上的範圍,将之連接配接起來,是一個四方的圍,在這個範圍内的touch,定義為中心點選
if((x>min_center_x&&x<max_center_x)&&(y>min_center_y&&y<max_center_y))
{
//在圍内
Log.e(tag, "中間區域:x:"+x+" y:"+y);
return ResultStatus.CENTER;
}
else if(x<center_x&&y<center_y)
{
//不在圍内
Log.e(tag, "左上區域:x:"+x+" y:"+y);
return ResultStatus.UP;
}
else if (x>center_x&&y<center_y) {
//不在圍内
Log.e(tag, "右上區域:x:"+x+" y:"+y);
return ResultStatus.UP;
}
else if (x>center_x&&y>center_y) {
//不在圍内
Log.e(tag, "右下區域:x:"+x+" y:"+y);
return ResultStatus.DOWN;
}
else if (x<center_x&&y>center_y) {
//不在圍内
Log.e(tag, "左下區域:x:"+x+" y:"+y);
return ResultStatus.DOWN;
}
else {
Log.e(tag, "未知區域:x:"+x+" y:"+y);
return null;
}
}
}
根據以上的代碼我們可以實作點選不同的區域做不同的操作。這樣在接口的實作中加入一些代碼,看效果:
package com.example.androiddetector_csdn;
import android.content.Context;
import android.util.Log;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
public class GuestureImp implements OnGestureListener{
Context context;
View view;
String tag="me";
String firstString="秦霸先,他是英雄的典範,武當派出身,早早習練“純陽功”。文武雙全,年輕時又名秦策,道号元沖,此人文武全才,當世英豪。二十四歲因一女子反出武當,自赴西北,後練成天山武學,自稱天下無敵。二十六歲中狀元,改名霸先。 因身為狀元卻又武藝淵深(“隻手便舉起殿前石獅子,縱躍飛奔如常”),初時在朝中無親無故。後受到武英帝賞識,與柳昂天平定也先有功,爵賜武德侯,官拜征西大都督,與柳昂天并稱“西霸先、北昂天”。武英十五年,武英帝禦駕親征失敗,他将武英帝藏入神機洞中,反遭奸人陷害,一家老小幾滅滿門,被迫率三萬将士造反,創立怒蒼山,立忠義堂,聚天下群豪,與景泰朝廷大戰十四年,後接受招安,慘死神鬼亭。傳下“戊辰歲終,龍皇動世,天機猶真,神鬼自在”四句偈語,與一張羊皮一起被稱為關系天下氣運。";
String secondString="盧雲,山東濰縣人,自幼父母雙亡,苦讀自學,學得一身經世緻用的好學問,卻不幸屢試不第,淪落到靠做酒肆店夥為生。在做酒肆店夥時為當地地痞陷害,又被貪官誣指為殺人犯,旦夕将死,适逢怒蒼山殘黨(太湖雙龍寨)劫獄救人,才得以脫困而出。逃獄之後,盧雲以拉纖為業,順運河而下直至揚州,在揚州入景泰朝大臣顧嗣源家為僮仆,後于一偶然機會(對聯)為顧所賞識,被網羅為顧府幕僚,嗣源獨生女顧倩兮亦對盧雲深有好感。同時,盧雲并獲得了武當派的練氣之法,以及怒蒼山殘黨陸孤瞻的拳法傳授,結合兩者,在武藝上自創無絕心法,後遂成武林心體氣術勢五大宗中(練)氣一派的大師。";
String thirdString="楊肅觀,楊遠之子,面目俊俏,玉樹臨風,心機深沉,貴氣逼人,潛龍的養子或親生子,少林天絕傳人,英雄志中身世最神秘的人。心地像神佛一樣柔軟的人,卻承擔了太多人的期望,以至于肩負了整個天下。作為替罪羔羊,曆經猜疑磨難,已經心碎,終于當斷則斷,殺出重圍,建立“鎮國鐵衛”,一舉締造佛國。身負“天訣”,手握“神劍”,馭“六道輪回”,一生費盡機心,以鐵血平天下,自比修羅王,由佛入魔。其才天下無匹,其機心,直逼潛龍,其胸懷野心,更始吞吐天下。但是也是以毀情滅欲,罪惡滔天。佛說,我不如地獄,誰入地獄。楊肅觀以一人入魔求天下太平,雖然滅絕人性,但仍不失為上上人物。";
public GuestureImp(Context ct,View vw) {
// TODO Auto-generated constructor stub
context=ct;
view=vw;
}
@Override
public boolean onDown(MotionEvent arg0) {
// TODO Auto-generated method stub
Log.e(tag, "down-"+"x:"+arg0.getX()+"y:"+arg0.getY());
MatchXY matchXY=new MatchXY(context,view);
ResultStatus resultStatus= matchXY.GetWhereAreYou(arg0.getX(),arg0.getY());
if(resultStatus.equals(ResultStatus.UP))
{
((TextView)view).setText(firstString);
}
else if(resultStatus.equals(ResultStatus.DOWN)){
((TextView)view).setText(secondString);
}
else if(resultStatus.equals(ResultStatus.CENTER)){
((TextView)view).setText(thirdString);
}
return true;
}
@Override
public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2,
float arg3) {
// TODO Auto-generated method stub
int mini_width=120;
int mini_speed=0;
float distance_right=arg1.getX()-arg0.getX();
float distance_left=arg0.getX()-arg1.getX();
float distance_down=arg1.getY()-arg0.getY();
float distance_up=arg0.getY()-arg1.getY();
if(distance_right>mini_width && Math.abs(arg2)>mini_speed)
{
Log.e(tag, "onFling-"+"向右滑動");
}
else if(distance_left>mini_width && Math.abs(arg2)>mini_speed)
{
Log.e(tag, "onFling-"+"向左滑動");
}
else if(distance_down>mini_width && Math.abs(arg2)>mini_speed)
{
Log.e(tag, "onFling-"+"向下滑動");
}
else if(distance_up>mini_width && Math.abs(arg2)>mini_speed)
{
Log.e(tag, "onFling-"+"向上滑動");
}
return true;
}
@Override
public void onLongPress(MotionEvent arg0) {
// TODO Auto-generated method stub
Log.e(tag, "onLongPress-"+"x:"+arg0.getX()+"y:"+arg0.getY());
}
@Override
public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2,
float arg3) {
// TODO Auto-generated method stub
Log.e(tag, "onScroll-"+"x:"+arg0.getX()+"y:"+arg0.getY());
return false;
}
@Override
public void onShowPress(MotionEvent arg0) {
// TODO Auto-generated method stub
Log.e(tag, "onShowPress-"+"x:"+arg0.getX()+"y:"+arg0.getY());
}
@Override
public boolean onSingleTapUp(MotionEvent arg0) {
// TODO Auto-generated method stub
Log.e(tag, "onSingleTapUp-"+"x:"+arg0.getX()+"y:"+arg0.getY());
return false;
}
}
效果如下:
點選不同區域,textview中内容發生變化,簡易版本翻譯實作。
源碼
源碼位于:http://download.csdn.net/detail/yangzhaomuma/9326441