用戶手勢檢測-GestureDetector使用詳解

1、概述

當用戶觸摸屏幕的時候,會產生許多手勢,例如down,up,scroll,filing等等。
通常狀況下,咱們知道View類有個View.OnTouchListener內部接口,經過重寫他的onTouch(View v, MotionEvent event)方法,咱們能夠處理一些touch事件,可是這個方法太過簡單,若是須要處理一些複雜的手勢,用這個接口就會很麻煩(由於咱們要本身根據用戶觸摸的軌跡去判斷是什麼手勢)。
Android sdk給咱們提供了GestureDetector(Gesture:手勢Detector:識別)類,經過這個類咱們能夠識別不少的手勢,主要是經過他的onTouchEvent(event)方法完成了不一樣手勢的識別。雖然他能識別手勢,可是不一樣的手勢要怎麼處理,應該是提供給程序員實現的。java

GestureDetector這個類對外提供了兩個接口和一個外部類
接口:OnGestureListener,OnDoubleTapListener
內部類:SimpleOnGestureListener

android

這個外部類,實際上是兩個接口中全部函數的集成,它包含了這兩個接口裏全部必需要實現的函數並且都已經重寫,但全部方法體都是空的;不一樣點在於:該類是static class,程序員能夠在外部繼承這個類,重寫裏面的手勢處理方法。程序員

下面咱們先看OnGestureListener接口;ide

2、GestureDetector.OnGestureListener---接口

一、基本講解

若是咱們寫一個類並implements OnGestureListener,會提示有幾個必須重寫的函數,加上以後是這個樣子的:函數

private class gesturelistener implements GestureDetector.OnGestureListener{

	public boolean onDown(MotionEvent e) {
		// TODO Auto-generated method stub
		return false;
	}

	public void onShowPress(MotionEvent e) {
		// TODO Auto-generated method stub
		
	}

	public boolean onSingleTapUp(MotionEvent e) {
		// TODO Auto-generated method stub
		return false;
	}

	public boolean onScroll(MotionEvent e1, MotionEvent e2,
			float distanceX, float distanceY) {
		// TODO Auto-generated method stub
		return false;
	}

	public void onLongPress(MotionEvent e) {
		// TODO Auto-generated method stub
		
	}

	public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
			float velocityY) {
		// TODO Auto-generated method stub
		return false;
	}
	
}

可見,這裏總共重寫了六個函數,這些函數都在什麼狀況下才會觸發呢,下面講一下:佈局

OnDown(MotionEvent e):用戶按下屏幕就會觸發;
onShowPress(MotionEvent e):若是是按下的時間超過瞬間,並且在按下的時候沒有鬆開或者是拖動的,那麼onShowPress就會執行,具體這個瞬間是多久,我也不清楚呃……
onLongPress(MotionEvent e):長按觸摸屏,超過必定時長,就會觸發這個事件
    觸發順序:
    onDown->onShowPress->onLongPress
this

onSingleTapUp(MotionEvent e):從名子也能夠看出,一次單獨的輕擊擡起操做,也就是輕擊一下屏幕,馬上擡起來,纔會有這個觸發,固然,若是除了Down之外還有其它操做,那就再也不算是Single操做了,因此也就不會觸發這個事件
    觸發順序:
    點擊一下很是快的(不滑動)Touchup:
    onDown->onSingleTapUp->onSingleTapConfirmed 
    點擊一下稍微慢點的(不滑動)Touchup:
    onDown->onShowPress->onSingleTapUp->onSingleTapConfirmedspa

onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) :滑屏,用戶按下觸摸屏、快速移動後鬆開,由1個MotionEvent ACTION_DOWN, 多個ACTION_MOVE, 1個ACTION_UP觸發   
     參數解釋:
    e1:第1個ACTION_DOWN MotionEvent
    e2:最後一個ACTION_MOVE MotionEvent
    velocityX:X軸上的移動速度,像素/秒
    velocityY:Y軸上的移動速度,像素/秒   
日誌

onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY):在屏幕上拖動事件。不管是用手拖動view,或者是以拋的動做滾動,都會屢次觸發,這個方法       在ACTION_MOVE動做發生時就會觸發
    滑屏:手指觸動屏幕後,稍微滑動後當即鬆開
    onDown-----》onScroll----》onScroll----》onScroll----》………----->onFling
    拖動
    onDown------》onScroll----》onScroll------》onFiling
code

    可見,不管是滑屏,仍是拖動,影響的只是中間OnScroll觸發的數量多少而已,最終都會觸發onFling事件!

二、實例

要使用GestureDetector,有三步要走:

一、建立OnGestureListener監聽函數:
可使用構造實例:

GestureDetector.OnGestureListener listener = new GestureDetector.OnGestureListener(){
			
		};

也能夠構造類:

private class gestureListener implements GestureDetector.OnGestureListener{
	
	}

二、建立GestureDetector實例mGestureDetector:

構造函數有下面三個,根據須要選擇:

GestureDetector gestureDetector=new GestureDetector(GestureDetector.OnGestureListener listener);
GestureDetector gestureDetector=new GestureDetector(Context context,GestureDetector.OnGestureListener listener);
GestureDetector gestureDetector=new GestureDetector(Context context,GestureDetector.SimpleOnGestureListener listener);

三、onTouch(View v, MotionEvent event)中攔截:

public boolean onTouch(View v, MotionEvent event) {
	return mGestureDetector.onTouchEvent(event);   
}

四、控件綁定

      TextView tv = (TextView)findViewById(R.id.tv);
      tv.setOnTouchListener(this);

如今進入實例階段:

首先,在主佈局頁面添加一個textView,並將其放大到整屏,方便在其上的手勢識別,代碼爲:

<RelativeLayout 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"
    tools:context="com.example.gesturedetectorinterface.MainActivity" >

    <TextView
        android:id="@+id/tv"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_margin="50dip"
        android:background="#ff00ff"
        android:text="@string/hello_world" />

</RelativeLayout>

而後在JAVA代碼中,依據上面的三步走原則,寫出代碼,並在全部的手勢下添加上Toast提示並寫上Log

public class MainActivity extends Activity implements OnTouchListener{

	private GestureDetector mGestureDetector;   
	

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
      mGestureDetector = new GestureDetector(new gestureListener()); //使用派生自OnGestureListener
        
