1. 程式人生 > >Android手勢識別——上下左右滑動、螢幕上下左右中區域處理

Android手勢識別——上下左右滑動、螢幕上下左右中區域處理

手勢識別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