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

歡迎訪問 生活随笔!

生活随笔

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

Android

Android笔记:触摸事件的分析与总结----多点触控

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


? ?其他相關博文:

? ?Android筆記:觸摸事件的分析與總結----MotionEvent對象

? ?Android筆記:觸摸事件的分析與總結----TouchEvent處理機制

????Android筆記:觸摸事件的分析與總結----多點觸控



一、多點觸控

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

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

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

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

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

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


簡單測試范例


布局xml代碼如下:

<RelativeLayout?xmlns:android="http://schemas.android.com/apk/res/android"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"?><TextViewandroid:id="@+id/message"android:layout_width="wrap_content"android:layout_height="wrap_content"android:tag="true控件"android:text="@string/hello_world"?/></RelativeLayout>


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?? {@Overrideprotected?void?onCreate(Bundle?savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);RelativeLayout?layout?=?(RelativeLayout)?findViewById(R.id.layout1);layout.setOnTouchListener(this);}@Overridepublic?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_DOWNint?count?=?event.getPointerCount();sb.append("觸點個數:?").append(count).append("\n");int?i?=?0;while?(i?<?count){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之間,看情況,很可能始終返回1sb.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.?*/@Overridepublic?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);//?設置圖片填充ImageViewp_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()));}}//?繪制邊框@Overrideprotected?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,否則傳1if?(gapLenght?>?0){this.setScale(scale,?0);}else{this.setScale(scale,?1);}beforeLenght?=?afterLenght;break;}}}//?這里來監聽屏幕觸控時間@Overridepublic?boolean?onTouchEvent(MotionEvent?event){/***?判定用戶是否觸摸到了圖片?如果是單點觸摸則調用控制圖片移動的方法?如果是2點觸控則調用控制圖片大小的方法*/if?(event.getY()?>?p_w_picpathView.getTop()?&&?event.getY()?<?p_w_picpathView.getBottom()?&&?event.getX()?>?p_w_picpathView.getLeft()&&?event.getX()?<?p_w_picpathView.getRight()){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";/***?事件信息類?<br/>*?用來記錄一個手勢*/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;}}@Overridepublic?void?finalize(){this.recycle();}}/***?多點事件類?<br/>*?將一個多點事件拆分為多個單點事件,并方便獲得事件的絕對坐標?<br/>*?絕對坐標用以在界面中找到觸點所在的控件*?*?@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;}//?事件IDpublic?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<EventInfo>?sEventInfos?=?new?ArrayList<EventInfo>(10);//?雙擊判斷隊列,這個隊列中的元素等待雙擊超時的判斷結果private?static?List<EventInfo>?sEventForDoubleTap?=?new?ArrayList<EventInfo>(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());}@Overridepublic?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//?tapif?(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;}/***?判斷當前事件是否為雙擊事件?<br/>*?通過遍歷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?<?sEventForDoubleTap.size();?i++){EventInfo?info?=?sEventForDoubleTap.get(i);if?(info?!=?null?&&?isConsideredDoubleTap(info,?e)){sEventForDoubleTap.set(i,?null);//?這個單擊事件已經被消耗了,所以置為nullmHandler.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?<?mDoubleTapSlopSquare);//?最后判斷兩次單擊事件的距離}/***?將事件信息放入雙擊判斷隊列,并返回序號*?*?@param?info*?@return*/private?int?addIntoTheMinIndex(EventInfo?info){for?(int?i?=?0;?i?<?sEventForDoubleTap.size();?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?<?0){//?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?<?sEventInfos.size()){//?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());//?獲取觸摸事件的idswitch?(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){//?如果雙擊監聽器不為nullif?(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?distancefinal?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?<?ev.getPointerCount();?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){?//?移動距離超過mTouchSlopSquarehandled?=?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

































轉載于:https://blog.51cto.com/glblong/1569058

總結

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

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