      TextView tv = (TextView)findViewById(R.id.tv);
      tv.setOnTouchListener(this);
      tv.setFocusable(true);   
      tv.setClickable(true);   
      tv.setLongClickable(true); 
	}
	
	
	/* 
     * 在onTouch()方法中,咱們調用GestureDetector的onTouchEvent()方法,將捕捉到的MotionEvent交給GestureDetector 
     * 來分析是否有合適的callback函數來處理用戶的手勢 
     */  
	public boolean onTouch(View v, MotionEvent event) {
		return mGestureDetector.onTouchEvent(event);   
	}
	
	private class gestureListener implements GestureDetector.OnGestureListener{

		// 用戶輕觸觸摸屏,由1個MotionEvent ACTION_DOWN觸發   
		public boolean onDown(MotionEvent e) {
			Log.i("MyGesture", "onDown");   
	        Toast.makeText(MainActivity.this, "onDown", Toast.LENGTH_SHORT).show();   
			return false;
		}

		/*  
	     * 用戶輕觸觸摸屏,還沒有鬆開或拖動,由一個1個MotionEvent ACTION_DOWN觸發  
	     * 注意和onDown()的區別,強調的是沒有鬆開或者拖動的狀態  
	     * 
	     * 而onDown也是由一個MotionEventACTION_DOWN觸發的,可是他沒有任何限制,
	     * 也就是說當用戶點擊的時候,首先MotionEventACTION_DOWN,onDown就會執行,
	     * 若是在按下的瞬間沒有鬆開或者是拖動的時候onShowPress就會執行,若是是按下的時間超過瞬間
	     * (這塊我也不太清楚瞬間的時間差是多少,通常狀況下都會執行onShowPress),拖動了,就不執行onShowPress。
	     */
		public void onShowPress(MotionEvent e) {
			Log.i("MyGesture", "onShowPress");   
	        Toast.makeText(MainActivity.this, "onShowPress", Toast.LENGTH_SHORT).show();   
		}

		// 用戶(輕觸觸摸屏後)鬆開,由一個1個MotionEvent ACTION_UP觸發   
		///輕擊一下屏幕,馬上擡起來,纔會有這個觸發
		//從名子也能夠看出,一次單獨的輕擊擡起操做,固然,若是除了Down之外還有其它操做,那就再也不算是Single操做了,因此這個事件 就再也不響應
		public boolean onSingleTapUp(MotionEvent e) {
			Log.i("MyGesture", "onSingleTapUp");   
	        Toast.makeText(MainActivity.this, "onSingleTapUp", Toast.LENGTH_SHORT).show();   
	        return true;   
		}

		// 用戶按下觸摸屏,並拖動,由1個MotionEvent ACTION_DOWN, 多個ACTION_MOVE觸發   
		public boolean onScroll(MotionEvent e1, MotionEvent e2,
				float distanceX, float distanceY) {
			Log.i("MyGesture22", "onScroll:"+(e2.getX()-e1.getX()) +"   "+distanceX);   
	        Toast.makeText(MainActivity.this, "onScroll", Toast.LENGTH_LONG).show();   
	        
	        return true;   
		}

		// 用戶長按觸摸屏,由多個MotionEvent ACTION_DOWN觸發   
		public void onLongPress(MotionEvent e) {
			 Log.i("MyGesture", "onLongPress");   
		     Toast.makeText(MainActivity.this, "onLongPress", Toast.LENGTH_LONG).show();   
		}

		// 用戶按下觸摸屏、快速移動後鬆開,由1個MotionEvent ACTION_DOWN, 多個ACTION_MOVE, 1個ACTION_UP觸發   
		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
				float velocityY) {
			Log.i("MyGesture", "onFling");   
	        Toast.makeText(MainActivity.this, "onFling", Toast.LENGTH_LONG).show();   
			return true;
		}
	};
	

}

3、GestureDetector.OnDoubleTapListener---接口

有兩種方式設置雙擊監聽:

方法一:新建一個類同時派生自OnGestureListener和OnDoubleTapListener:

private class gestureListener implements GestureDetector.OnGestureListener,GestureDetector.OnDoubleTapListener{
	}

方法二:使用GestureDetector::setOnDoubleTapListener();函數設置監聽:

//構建GestureDetector實例	
mGestureDetector = new GestureDetector(new gestureListener()); //使用派生自OnGestureListener
private class gestureListener implements GestureDetector.OnGestureListener{
	
}

//設置雙擊監聽器
mGestureDetector.setOnDoubleTapListener(new doubleTapListener());
private class doubleTapListener implements GestureDetector.OnDoubleTapListener{
	
}

注意:你們能夠看到不管在方法一仍是在方法二中,都須要派生自GestureDetector.OnGestureListener,前面咱們說過GestureDetector 的構造函數,以下:

GestureDetector gestureDetector=new GestureDetector(GestureDetector.OnGestureListener listener);
GestureDetector gestureDetector=new GestureDetector(Context context,GestureDetector.OnGestureListener listener);
GestureDetector gestureDetector=new GestureDetector(Context context,GestureDetector.SimpleOnGestureListener listener);

能夠看到,在構造函數中,除了後面要講的SimpleOnGestureListener 之外的其它兩個構造函數都必須是OnGestureListener的實例。因此要想使用OnDoubleTapListener的幾個函數,就必須先實現OnGestureListener。

二、函數講解:

首先看一下OnDoubleTapListener接口必須重寫的三個函數:

private class doubleTapListener implements GestureDetector.OnDoubleTapListener{

	public boolean onSingleTapConfirmed(MotionEvent e) {
		// TODO Auto-generated method stub
		return false;
	}

