日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

android怎么监听多点触摸_Android笔记:触摸事件的分析与总结----多点触控

發布時間:2023/12/16 Android 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android怎么监听多点触摸_Android笔记:触摸事件的分析与总结----多点触控 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

其他相關博文:

一、多點觸控

當多點同時觸摸屏幕時,系統將會產生如下的觸摸事件:

1.ACTION_DOWN:觸摸屏幕的第一個點。此時手勢開始。該點的數據通常在MotionEvent事件隊列索引位置0處。

2.ACTION_POINTER_DOWN:除了第一個點的其他觸摸點數據。該點的數據的索引位置由getActionIndex()方法返回。

3.ACTION_MOVE:在手勢過程中發生的一次變化。

4.ACTION_POINTER_UP:當不是第一個點的其他點UP后觸發。

5.ACTION_UP:當手勢中的最后一個點離開屏幕。

簡單測試范例

布局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("觸點個數:?").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("相對坐標:?").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("結束時間:?").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且指針ID為0的指針(ACTION_DOWN = 0);

B.接下去action輸出為2(ACTION_MOVE),此時仍然僅有一個指針,并且索引和ID都為0;

C.然后按下第二根手指,action值輸出為261。此值由兩部分組成:一個是指針索引,一個是指針正在執行何種操作;

D.將十進制261轉換為十六進制數為0x00000105。其中01代表指針索引,05代表操作值(即ACTION_POINTER_DOWN的值,用于多點觸摸場景);

E.依此類推,當按下第三根手指時action輸出值為0x00000205(十進制517),第四根則為0x000305(十進制773);

F.當第一根手指離開屏幕時,action值為0x00000006(十進制6),即索引為0,操作值為6(即ACTION_POINTER_UP);

G.如果此時第二根手指離開時,action值應該為0x00000106(十進制262),因為此時仍然擁有兩根手指的信息;

H.而實際結果并沒有得到262的action值,這是因為當第一根手指離開屏幕后,第二根手指的索引從1更改成了0,但是指針ID仍然為1.

I.對于每一次移動,action值將始終為2,因為ACTION_MOVE事件并沒有包含哪根手指在移動的信息。

附:依次按下四根手指的日志

其他小結:

(1)getPointerCount() 獲得觸屏的點數,

getActionIndex()獲得觸點的指針索引,

getPointerId(index)獲得指針索引對應的觸點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;

//?單點移動的前后坐標值

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()

{

//?設置p_w_picpathView的顯示圖片

p_w_picpathView.setImageBitmap(bitmap);

//?設置圖片填充ImageView

p_w_picpathView.setScaleType(ScaleType.FIT_XY);

}

//?創建一個自己的ImageView類

class?MyImageView?extends?ImageView

{

private?float?scale?=?0.1f;

public?MyImageView(Context?context)

{

super(context);

}

//?用來設置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是用來保存前一位置的坐標?afterX、Y是用來保存當前位置的坐標

*?它們的差值就是ImageView各坐標的增加或減少值

*/

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用來保存當前時間兩點之間的距離

*/

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;

}

//?如果當前時間兩點距離大于前一時間兩點距離,則傳0,否則傳1

if?(gapLenght?>?0)

{

this.setScale(scale,?0);

}

else

{

this.setScale(scale,?1);

}

beforeLenght?=?afterLenght;

break;

}

}

}

//?這里來監聽屏幕觸控時間

@Override

public?boolean?onTouchEvent(MotionEvent?event)

