android怎么监听多点触摸_Android笔记:触摸事件的分析与总结----多点触控
其他相關(guān)博文:
一、多點觸控
當(dāng)多點同時觸摸屏幕時,系統(tǒng)將會產(chǎn)生如下的觸摸事件:
1.ACTION_DOWN:觸摸屏幕的第一個點。此時手勢開始。該點的數(shù)據(jù)通常在MotionEvent事件隊列索引位置0處。
2.ACTION_POINTER_DOWN:除了第一個點的其他觸摸點數(shù)據(jù)。該點的數(shù)據(jù)的索引位置由getActionIndex()方法返回。
3.ACTION_MOVE:在手勢過程中發(fā)生的一次變化。
4.ACTION_POINTER_UP:當(dāng)不是第一個點的其他點UP后觸發(fā)。
5.ACTION_UP:當(dāng)手勢中的最后一個點離開屏幕。
簡單測試范例
布局xml代碼如下:
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:tag="true布局"
tools:context=".MainActivity"?>
android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tag="true控件"
android:text="@string/hello_world"?/>
java代碼如下:package?com.lonshine.d_touchduodian;
import?android.os.Bundle;
import?android.app.Activity;
import?android.util.Log;
import?android.view.MotionEvent;
import?android.view.View;
import?android.view.View.OnTouchListener;
import?android.widget.RelativeLayout;
public?class?MainActivity?extends?Activity?implements?OnTouchListener
{
@Override
protected?void?onCreate(Bundle?savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RelativeLayout?layout?=?(RelativeLayout)?findViewById(R.id.layout1);
layout.setOnTouchListener(this);
}
@Override
public?boolean?onTouch(View?v,?MotionEvent?event)
{
String?tag?=?v.getTag().toString();
Log.e(tag,?"**********************************************");
Log.e(tag,?"**********************************************");
Log.e(tag,?"觸摸的是:"?+?tag);
Log.e(tag,?describeEvent(event));
logAction(event);
Log.e(tag,?"**********************************************");
Log.e(tag,?"**********************************************");
Log.e("",?"????????????????????????");
Log.e("",?"????????????????????????");
if("true".equals(tag.substring(0,?4)))
{
return?true;
}
else
{
return?false;
}
}
protected?static?String?describeEvent(MotionEvent?event)
{
StringBuilder?sb?=?new?StringBuilder(500);
sb.append("Action:?").append(event.getAction()).append("\n");//?獲取觸控動作比如ACTION_DOWN
int?count?=?event.getPointerCount();
sb.append("觸點個數(shù):?").append(count).append("\n");
int?i?=?0;
while?(i?
{
sb.append("-------------------------").append("\n");
int?pointerId?=?event.getPointerId(i);
sb.append("觸點序號:?").append(i).append("\n");
sb.append("觸點ID?:?").append(pointerId).append("\n");
sb.append("相對坐標(biāo):?").append(event.getX(i)).append("??*??").append(event.getY(i)).append("\n");
sb.append("壓力???????:?").append(event.getPressure(i)).append("\n");//?壓力值,0-1之間,看情況,很可能始終返回1
sb.append("范圍大小:?").append(event.getSize(i)).append("\n");//?指壓范圍
i++;
sb.append("-------------------------").append("\n");
}
sb.append("按下時間:?").append(event.getDownTime()).append("ms???");
sb.append("結(jié)束時間:?").append(event.getEventTime()).append("ms???");
sb.append("運行時間:?").append(event.getEventTime()?-?event.getDownTime()).append("ms\n");
return?sb.toString();
}
private?void?logAction(MotionEvent?event)
{
int?masked?=?event.getActionMasked();
int?index?=?event.getActionIndex();
int?pointerId?=?event.getPointerId(index);
if(masked?==?5?||?masked?==?6)
{
masked?=?masked?-?5;
}
Log.e("Action",?"觸點序號:?"?+?index);
Log.e("Action",?"觸點ID?:??"?+?pointerId);
Log.e("Action",?"ActionMasked?:"?+?masked);
}
}
多點觸控的日志打印出來太多,此處僅針對日志輸出作簡單分析:
A.按下第一根手指時,獲得一個索引為0且指針I(yè)D為0的指針(ACTION_DOWN = 0);
B.接下去action輸出為2(ACTION_MOVE),此時仍然僅有一個指針,并且索引和ID都為0;
C.然后按下第二根手指,action值輸出為261。此值由兩部分組成:一個是指針?biāo)饕?#xff0c;一個是指針正在執(zhí)行何種操作;
D.將十進制261轉(zhuǎn)換為十六進制數(shù)為0x00000105。其中01代表指針?biāo)饕?#xff0c;05代表操作值(即ACTION_POINTER_DOWN的值,用于多點觸摸場景);
E.依此類推,當(dāng)按下第三根手指時action輸出值為0x00000205(十進制517),第四根則為0x000305(十進制773);
F.當(dāng)?shù)谝桓种鸽x開屏幕時,action值為0x00000006(十進制6),即索引為0,操作值為6(即ACTION_POINTER_UP);
G.如果此時第二根手指離開時,action值應(yīng)該為0x00000106(十進制262),因為此時仍然擁有兩根手指的信息;
H.而實際結(jié)果并沒有得到262的action值,這是因為當(dāng)?shù)谝桓种鸽x開屏幕后,第二根手指的索引從1更改成了0,但是指針I(yè)D仍然為1.
I.對于每一次移動,action值將始終為2,因為ACTION_MOVE事件并沒有包含哪根手指在移動的信息。
附:依次按下四根手指的日志
其他小結(jié):
(1)getPointerCount() 獲得觸屏的點數(shù),
getActionIndex()獲得觸點的指針?biāo)饕?#xff0c;
getPointerId(index)獲得指針?biāo)饕龑?yīng)的觸點ID;
(2)getActionMasked()表示用于多點觸控檢測點。而在1.6和2.1中并沒有event.getActionMasked()這個方法,其實他就是把event.getAction()& MotionEvent.ACTION_MASK封裝了一下。
二、其他參考范例:
(一)單點觸摸拖動圖片與多點觸摸縮放圖片package?com.lonshine.touchdemo;
import?android.app.Activity;
import?android.content.Context;
import?android.graphics.*;
import?android.graphics.drawable.BitmapDrawable;
import?android.os.Bundle;
import?android.view.MotionEvent;
import?android.widget.ImageView;
import?android.widget.ImageView.ScaleType;
/**
*?多點觸控來控制ImageView中圖像的放大與縮小,?單點觸控則用來拖動圖片
*
*?@author?zeng
*
*/
public?class?MainActivity?extends?Activity
{
private?MyImageView?p_w_picpathView;
private?Bitmap?bitmap;
//?兩點觸屏后之間的長度
private?float?beforeLenght;
private?float?afterLenght;
//?單點移動的前后坐標(biāo)值
private?float?afterX,?afterY;
private?float?beforeX,?beforeY;
/**?Called?when?the?activity?is?first?created.?*/
@Override
public?void?onCreate(Bundle?savedInstanceState)
{
super.onCreate(savedInstanceState);
findView();
setContentView(p_w_picpathView);
config();
}
private?void?findView()
{
p_w_picpathView?=?new?MyImageView(this);
//?獲得圖片
bitmap?=?((BitmapDrawable)?getResources().getDrawable(R.drawable.xing)).getBitmap();
}
private?void?config()
{
//?設(shè)置p_w_picpathView的顯示圖片
p_w_picpathView.setImageBitmap(bitmap);
//?設(shè)置圖片填充ImageView
p_w_picpathView.setScaleType(ScaleType.FIT_XY);
}
//?創(chuàng)建一個自己的ImageView類
class?MyImageView?extends?ImageView
{
private?float?scale?=?0.1f;
public?MyImageView(Context?context)
{
super(context);
}
//?用來設(shè)置ImageView的位置
private?void?setLocation(int?x,?int?y)
{
this.setFrame(this.getLeft()?+?x,?this.getTop()?+?y,?this.getRight()?+?x,?this.getBottom()?+?y);
}
/**
*?用來放大縮小ImageView?因為圖片是填充ImageView的,所以也就有放大縮小圖片的效果?flag為0是放大圖片,為1是縮小圖片
*/
private?void?setScale(float?temp,?int?flag)
{
if?(flag?==?0)
{
this.setFrame(this.getLeft()?-?(int)?(temp?*?this.getWidth()),?this.getTop()?-?(int)?(temp?*?this.getHeight()),?this.getRight()
+?(int)?(temp?*?this.getWidth()),?this.getBottom()?+?(int)?(temp?*?this.getHeight()));
}
else
{
this.setFrame(this.getLeft()?+?(int)?(temp?*?this.getWidth()),?this.getTop()?+?(int)?(temp?*?this.getHeight()),?this.getRight()
-?(int)?(temp?*?this.getWidth()),?this.getBottom()?-?(int)?(temp?*?this.getHeight()));
}
}
//?繪制邊框
@Override
protected?void?onDraw(Canvas?canvas)
{
super.onDraw(canvas);
Rect?rec?=?canvas.getClipBounds();
rec.bottom--;
rec.right--;
Paint?paint?=?new?Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(rec,?paint);
}
/**
*?讓圖片跟隨手指觸屏的位置移動?beforeX、Y是用來保存前一位置的坐標(biāo)?afterX、Y是用來保存當(dāng)前位置的坐標(biāo)
*?它們的差值就是ImageView各坐標(biāo)的增加或減少值
*/
public?void?moveWithFinger(MotionEvent?event)
{
switch?(event.getAction())
{
case?MotionEvent.ACTION_DOWN:
beforeX?=?event.getX();
beforeY?=?event.getY();
break;
case?MotionEvent.ACTION_MOVE:
afterX?=?event.getX();
afterY?=?event.getY();
this.setLocation((int)?(afterX?-?beforeX),?(int)?(afterY?-?beforeY));
beforeX?=?afterX;
beforeY?=?afterY;
break;
case?MotionEvent.ACTION_UP:
break;
}
}
/**
*?通過多點觸屏放大或縮小圖像?beforeLenght用來保存前一時間兩點之間的距離?afterLenght用來保存當(dāng)前時間兩點之間的距離
*/
public?void?scaleWithFinger(MotionEvent?event)
{
float?moveX?=?event.getX(1)?-?event.getX(0);
float?moveY?=?event.getY(1)?-?event.getY(0);
switch?(event.getAction())
{
case?MotionEvent.ACTION_DOWN:
beforeLenght?=?(float)?Math.sqrt((moveX?*?moveX)?+?(moveY?*?moveY));
break;
case?MotionEvent.ACTION_MOVE:
//?得到兩個點之間的長度
afterLenght?=?(float)?Math.sqrt((moveX?*?moveX)?+?(moveY?*?moveY));
float?gapLenght?=?afterLenght?-?beforeLenght;
if?(gapLenght?==?0)
{
break;
}
//?如果當(dāng)前時間兩點距離大于前一時間兩點距離,則傳0,否則傳1
if?(gapLenght?>?0)
{
this.setScale(scale,?0);
}
else
{
this.setScale(scale,?1);
}
beforeLenght?=?afterLenght;
break;
}
}
}
//?這里來監(jiān)聽屏幕觸控時間
@Override
public?boolean?onTouchEvent(MotionEvent?event)
{
/**
*?判定用戶是否觸摸到了圖片?如果是單點觸摸則調(diào)用控制圖片移動的方法?如果是2點觸控則調(diào)用控制圖片大小的方法
*/
if?(event.getY()?>?p_w_picpathView.getTop()?&&?event.getY()??p_w_picpathView.getLeft()
&&?event.getX()?
{
if?(event.getPointerCount()?==?2)
{
p_w_picpathView.scaleWithFinger(event);
}
else?if?(event.getPointerCount()?==?1)
{
p_w_picpathView.moveWithFinger(event);
}
}
return?true;
}
}
范例參考自:http://blog.csdn.net/ldj299/article/details/6422547
(二)實現(xiàn)多點消息攔截,用于多個物體拖動等效果package?com.lonshine.d_touchdemo;
import?java.util.ArrayList;
import?java.util.List;
import?android.content.Context;
import?android.os.Handler;
import?android.os.Message;
import?android.view.MotionEvent;
import?android.view.VelocityTracker;
import?android.view.ViewConfiguration;
/**
*?實現(xiàn)多點消息攔截,用于多個物體拖動等效果
*?@author?zeng
*/
public?class?MultiTouchGestureDetector
{
@SuppressWarnings("unused")
private?static?final?String?MYTAG?=?"Alan";
public?static?final?String?CLASS_NAME?=?"MultiTouchGestureDetector";
/**
*?事件信息類?
*?用來記錄一個手勢
*/
private?class?EventInfo
{
private?MultiMotionEvent?mCurrentDownEvent;?//?當(dāng)前的down事件
private?MultiMotionEvent?mPreviousUpEvent;?//?上一次up事件
private?boolean?mStillDown;?//?當(dāng)前手指是否還在屏幕上
private?boolean?mInLongPress;?//?當(dāng)前事件是否屬于長按手勢
private?boolean?mAlwaysInTapRegion;?//?是否當(dāng)前手指僅在小范圍內(nèi)移動,當(dāng)手指僅在小范圍內(nèi)移動時,視為手指未曾移動過,不會觸發(fā)onScroll手勢
private?boolean?mAlwaysInBiggerTapRegion;?//?是否當(dāng)前手指在較大范圍內(nèi)移動,僅當(dāng)此值為true時,雙擊手勢才能成立
private?boolean?mIsDoubleTapping;?//?當(dāng)前手勢,是否為雙擊手勢
private?float?mLastMotionY;?//?最后一次事件的X坐標(biāo)
private?float?mLastMotionX;?//?最后一次事件的Y坐標(biāo)
private?EventInfo(MotionEvent?e)
{
this(new?MultiMotionEvent(e));
}
private?EventInfo(MultiMotionEvent?me)
{
mCurrentDownEvent?=?me;
mStillDown?=?true;
mInLongPress?=?false;
mAlwaysInTapRegion?=?true;
mAlwaysInBiggerTapRegion?=?true;
mIsDoubleTapping?=?false;
}
//?釋放MotionEven對象,使系統(tǒng)能夠繼續(xù)使用它們
public?void?recycle()
{
if?(mCurrentDownEvent?!=?null)
{
mCurrentDownEvent.recycle();
mCurrentDownEvent?=?null;
}
if?(mPreviousUpEvent?!=?null)
{
mPreviousUpEvent.recycle();
mPreviousUpEvent?=?null;
}
}
@Override
public?void?finalize()
{
this.recycle();
}
}
/**
*?多點事件類?
*?將一個多點事件拆分為多個單點事件,并方便獲得事件的絕對坐標(biāo)?
*?絕對坐標(biāo)用以在界面中找到觸點所在的控件
*
*?@author?ray-ni
*/
public?class?MultiMotionEvent
{
private?MotionEvent?mEvent;
private?int?mIndex;
private?MultiMotionEvent(MotionEvent?e)
{
mEvent?=?e;
mIndex?=?(e.getAction()?&?MotionEvent.ACTION_POINTER_ID_MASK)?>>?MotionEvent.ACTION_POINTER_ID_SHIFT;?//?等效于
//?mEvent.getActionIndex();
}
private?MultiMotionEvent(MotionEvent?e,?int?idx)
{
mEvent?=?e;
mIndex?=?idx;
}
//?行為
public?int?getAction()
{
int?action?=?mEvent.getAction()?&?MotionEvent.ACTION_MASK;?//?等效于
//?mEvent.getActionMasked();
switch?(action)
{
case?MotionEvent.ACTION_POINTER_DOWN:
action?=?MotionEvent.ACTION_DOWN;
break;
case?MotionEvent.ACTION_POINTER_UP:
action?=?MotionEvent.ACTION_UP;
break;
}
return?action;
}
//?返回X的絕對坐標(biāo)
public?float?getX()
{
return?mEvent.getX(mIndex)?+?mEvent.getRawX()?-?mEvent.getX();
}
//?返回Y的絕對坐標(biāo)
public?float?getY()
{
return?mEvent.getY(mIndex)?+?mEvent.getRawY()?-?mEvent.getY();
}
//?事件發(fā)生的時間
public?long?getEventTime()
{
return?mEvent.getEventTime();
}
//?事件序號
public?int?getIndex()
{
return?mIndex;
}
//?事件ID
public?int?getId()
{
return?mEvent.getPointerId(mIndex);
}
//?釋放事件對象,使系統(tǒng)能夠繼續(xù)使用
public?void?recycle()
{
if?(mEvent?!=?null)
{
mEvent.recycle();
mEvent?=?null;
}
}
}
//?多點手勢監(jiān)聽器
public?interface?MultiTouchGestureListener
{
//?手指觸碰到屏幕,由一個?ACTION_DOWN觸發(fā)
boolean?onDown(MultiMotionEvent?e);
//?確定一個press事件,強調(diào)手指按下的一段時間(TAP_TIMEOUT)內(nèi),手指未曾移動或抬起
void?onShowPress(MultiMotionEvent?e);
//?手指點擊屏幕后離開,由?ACTION_UP引發(fā),可以簡單的理解為單擊事件,即手指點擊時間不長(未構(gòu)成長按事件),也不曾移動過
boolean?onSingleTapUp(MultiMotionEvent?e);
//?長按,手指點下后一段時間(DOUBLE_TAP_TIMEOUT)內(nèi),不曾抬起或移動
void?onLongPress(MultiMotionEvent?e);
//?拖動,由ACTION_MOVE觸發(fā),手指地按下后,在屏幕上移動
boolean?onScroll(MultiMotionEvent?e1,?MultiMotionEvent?e2,?float?distanceX,?float?distanceY);
//?滑動,由ACTION_UP觸發(fā),手指按下并移動一段距離后,抬起時觸發(fā)
boolean?onFling(MultiMotionEvent?e1,?MultiMotionEvent?e2,?float?velocityX,?float?velocityY);
}
//?多點雙擊監(jiān)聽器
public?interface?MultiTouchDoubleTapListener
{
//?單擊事件確認,強調(diào)第一個單擊事件發(fā)生后,一段時間內(nèi),未發(fā)生第二次單擊事件,即確定不會觸發(fā)雙擊事件
boolean?onSingleTapConfirmed(MultiMotionEvent?e);
//?雙擊事件,
//?由ACTION_DOWN觸發(fā),從第一次單擊事件的DOWN事件開始的一段時間(DOUBLE_TAP_TIMEOUT)內(nèi)結(jié)束(即手指),
//?并且在第一次單擊事件的UP時間開始后的一段時間內(nèi)(DOUBLE_TAP_TIMEOUT)發(fā)生第二次單擊事件,
//?除此之外兩者坐標(biāo)間距小于定值(DOUBLE_TAP_SLAP)時,則觸發(fā)雙擊事件
boolean?onDoubleTap(MultiMotionEvent?e);
//?雙擊事件,與onDoubleTap事件不同之處在于,構(gòu)成雙擊的第二次點擊的ACTION_DOWN,ACTION_MOVE和ACTION_UP都會觸發(fā)該事件
boolean?onDoubleTapEvent(MultiMotionEvent?e);
}
//?事件信息隊列,隊列的下標(biāo)與MotionEvent的pointId對應(yīng)
private?static?List?sEventInfos?=?new?ArrayList(10);
//?雙擊判斷隊列,這個隊列中的元素等待雙擊超時的判斷結(jié)果
private?static?List?sEventForDoubleTap?=?new?ArrayList(5);
//?指定大點擊區(qū)域的大小(這個比較拗口),這個值主要用于幫助判斷雙擊是否成立
private?int?mBiggerTouchSlopSquare?=?20?*?20;
//?判斷是否構(gòu)成onScroll手勢,當(dāng)手指在這個范圍內(nèi)移動時,不觸發(fā)onScroll手勢
private?int?mTouchSlopSquare;
//?判斷是否構(gòu)成雙擊,只有兩次點擊的距離小于該值,才能構(gòu)成雙擊手勢
private?int?mDoubleTapSlopSquare;
//?最小滑動速度
private?int?mMinimumFlingVelocity;
//?最大滑動速度
private?int?mMaximumFlingVelocity;
//?長按閥值,當(dāng)手指按下后,在該閥值的時間內(nèi),未移動超過mTouchSlopSquare的距離并未抬起,則長按手勢觸發(fā)
private?static?final?int?LONGPRESS_TIMEOUT?=?ViewConfiguration.getLongPressTimeout();
//?showPress手勢的觸發(fā)閥值,當(dāng)手指按下后,在該閥值的時間內(nèi),未移動超過mTouchSlopSquare的距離并未抬起,則showPress手勢觸發(fā)
private?static?final?int?TAP_TIMEOUT?=?ViewConfiguration.getTapTimeout();
//?雙擊超時閥值,僅在兩次雙擊事件的間隔(第一次單擊的UP事件和第二次單擊的DOWN事件)小于此閥值,雙擊事件才能成立
private?static?final?int?DOUBLE_TAP_TIMEOUT?=?ViewConfiguration.getDoubleTapTimeout();
//?雙擊區(qū)域閥值,僅在兩次雙擊事件的距離小于此閥值,雙擊事件才能成立
private?static?final?int?DOUBLE_TAP_SLAP?=?64;
//?GestureHandler所處理的Message的what屬性可能為以下?常量:
//?showPress手勢
private?static?final?int?SHOW_PRESS?=?1;
//?長按手勢
private?static?final?int?LONG_PRESS?=?2;
//?SingleTapConfirmed手勢
private?static?final?int?TAP_SINGLE?=?3;
//?雙擊手勢
private?static?final?int?TAP_DOUBLE?=?4;
//?手勢處理器
private?final?GestureHandler?mHandler;
//?手勢監(jiān)聽器
private?final?MultiTouchGestureListener?mListener;
//?雙擊監(jiān)聽器
private?MultiTouchDoubleTapListener?mDoubleTapListener;
//?長按允許閥值
private?boolean?mIsLongpressEnabled;
//?速度追蹤器
private?VelocityTracker?mVelocityTracker;
private?class?GestureHandler?extends?Handler
{
GestureHandler()
{
super();
}
GestureHandler(Handler?handler)
{
super(handler.getLooper());
}
@Override
public?void?handleMessage(Message?msg)
{
int?idx?=?(Integer)?msg.obj;
switch?(msg.what)
{
case?SHOW_PRESS:
{
if?(idx?>=?sEventInfos.size())
{
//?Log.w(MYTAG,?CLASS_NAME?+
//?":handleMessage,?msg.what?=?SHOW_PRESS,?idx="?+?idx?+
//?",?while?sEventInfos.size()="
//?+?sEventInfos.size());
break;
}
EventInfo?info?=?sEventInfos.get(idx);
if?(info?==?null)
{
//?Log.e(MYTAG,?CLASS_NAME?+
//?":handleMessage,?msg.what?=?SHOW_PRESS,?idx="?+?idx?+
//?",?Info?=?null");
break;
}
//?觸發(fā)手勢監(jiān)聽器的onShowPress事件
mListener.onShowPress(info.mCurrentDownEvent);
break;
}
case?LONG_PRESS:
{
//?Log.d(MYTAG,?CLASS_NAME?+?":trigger?LONG_PRESS");
if?(idx?>=?sEventInfos.size())
{
//?Log.w(MYTAG,?CLASS_NAME?+
//?":handleMessage,?msg.what?=?LONG_PRESS,?idx="?+?idx?+
//?",?while?sEventInfos.size()="
//?+?sEventInfos.size());
break;
}
EventInfo?info?=?sEventInfos.get(idx);
if?(info?==?null)
{
//?Log.e(MYTAG,?CLASS_NAME?+
//?":handleMessage,?msg.what?=?LONG_PRESS,?idx="?+?idx?+
//?",?Info?=?null");
break;
}
dispatchLongPress(info,?idx);
break;
}
case?TAP_SINGLE:
{
//?Log.d(MYTAG,?CLASS_NAME?+?":trriger?TAP_SINGLE");
//?If?the?user's?finger?is?still?down,?do?not?count?it?as?a
//?tap
if?(idx?>=?sEventInfos.size())
{
//?Log.e(MYTAG,?CLASS_NAME?+
//?":handleMessage,?msg.what?=?TAP_SINGLE,?idx="?+?idx?+
//?",?while?sEventInfos.size()="
//?+?sEventInfos.size());
break;
}
EventInfo?info?=?sEventInfos.get(idx);
if?(info?==?null)
{
//?Log.e(MYTAG,?CLASS_NAME?+
//?":handleMessage,?msg.what?=?TAP_SINGLE,?idx="?+?idx?+
//?",?Info?=?null");
break;
}
if?(mDoubleTapListener?!=?null?&&?!info.mStillDown)
{?//?手指在雙擊超時的閥值內(nèi)未離開屏幕進行第二次單擊事件,則確定單擊事件成立(不再觸發(fā)雙擊事件)
mDoubleTapListener.onSingleTapConfirmed(info.mCurrentDownEvent);
}
break;
}
case?TAP_DOUBLE:
{
if?(idx?>=?sEventForDoubleTap.size())
{
//?Log.w(MYTAG,?CLASS_NAME?+
//?":handleMessage,?msg.what?=?TAP_DOUBLE,?idx="?+?idx?+
//?",?while?sEventForDoubleTap.size()="
//?+?sEventForDoubleTap.size());
break;
}
EventInfo?info?=?sEventForDoubleTap.get(idx);
if?(info?==?null)
{
//?Log.w(MYTAG,?CLASS_NAME?+
//?":handleMessage,?msg.what?=?TAP_DOUBLE,?idx="?+?idx?+
//?",?Info?=?null");
break;
}
sEventForDoubleTap.set(idx,?null);//?這個沒什么好做的,就是把隊列中對應(yīng)的元素清除而已
break;
}
default:
throw?new?RuntimeException("Unknown?message?"?+?msg);?//?never
}
}
}
/**
*?觸發(fā)長按事件
*
*?@param?info
*?@param?idx
*/
private?void?dispatchLongPress(EventInfo?info,?int?idx)
{
mHandler.removeMessages(TAP_SINGLE,?idx);//?移除單擊事件確認
info.mInLongPress?=?true;
mListener.onLongPress(info.mCurrentDownEvent);
}
/**
*?構(gòu)造器1
*
*?@param?context
*?@param?listener
*/
public?MultiTouchGestureDetector(Context?context,?MultiTouchGestureListener?listener)
{
this(context,?listener,?null);
}
/**
*?構(gòu)造器2
*
*?@param?context
*?@param?listener
*?@param?handler
*/
public?MultiTouchGestureDetector(Context?context,?MultiTouchGestureListener?listener,?Handler?handler)
{
if?(handler?!=?null)
{
mHandler?=?new?GestureHandler(handler);
}
else
{
mHandler?=?new?GestureHandler();
}
mListener?=?listener;
if?(listener?instanceof?MultiTouchDoubleTapListener)
{
setOnDoubleTapListener((MultiTouchDoubleTapListener)?listener);
}
init(context);
}
/**
*?初始化識別器
*
*?@param?context
*/
private?void?init(Context?context)
{
if?(mListener?==?null)
{
throw?new?NullPointerException("OnGestureListener?must?not?be?null");
}
mIsLongpressEnabled?=?true;
int?touchSlop,?doubleTapSlop;
if?(context?==?null)
{
touchSlop?=?ViewConfiguration.getTouchSlop();
doubleTapSlop?=?DOUBLE_TAP_SLAP;
mMinimumFlingVelocity?=?ViewConfiguration.getMinimumFlingVelocity();
mMaximumFlingVelocity?=?ViewConfiguration.getMaximumFlingVelocity();
}
else
{//?允許識別器在App中,使用偏好的設(shè)定
final?ViewConfiguration?configuration?=?ViewConfiguration.get(context);
touchSlop?=?configuration.getScaledTouchSlop();
doubleTapSlop?=?configuration.getScaledDoubleTapSlop();
mMinimumFlingVelocity?=?configuration.getScaledMinimumFlingVelocity();
mMaximumFlingVelocity?=?configuration.getScaledMaximumFlingVelocity();
}
mTouchSlopSquare?=?touchSlop?*?touchSlop?/?16;
mDoubleTapSlopSquare?=?doubleTapSlop?*?doubleTapSlop;
}
/**
*?設(shè)置雙擊監(jiān)聽器
*
*?@param?onDoubleTapListener
*/
public?void?setOnDoubleTapListener(MultiTouchDoubleTapListener?onDoubleTapListener)
{
mDoubleTapListener?=?onDoubleTapListener;
}
/**
*?設(shè)置是否允許長按
*
*?@param?isLongpressEnabled
*/
public?void?setIsLongpressEnabled(boolean?isLongpressEnabled)
{
mIsLongpressEnabled?=?isLongpressEnabled;
}
/**
*?判斷是否允許長按
*
*?@return
*/
public?boolean?isLongpressEnabled()
{
return?mIsLongpressEnabled;
}
/**
*?判斷當(dāng)前事件是否為雙擊事件?
*?通過遍歷sEventForDoubleTap來匹配是否存在能夠構(gòu)成雙擊事件的單擊事件
*
*?@param?e
*?@return
*/
private?EventInfo?checkForDoubleTap(MultiMotionEvent?e)
{
if?(sEventForDoubleTap.isEmpty())
{
//?Log.e(MYTAG,?CLASS_NAME?+
//?":checkForDoubleTap(),?sEventForDoubleTap?is?empty?!");
return?null;
}
for?(int?i?=?0;?i?
{
EventInfo?info?=?sEventForDoubleTap.get(i);
if?(info?!=?null?&&?isConsideredDoubleTap(info,?e))
{
sEventForDoubleTap.set(i,?null);//?這個單擊事件已經(jīng)被消耗了,所以置為null
mHandler.removeMessages(TAP_DOUBLE,?i);//?移除Handler內(nèi)的為處理消息
return?info;
}
}
return?null;
}
/**
*?判斷當(dāng)前按下事件是否能和指定的單擊事件構(gòu)成雙擊事件
*
*?@param?info
*?@param?secondDown
*?@return
*/
private?boolean?isConsideredDoubleTap(EventInfo?info,?MultiMotionEvent?secondDown)
{
if?(!info.mAlwaysInBiggerTapRegion)
{?//?如多第一次單擊事件有過較大距離的移動,則無法構(gòu)成雙擊事件
return?false;
}
if?(secondDown.getEventTime()?-?info.mPreviousUpEvent.getEventTime()?>?DOUBLE_TAP_TIMEOUT)
{
//?如果第一次單擊的UP時間和第二次單擊的down時間時間間隔大于DOUBLE_TAP_TIMEOUT,也無法構(gòu)成雙擊事件
return?false;
}
int?deltaX?=?(int)?info.mCurrentDownEvent.getX()?-?(int)?secondDown.getX();
int?deltaY?=?(int)?info.mCurrentDownEvent.getY()?-?(int)?secondDown.getY();
return?(deltaX?*?deltaX?+?deltaY?*?deltaY?
}
/**
*?將事件信息放入雙擊判斷隊列,并返回序號
*
*?@param?info
*?@return
*/
private?int?addIntoTheMinIndex(EventInfo?info)
{
for?(int?i?=?0;?i?
{
if?(sEventForDoubleTap.get(i)?==?null)
{
sEventForDoubleTap.set(i,?info);
return?i;
}
}
sEventForDoubleTap.add(info);
return?sEventForDoubleTap.size()?-?1;
}
/**
*?從事件信息隊列中移除指定序號的事件
*
*?@param?idx
*/
private?void?removeEventFromList(int?id)
{
if?(id?>?sEventInfos.size()?||?id?
{
//?Log.e(MYTAG,?CLASS_NAME?+?".removeEventFromList(),?id="?+?id?+
//?",?while?sEventInfos.size()?="?+
//?sEventInfos.size());
return;
}
sEventInfos.set(id,?null);
}
/**
*?向事件隊列中添加新信息
*
*?@param?e
*/
private?void?addEventIntoList(EventInfo?info)
{
int?id?=?info.mCurrentDownEvent.getId();
if?(id?
{
//?if?(sEventInfos.get(id)?!=?null)
//?Log.e(MYTAG,?CLASS_NAME?+?".addEventIntoList,?info("?+?id?+
//?")?has?not?set?to?null?!");
sEventInfos.set(info.mCurrentDownEvent.getId(),?info);
}
else?if?(id?==?sEventInfos.size())
{
sEventInfos.add(info);
}
else
{
//?Log.e(MYTAG,?CLASS_NAME?+?".addEventIntoList,?invalidata?id?!");
}
}
public?boolean?onTouchEvent(MotionEvent?ev)
{
if?(mVelocityTracker?==?null)
{
mVelocityTracker?=?VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);//?把所有事件都添加到速度追蹤器,為計算速度做準備
boolean?handled?=?false;
final?int?action?=?ev.getAction();?//?獲取Action
//?int?idx?=?(action?&?MotionEvent.ACTION_POINTER_INDEX_MASK)?>>
//?MotionEvent.ACTION_POINTER_INDEX_SHIFT;//獲取觸摸事件的序號
int?idx?=?ev.getPointerId(ev.getActionIndex());//?獲取觸摸事件的id
switch?(action?&?MotionEvent.ACTION_MASK)
{
case?MotionEvent.ACTION_DOWN:
case?MotionEvent.ACTION_POINTER_DOWN:
{
EventInfo?info?=?new?EventInfo(MotionEvent.obtain(ev));
this.addEventIntoList(info);//?將手勢信息保存到隊列中
if?(mDoubleTapListener?!=?null)
{//?如果雙擊監(jiān)聽器不為null
if?(mHandler.hasMessages(TAP_DOUBLE))
{
MultiMotionEvent?e?=?new?MultiMotionEvent(ev);
EventInfo?origInfo?=?checkForDoubleTap(e);//?檢查是否構(gòu)成雙擊事件
if?(origInfo?!=?null)
{
info.mIsDoubleTapping?=?true;
handled?|=?mDoubleTapListener.onDoubleTap(origInfo.mCurrentDownEvent);
handled?|=?mDoubleTapListener.onDoubleTapEvent(e);
}
}
if?(!info.mIsDoubleTapping)
{//?當(dāng)前事件不構(gòu)成雙擊事件,那么發(fā)送延遲消息以判斷onSingleTapConfirmed事件
mHandler.sendMessageDelayed(mHandler.obtainMessage(TAP_SINGLE,?idx),?DOUBLE_TAP_TIMEOUT);
//?Log.d(MYTAG,?CLASS_NAME?+?":?add?TAP_SINGLE");
}
}
//?記錄X坐標(biāo)和Y坐標(biāo)
info.mLastMotionX?=?info.mCurrentDownEvent.getX();
info.mLastMotionY?=?info.mCurrentDownEvent.getY();
if?(mIsLongpressEnabled)
{//?允許長按
mHandler.removeMessages(LONG_PRESS,?idx);
mHandler.sendMessageAtTime(mHandler.obtainMessage(LONG_PRESS,?idx),?info.mCurrentDownEvent.getEventTime()?+?TAP_TIMEOUT?+?LONGPRESS_TIMEOUT);//?延時消息以觸發(fā)長按手勢
//?Log.d(MYTAG,?CLASS_NAME?+
//?":add?LONG_PRESS?to?handler??for?idx?"?+?idx);
}
mHandler.sendMessageAtTime(mHandler.obtainMessage(SHOW_PRESS,?idx),?info.mCurrentDownEvent.getEventTime()?+?TAP_TIMEOUT);//?延時消息,觸發(fā)showPress手勢
handled?|=?mListener.onDown(info.mCurrentDownEvent);//?觸發(fā)onDown()
break;
}
case?MotionEvent.ACTION_UP:
case?MotionEvent.ACTION_POINTER_UP:
{
MultiMotionEvent?currentUpEvent?=?new?MultiMotionEvent(ev);
if?(idx?>=?sEventInfos.size())
{
//?Log.e(MYTAG,?CLASS_NAME?+?":ACTION_POINTER_UP,?idx="?+
//?idx?+?",?while?sEventInfos.size()="?+
//?sEventInfos.size());
break;
}
EventInfo?info?=?sEventInfos.get(currentUpEvent.getId());
if?(info?==?null)
{
//?Log.e(MYTAG,?CLASS_NAME?+?":ACTION_POINTER_UP,?idx="?+
//?idx?+?",?Info?=?null");
break;
}
info.mStillDown?=?false;
if?(info.mIsDoubleTapping)
{?//?處于雙擊狀態(tài),則觸發(fā)onDoubleTapEvent事件
handled?|=?mDoubleTapListener.onDoubleTapEvent(currentUpEvent);
}
else?if?(info.mInLongPress)
{//?處于長按狀態(tài)
mHandler.removeMessages(TAP_SINGLE,?idx);//?可以無視這行代碼
info.mInLongPress?=?false;
}
else?if?(info.mAlwaysInTapRegion)
{//?尚未移動過
if?(mHandler.hasMessages(TAP_SINGLE,?idx))
{//?還在雙擊的時間閥值內(nèi),所以要為雙擊判斷做額外處理
mHandler.removeMessages(TAP_SINGLE,?idx);
info.mPreviousUpEvent?=?new?MultiMotionEvent(MotionEvent.obtain(ev));
int?index?=?this.addIntoTheMinIndex(info);//?把當(dāng)前事件放入隊列,等待雙擊的判斷
mHandler.sendMessageAtTime(mHandler.obtainMessage(TAP_DOUBLE,?index),?info.mCurrentDownEvent.getEventTime()?+?DOUBLE_TAP_TIMEOUT);?//?將雙擊超時判斷添加到Handler
//?Log.d(MYTAG,?CLASS_NAME?+?":?add?TAP_DOUBLE");
}
handled?=?mListener.onSingleTapUp(currentUpEvent);//?觸發(fā)onSingleTapUp事件
}
else
{
//?A?fling?must?travel?the?minimum?tap?distance
final?VelocityTracker?velocityTracker?=?mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000,?mMaximumFlingVelocity);//?計算1秒鐘內(nèi)的滑動速度
//?獲取X和Y方向的速度
final?float?velocityX?=?velocityTracker.getXVelocity(idx);
final?float?velocityY?=?velocityTracker.getYVelocity(idx);
//?Log.i(MYTAG,?CLASS_NAME?+?":ACTION_POINTER_UP,?idx="?+
//?idx?+
//?",?vx="?+?velocityX?+?",?vy="?+?velocityY);
//?觸發(fā)滑動事件
if?((Math.abs(velocityY)?>?mMinimumFlingVelocity)?||?(Math.abs(velocityX)?>?mMinimumFlingVelocity))
{
handled?=?mListener.onFling(info.mCurrentDownEvent,?currentUpEvent,?velocityX,?velocityY);
}
}
//?Hold?the?event?we?obtained?above?-?listeners?may?have?changed
//?the
//?original.
if?(action?==?MotionEvent.ACTION_UP)
{?//?釋放速度追蹤器
mVelocityTracker.recycle();
mVelocityTracker?=?null;
//?Log.w(MYTAG,?CLASS_NAME?+
//?":ACTION_POINTER_UP,?mVelocityTracker.recycle()");
}
info.mIsDoubleTapping?=?false;
//?Log.d(MYTAG,?CLASS_NAME?+?"remove?LONG_PRESS");
//?移除showPress和長按消息
mHandler.removeMessages(SHOW_PRESS,?idx);
mHandler.removeMessages(LONG_PRESS,?idx);
removeEventFromList(currentUpEvent.getId());//?手指離開,則從隊列中刪除手勢信息
break;
}
case?MotionEvent.ACTION_MOVE:
for?(int?rIdx?=?0;?rIdx?
{//?因為無法確定當(dāng)前發(fā)生移動的是哪個手指,所以遍歷處理所有手指
MultiMotionEvent?e?=?new?MultiMotionEvent(ev,?rIdx);
if?(e.getId()?>=?sEventInfos.size())
{
//?Log.e(MYTAG,?CLASS_NAME?+?":ACTION_MOVE,?idx="?+?rIdx
//?+?",?while?sEventInfos.size()="?+
//?sEventInfos.size());
break;
}
EventInfo?info?=?sEventInfos.get(e.getId());
if?(info?==?null)
{
//?Log.e(MYTAG,?CLASS_NAME?+?":ACTION_MOVE,?idx="?+?rIdx
//?+?",?Info?=?null");
break;
}
if?(info.mInLongPress)
{?//?長按,則不處理move事件
break;
}
//?當(dāng)前坐標(biāo)
float?x?=?e.getX();
float?y?=?e.getY();
//?距離上次事件移動的位置
final?float?scrollX?=?x?-?info.mLastMotionX;
final?float?scrollY?=?y?-?info.mLastMotionY;
if?(info.mIsDoubleTapping)
{//?雙擊事件
handled?|=?mDoubleTapListener.onDoubleTapEvent(e);
}
else?if?(info.mAlwaysInTapRegion)
{//?該手勢尚未移動過(移動的距離小于mTouchSlopSquare,視為未移動過)
//?計算從落下到當(dāng)前事件,移動的距離
final?int?deltaX?=?(int)?(x?-?info.mCurrentDownEvent.getX());
final?int?deltaY?=?(int)?(y?-?info.mCurrentDownEvent.getY());
//?Log.d(MYTAG,?CLASS_NAME?+?"deltaX="+deltaX+";deltaY="
//?+
//?deltaX?+"mTouchSlopSquare="?+?mTouchSlopSquare);
int?distance?=?(deltaX?*?deltaX)?+?(deltaY?*?deltaY);
if?(distance?>?mTouchSlopSquare)
{?//?移動距離超過mTouchSlopSquare
handled?=?mListener.onScroll(info.mCurrentDownEvent,?e,?scrollX,?scrollY);
info.mLastMotionX?=?e.getX();
info.mLastMotionY?=?e.getY();
info.mAlwaysInTapRegion?=?false;
//?Log.d(MYTAG,?CLASS_NAME?+
//?":remove?LONG_PRESS?for?idx"?+?rIdx?+
//?",mTouchSlopSquare("+mTouchSlopSquare+"),?distance("+distance+")");
//?清除onSingleTapConform,showPress,longPress三種消息
int?id?=?e.getId();
mHandler.removeMessages(TAP_SINGLE,?id);
mHandler.removeMessages(SHOW_PRESS,?id);
mHandler.removeMessages(LONG_PRESS,?id);
}
if?(distance?>?mBiggerTouchSlopSquare)
{//?移動距離大于mBiggerTouchSlopSquare,則無法構(gòu)成雙擊事件
info.mAlwaysInBiggerTapRegion?=?false;
}
}
else?if?((Math.abs(scrollX)?>=?1)?||?(Math.abs(scrollY)?>=?1))
{//?之前已經(jīng)移動過了
handled?=?mListener.onScroll(info.mCurrentDownEvent,?e,?scrollX,?scrollY);
info.mLastMotionX?=?x;
info.mLastMotionY?=?y;
}
}
break;
case?MotionEvent.ACTION_CANCEL:
cancel();//?清理
}
return?handled;
}
//?清理所有隊列
private?void?cancel()
{
mHandler.removeMessages(SHOW_PRESS);
mHandler.removeMessages(LONG_PRESS);
mHandler.removeMessages(TAP_SINGLE);
mVelocityTracker.recycle();
mVelocityTracker?=?null;
sEventInfos.clear();
sEventForDoubleTap.clear();
}
}
參考資料:http://xiaxingwork.iteye.com/blog/1771714
總結(jié)
以上是生活随笔為你收集整理的android怎么监听多点触摸_Android笔记:触摸事件的分析与总结----多点触控的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: kylin构建cube时step1报错M
- 下一篇: 网络存储服务器dsm系统,群晖nas网络