	public boolean onDoubleTap(MotionEvent e) {
		// TODO Auto-generated method stub
		return false;
	}

	public boolean onDoubleTapEvent(MotionEvent e) {
		// TODO Auto-generated method stub
		return false;
	}
}

onSingleTapConfirmed(MotionEvent e):單擊事件。用來斷定該次點擊是SingleTap而不是DoubleTap,若是連續點擊兩次就是DoubleTap手勢,若是隻點擊一次,系統等待一段時間後沒有收到第二次點擊則斷定該次點擊爲SingleTap而不是DoubleTap,而後觸發SingleTapConfirmed事件。觸發順序是:OnDown->OnsingleTapUp->OnsingleTapConfirmed
關於onSingleTapConfirmed和onSingleTapUp的一點區別: OnGestureListener有這樣的一個方法onSingleTapUp,和onSingleTapConfirmed容易混淆。兩者的區別是:onSingleTapUp,只要手擡起就會執行,而對於onSingleTapConfirmed來講,若是雙擊的話,則onSingleTapConfirmed不會執行。

onDoubleTap(MotionEvent e):雙擊事件

onDoubleTapEvent(MotionEvent e):雙擊間隔中發生的動做。指觸發onDoubleTap之後,在雙擊之間發生的其它動做,包含down、up和move事件;下圖是雙擊一下的Log輸出:

兩點總結:

一、從上圖能夠看出,在第二下點擊時,先觸發OnDoubleTap,而後再觸發OnDown(第二次點擊)

二、其次在觸發OnDoubleTap之後,就開始觸發onDoubleTapEvent了,onDoubleTapEvent後面的數字表明瞭當前的事件,0指ACTION_DOWN,1指ACTION_UP,2 指ACTION_MOVE
在上一個例子的基礎上,咱們再添加一個雙擊監聽類,實現以下:

public class MainActivity extends Activity implements OnTouchListener{

	private GestureDetector mGestureDetector;   
	

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		

      mGestureDetector = new GestureDetector(new gestureListener()); //使用派生自OnGestureListener
      mGestureDetector.setOnDoubleTapListener(new doubleTapListener());
        
      TextView tv = (TextView)findViewById(R.id.tv);
      tv.setOnTouchListener(this);
      tv.setFocusable(true);   
      tv.setClickable(true);   
      tv.setLongClickable(true); 
	}
	
	
	/* 
     * 在onTouch()方法中,咱們調用GestureDetector的onTouchEvent()方法,將捕捉到的MotionEvent交給GestureDetector 
     * 來分析是否有合適的callback函數來處理用戶的手勢 
     */  
	public boolean onTouch(View v, MotionEvent event) {
		return mGestureDetector.onTouchEvent(event);   
	}
	
	//OnGestureListener監聽
	private class gestureListener implements GestureDetector.OnGestureListener{

		public boolean onDown(MotionEvent e) {
			Log.i("MyGesture", "onDown");   
	        Toast.makeText(MainActivity.this, "onDown", Toast.LENGTH_SHORT).show();   
			return false;
		}

		public void onShowPress(MotionEvent e) {
			Log.i("MyGesture", "onShowPress");   
	        Toast.makeText(MainActivity.this, "onShowPress", Toast.LENGTH_SHORT).show();   
		}

		public boolean onSingleTapUp(MotionEvent e) {
			Log.i("MyGesture", "onSingleTapUp");   
	        Toast.makeText(MainActivity.this, "onSingleTapUp", Toast.LENGTH_SHORT).show();   
	        return true;   
		}

		public boolean onScroll(MotionEvent e1, MotionEvent e2,
				float distanceX, float distanceY) {
			Log.i("MyGesture22", "onScroll:"+(e2.getX()-e1.getX()) +"   "+distanceX);   
	        Toast.makeText(MainActivity.this, "onScroll", Toast.LENGTH_LONG).show();   
	        
	        return true;   
		}

		public void onLongPress(MotionEvent e) {
			 Log.i("MyGesture", "onLongPress");   
		     Toast.makeText(MainActivity.this, "onLongPress", Toast.LENGTH_LONG).show();   
		}

		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
				float velocityY) {
			Log.i("MyGesture", "onFling");   
	        Toast.makeText(MainActivity.this, "onFling", Toast.LENGTH_LONG).show();   
			return true;
		}
	};
	
	//OnDoubleTapListener監聽
	private class doubleTapListener implements GestureDetector.OnDoubleTapListener{

		public boolean onSingleTapConfirmed(MotionEvent e) {
			Log.i("MyGesture", "onSingleTapConfirmed");   
	        Toast.makeText(MainActivity.this, "onSingleTapConfirmed", Toast.LENGTH_LONG).show();  
			return true;
		}

		public boolean onDoubleTap(MotionEvent e) {
			Log.i("MyGesture", "onDoubleTap");   
	        Toast.makeText(MainActivity.this, "onDoubleTap", Toast.LENGTH_LONG).show();  
			return true;
		}

		public boolean onDoubleTapEvent(MotionEvent e) {
			Log.i("MyGesture", "onDoubleTapEvent");   
	        Toast.makeText(MainActivity.this, "onDoubleTapEvent", Toast.LENGTH_LONG).show();  
			return true;
		}
	};
}