{

/**

*?判定用戶是否觸摸到了圖片?如果是單點觸摸則調用控制圖片移動的方法?如果是2點觸控則調用控制圖片大小的方法

*/

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

(二)實現多點消息攔截,用于多個物體拖動等效果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;

/**

*?實現多點消息攔截,用于多個物體拖動等效果

*?@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;?//?當前的down事件

private?MultiMotionEvent?mPreviousUpEvent;?//?上一次up事件

private?boolean?mStillDown;?//?當前手指是否還在屏幕上

private?boolean?mInLongPress;?//?當前事件是否屬于長按手勢

private?boolean?mAlwaysInTapRegion;?//?是否當前手指僅在小范圍內移動,當手指僅在小范圍內移動時,視為手指未曾移動過,不會觸發onScroll手勢

private?boolean?mAlwaysInBiggerTapRegion;?//?是否當前手指在較大范圍內移動,僅當此值為true時,雙擊手勢才能成立

private?boolean?mIsDoubleTapping;?//?當前手勢,是否為雙擊手勢

private?float?mLastMotionY;?//?最后一次事件的X坐標

private?float?mLastMotionX;?//?最后一次事件的Y坐標

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對象,使系統能夠繼續使用它們

public?void?recycle()

{

if?(mCurrentDownEvent?!=?null)

{

mCurrentDownEvent.recycle();

mCurrentDownEvent?=?null;

}

if?(mPreviousUpEvent?!=?null)

{

mPreviousUpEvent.recycle();

mPreviousUpEvent?=?null;

}

}

@Override

public?void?finalize()

{

this.recycle();

}

}

/**

*?多點事件類?

*?將一個多點事件拆分為多個單點事件,并方便獲得事件的絕對坐標?

*?絕對坐標用以在界面中找到觸點所在的控件

*

*?@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的絕對坐標

public?float?getX()

{

return?mEvent.getX(mIndex)?+?mEvent.getRawX()?-?mEvent.getX();

}

//?返回Y的絕對坐標

public?float?getY()

{

return?mEvent.getY(mIndex)?+?mEvent.getRawY()?-?mEvent.getY();

}

//?事件發生的時間

public?long?getEventTime()

{

return?mEvent.getEventTime();

}

//?事件序號

public?int?getIndex()

{

return?mIndex;

}

//?事件ID

public?int?getId()

{

return?mEvent.getPointerId(mIndex);

}

//?釋放事件對象,使系統能夠繼續使用

public?void?recycle()

{

if?(mEvent?!=?null)

{

mEvent.recycle();

mEvent?=?null;

}

}

}

//?多點手勢監聽器

public?interface?MultiTouchGestureListener

{

//?手指觸碰到屏幕,由一個?ACTION_DOWN觸發

boolean?onDown(MultiMotionEvent?e);

//?確定一個press事件,強調手指按下的一段時間(TAP_TIMEOUT)內,手指未曾移動或抬起

void?onShowPress(MultiMotionEvent?e);

//?手指點擊屏幕后離開,由?ACTION_UP引發,可以簡單的理解為單擊事件,即手指點擊時間不長(未構成長按事件),也不曾移動過

boolean?onSingleTapUp(MultiMotionEvent?e);

//?長按,手指點下后一段時間(DOUBLE_TAP_TIMEOUT)內,不曾抬起或移動

void?onLongPress(MultiMotionEvent?e);

//?拖動,由ACTION_MOVE觸發,手指地按下后,在屏幕上移動

boolean?onScroll(MultiMotionEvent?e1,?MultiMotionEvent?e2,?float?distanceX,?float?distanceY);

//?滑動,由ACTION_UP觸發,手指按下并移動一段距離后,抬起時觸發

boolean?onFling(MultiMotionEvent?e1,?MultiMotionEvent?e2,?float?velocityX,?float?velocityY);

}

//?多點雙擊監聽器

public?interface?MultiTouchDoubleTapListener

{

//?單擊事件確認,強調第一個單擊事件發生后,一段時間內,未發生第二次單擊事件,即確定不會觸發雙擊事件

boolean?onSingleTapConfirmed(MultiMotionEvent?e);

//?雙擊事件,

//?由ACTION_DOWN觸發,從第一次單擊事件的DOWN事件開始的一段時間(DOUBLE_TAP_TIMEOUT)內結束(即手指),

//?并且在第一次單擊事件的UP時間開始后的一段時間內(DOUBLE_TAP_TIMEOUT)發生第二次單擊事件,

//?除此之外兩者坐標間距小于定值(DOUBLE_TAP_SLAP)時,則觸發雙擊事件

boolean?onDoubleTap(MultiMotionEvent?e);

//?雙擊事件,與onDoubleTap事件不同之處在于,構成雙擊的第二次點擊的ACTION_DOWN,ACTION_MOVE和ACTION_UP都會觸發該事件

boolean?onDoubleTapEvent(MultiMotionEvent?e);

}

//?事件信息隊列,隊列的下標與MotionEvent的pointId對應

private?static?List?sEventInfos?=?new?ArrayList(10);

//?雙擊判斷隊列,這個隊列中的元素等待雙擊超時的判斷結果

private?static?List?sEventForDoubleTap?=?new?ArrayList(5);

//?指定大點擊區域的大小(這個比較拗口),這個值主要用于幫助判斷雙擊是否成立

private?int?mBiggerTouchSlopSquare?=?20?*?20;

//?判斷是否構成onScroll手勢,當手指在這個范圍內移動時,不觸發onScroll手勢

private?int?mTouchSlopSquare;

//?判斷是否構成雙擊,只有兩次點擊的距離小于該值,才能構成雙擊手勢

private?int?mDoubleTapSlopSquare;

//?最小滑動速度

private?int?mMinimumFlingVelocity;

//?最大滑動速度

private?int?mMaximumFlingVelocity;

//?長按閥值,當手指按下后,在該閥值的時間內,未移動超過mTouchSlopSquare的距離并未抬起,則長按手勢觸發

private?static?final?int?LONGPRESS_TIMEOUT?=?ViewConfiguration.getLongPressTimeout();

//?showPress手勢的觸發閥值,當手指按下后,在該閥值的時間內,未移動超過mTouchSlopSquare的距離并未抬起,則showPress手勢觸發

private?static?final?int?TAP_TIMEOUT?=?ViewConfiguration.getTapTimeout();

//?雙擊超時閥值,僅在兩次雙擊事件的間隔(第一次單擊的UP事件和第二次單擊的DOWN事件)小于此閥值,雙擊事件才能成立

private?static?final?int?DOUBLE_TAP_TIMEOUT?=?ViewConfiguration.getDoubleTapTimeout();

//?雙擊區域閥值,僅在兩次雙擊事件的距離小于此閥值,雙擊事件才能成立

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;

//?手勢監聽器

private?final?MultiTouchGestureListener?mListener;

//?雙擊監聽器

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;

}

//?觸發手勢監聽器的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)

{?//?手指在雙擊超時的閥值內未離開屏幕進行第二次單擊事件,則確定單擊事件成立(不再觸發雙擊事件)

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);//?這個沒什么好做的,就是把隊列中對應的元素清除而已

break;

}

default:

throw?new?RuntimeException("Unknown?message?"?+?msg);?//?never

}

}

}

/**

*?觸發長按事件

*

*?@param?info

*?@param?idx

*/

private?void?dispatchLongPress(EventInfo?info,?int?idx)

{

mHandler.removeMessages(TAP_SINGLE,?idx);//?移除單擊事件確認

info.mInLongPress?=?true;

mListener.onLongPress(info.mCurrentDownEvent);

}

/**

*?構造器1

*

*?@param?context

*?@param?listener

*/

public?MultiTouchGestureDetector(Context?context,?MultiTouchGestureListener?listener)

{

this(context,?listener,?null);

}

/**

*?構造器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中,使用偏好的設定

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;

}

/**

*?設置雙擊監聽器

*

*?@param?onDoubleTapListener

*/

public?void?setOnDoubleTapListener(MultiTouchDoubleTapListener?onDoubleTapListener)

{

mDoubleTapListener?=?onDoubleTapListener;

}

/**

*?設置是否允許長按

*

*?@param?isLongpressEnabled

*/

public?void?setIsLongpressEnabled(boolean?isLongpressEnabled)

{

mIsLongpressEnabled?=?isLongpressEnabled;

}

/**

*?判斷是否允許長按

*

*?@return

*/

public?boolean?isLongpressEnabled()

{

return?mIsLongpressEnabled;

}

/**

*?判斷當前事件是否為雙擊事件?

*?通過遍歷sEventForDoubleTap來匹配是否存在能夠構成雙擊事件的單擊事件

*

*?@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);//?這個單擊事件已經被消耗了,所以置為null

mHandler.removeMessages(TAP_DOUBLE,?i);//?移除Handler內的為處理消息

return?info;

}

}

return?null;

}

/**

*?判斷當前按下事件是否能和指定的單擊事件構成雙擊事件

*

*?@param?info

*?@param?secondDown

*?@return

*/

private?boolean?isConsideredDoubleTap(EventInfo?info,?MultiMotionEvent?secondDown)

{

if?(!info.mAlwaysInBiggerTapRegion)

{?//?如多第一次單擊事件有過較大距離的移動,則無法構成雙擊事件

return?false;

}

if?(secondDown.getEventTime()?-?info.mPreviousUpEvent.getEventTime()?>?DOUBLE_TAP_TIMEOUT)

{

//?如果第一次單擊的UP時間和第二次單擊的down時間時間間隔大于DOUBLE_TAP_TIMEOUT,也無法構成雙擊事件

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)

{//?如果雙擊監聽器不為null

if?(mHandler.hasMessages(TAP_DOUBLE))

{

MultiMotionEvent?e?=?new?MultiMotionEvent(ev);

EventInfo?origInfo?=?checkForDoubleTap(e);//?檢查是否構成雙擊事件

if?(origInfo?!=?null)

{

info.mIsDoubleTapping?=?true;

handled?|=?mDoubleTapListener.onDoubleTap(origInfo.mCurrentDownEvent);

handled?|=?mDoubleTapListener.onDoubleTapEvent(e);

}

}

if?(!info.mIsDoubleTapping)

{//?當前事件不構成雙擊事件,那么發送延遲消息以判斷onSingleTapConfirmed事件

mHandler.sendMessageDelayed(mHandler.obtainMessage(TAP_SINGLE,?idx),?DOUBLE_TAP_TIMEOUT);

//?Log.d(MYTAG,?CLASS_NAME?+?":?add?TAP_SINGLE");

}

}

//?記錄X坐標和Y坐標

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);//?延時消息以觸發長按手勢

//?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);//?延時消息,觸發showPress手勢

handled?|=?mListener.onDown(info.mCurrentDownEvent);//?觸發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)

{?//?處于雙擊狀態,則觸發onDoubleTapEvent事件

handled?|=?mDoubleTapListener.onDoubleTapEvent(currentUpEvent);

}

else?if?(info.mInLongPress)

{//?處于長按狀態

mHandler.removeMessages(TAP_SINGLE,?idx);//?可以無視這行代碼

info.mInLongPress?=?false;

}

else?if?(info.mAlwaysInTapRegion)

{//?尚未移動過

if?(mHandler.hasMessages(TAP_SINGLE,?idx))

{//?還在雙擊的時間閥值內,所以要為雙擊判斷做額外處理

mHandler.removeMessages(TAP_SINGLE,?idx);

info.mPreviousUpEvent?=?new?MultiMotionEvent(MotionEvent.obtain(ev));

int?index?=?this.addIntoTheMinIndex(info);//?把當前事件放入隊列,等待雙擊的判斷

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);//?觸發onSingleTapUp事件

}

else

{

//?A?fling?must?travel?the?minimum?tap?distance

final?VelocityTracker?velocityTracker?=?mVelocityTracker;

velocityTracker.computeCurrentVelocity(1000,?mMaximumFlingVelocity);//?計算1秒鐘內的滑動速度

//?獲取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);

//?觸發滑動事件

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?

{//?因為無法確定當前發生移動的是哪個手指,所以遍歷處理所有手指

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;

}

//?當前坐標

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,視為未移動過)

//?計算從落下到當前事件,移動的距離

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,則無法構成雙擊事件

info.mAlwaysInBiggerTapRegion?=?false;

}

}

else?if?((Math.abs(scrollX)?>=?1)?||?(Math.abs(scrollY)?>=?1))

{//?之前已經移動過了

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

總結

以上是生活随笔為你收集整理的android怎么监听多点触摸_Android笔记:触摸事件的分析与总结----多点触控的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。