4、GestureDetector.SimpleOnGestureListener---類

它與前兩個不一樣的是:
一、這是一個類,在它基礎上新建類的話,要用extends派生而不是用implements繼承!
二、OnGestureListener和OnDoubleTapListener接口裏的函數都是強制必須重寫的,即便用不到也要重寫出來一個空函數但在SimpleOnGestureListener類的實例或派生類中沒必要如此,能夠根據狀況,用到哪一個函數就重寫哪一個函數,由於SimpleOnGestureListener類自己已經實現了這兩個接口的全部函數,只是裏面全是空的而已。

下面利用SimpleOnGestureListener類來從新實現上面的幾個效果,代碼以下:

public class MainActivity extends Activity implements OnTouchListener {

	private GestureDetector mGestureDetector;   
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		mGestureDetector = new GestureDetector(new simpleGestureListener());
		
		TextView tv = (TextView)findViewById(R.id.tv);
	    tv.setOnTouchListener(this);
	    tv.setFocusable(true);   
	    tv.setClickable(true);   
	    tv.setLongClickable(true); 
	}
	
	public boolean onTouch(View v, MotionEvent event) {
		// TODO Auto-generated method stub
		return mGestureDetector.onTouchEvent(event);   
	}

	private class simpleGestureListener extends
			GestureDetector.SimpleOnGestureListener {
		
		/*****OnGestureListener的函數*****/
		public boolean onDown(MotionEvent e) {
			Log.i("MyGesture", "onDown");
			Toast.makeText(MainActivity.this, "onDown", Toast.LENGTH_SHORT)
					.show();
			return false;
		}

		public void onShowPress(MotionEvent e) {
			Log.i("MyGesture", "onShowPress");
			Toast.makeText(MainActivity.this, "onShowPress", Toast.LENGTH_SHORT)
					.show();
		}

		public boolean onSingleTapUp(MotionEvent e) {
			Log.i("MyGesture", "onSingleTapUp");
			Toast.makeText(MainActivity.this, "onSingleTapUp",
					Toast.LENGTH_SHORT).show();
			return true;
		}

		public boolean onScroll(MotionEvent e1, MotionEvent e2,
				float distanceX, float distanceY) {
			Log.i("MyGesture", "onScroll:" + (e2.getX() - e1.getX()) + "   "
					+ distanceX);
			Toast.makeText(MainActivity.this, "onScroll", Toast.LENGTH_LONG)
					.show();

			return true;
		}

		public void onLongPress(MotionEvent e) {
			Log.i("MyGesture", "onLongPress");
			Toast.makeText(MainActivity.this, "onLongPress", Toast.LENGTH_LONG)
					.show();
		}

		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
				float velocityY) {
			Log.i("MyGesture", "onFling");
			Toast.makeText(MainActivity.this, "onFling", Toast.LENGTH_LONG)
					.show();
			return true;
		}
		
		/*****OnDoubleTapListener的函數*****/
		public boolean onSingleTapConfirmed(MotionEvent e) {
			Log.i("MyGesture", "onSingleTapConfirmed");
			Toast.makeText(MainActivity.this, "onSingleTapConfirmed",
					Toast.LENGTH_LONG).show();
			return true;
		}

		public boolean onDoubleTap(MotionEvent e) {
			Log.i("MyGesture", "onDoubleTap");
			Toast.makeText(MainActivity.this, "onDoubleTap", Toast.LENGTH_LONG)
					.show();
			return true;
		}

		public boolean onDoubleTapEvent(MotionEvent e) {
			Log.i("MyGesture", "onDoubleTapEvent");
			Toast.makeText(MainActivity.this, "onDoubleTapEvent",
					Toast.LENGTH_LONG).show();
			return true;
		}

	}
}

到此,有關GestureDetector的全部基礎知識都講解完了,下面給出一個小應用——識別用戶是向左滑仍是向右滑!

5、OnFling應用——識別向左滑仍是向右滑

這部分就有點意思了,能夠說是上面知識的一個小應用,咱們利用OnFling函數來識別當前用戶是在向左滑仍是向右滑,從而打出日誌。先看下OnFling的參數:

boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY)
參數解釋:   
e1:第1個ACTION_DOWN MotionEvent   
e2:最後一個ACTION_MOVE MotionEvent   
velocityX:X軸上的移動速度,像素/秒   
velocityY:Y軸上的移動速度,像素/秒

首先,先說一下實現的功能:當用戶向左滑動距離超過100px,且滑動速度超過100 px/s時,即判斷爲向左滑動;向右同理.代碼以下:

public class MainActivity extends Activity implements OnTouchListener {

	private GestureDetector mGestureDetector;   
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		mGestureDetector = new GestureDetector(new simpleGestureListener());
		
		TextView tv = (TextView)findViewById(R.id.tv);
	    tv.setOnTouchListener(this);
	    tv.setFocusable(true);   
	    tv.setClickable(true);   
	    tv.setLongClickable(true); 
	}
	
	public boolean onTouch(View v, MotionEvent event) {
		// TODO Auto-generated method stub
		return mGestureDetector.onTouchEvent(event);   
	}

	private class simpleGestureListener extends
			GestureDetector.SimpleOnGestureListener {
		
		/*****OnGestureListener的函數*****/

		final int FLING_MIN_DISTANCE = 100, FLING_MIN_VELOCITY = 200;  
		
		// 觸發條件 :   
        // X軸的座標位移大於FLING_MIN_DISTANCE,且移動速度大於FLING_MIN_VELOCITY個像素/秒   
       
		// 參數解釋:   
        // e1:第1個ACTION_DOWN MotionEvent   
        // e2:最後一個ACTION_MOVE MotionEvent   
        // velocityX:X軸上的移動速度,像素/秒   
        // velocityY:Y軸上的移動速度,像素/秒   
		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
				float velocityY) {
			
	        
	        if (e1.getX() - e2.getX() > FLING_MIN_DISTANCE  
	                && Math.abs(velocityX) > FLING_MIN_VELOCITY) {  
	            // Fling left   
	            Log.i("MyGesture", "Fling left");  
	            Toast.makeText(MainActivity.this, "Fling Left", Toast.LENGTH_SHORT).show();  
	        } else if (e2.getX() - e1.getX() > FLING_MIN_DISTANCE  
	                && Math.abs(velocityX) > FLING_MIN_VELOCITY) {  
	            // Fling right   
	            Log.i("MyGesture", "Fling right");  
	            Toast.makeText(MainActivity.this, "Fling Right", Toast.LENGTH_SHORT).show();  
	        }  
			return true;
		}

	}
}
相關文章
相關標籤/搜索