Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> Android觸摸事件(一)-AbsTouchEventHandle

Android觸摸事件(一)-AbsTouchEventHandle

編輯:關於android開發

Android觸摸事件(一)-AbsTouchEventHandle


目錄

概述

這是一個 觸摸 事件統一處理輔助類;處理的主要是點擊事件,其中包括了:

單點觸摸事件 多點(兩點)觸摸事件

此類可以處理的事件包括:

單擊事件(基於時間與距離的兩種單擊事件,詳見下文) 雙擊事件 單點觸摸移動事件(可用於實現界面拖動) 多點觸摸移動事件(可用於實現界面縮放) 所有觸摸事件的相應回調(down/move/up事件)

關於單點觸摸事件(singleTouch)

單點觸摸事件很好理解,觸發的流程一般是:

mouse_down -> mouse_move -> mouse_up

這個過程可能發生的事件有以下幾種:

單擊事件 單點移動事件 雙擊事件

但是必須注意的點是:

單擊事件可能觸發mouse_move事件,而單點移動必定觸發mouse_move事件

在單擊事件中,mouse_move事件並不是百分百會觸發的,觸摸的時候先觸發的是mouse_down事件,如果觸摸的時間足夠長(按住不動時),接下來會觸發mouse_move事件,之後抬起時會觸發mouse_up事件
雖然觸發了mouse_move事件(按住不動),但是這依然是一個單擊事件,如果進行調試或者輸出移動的距離,可以明顯得到距離為 0


單擊的兩種方式

基於時間的單點觸摸事件(singleTouchByTime)

以時間來計算單擊事件時,這個過程可以不必過多地考慮單擊可能觸發的mouse_move事件,因為單擊本身就是一個時間足夠短的操作,即便存在一定小范圍的移動偏差也是允許的,當然這種情況是在 時間足夠短 的情況下
我們可以這麼處理:

//定義全局變量用於存放按下時的時間點
long downTime=0;
switch(event.getAction()){
    case MotionEvent.ACTION_DOWN:
        //觸摸時記錄當前時間
        downTime=System.currentTimeMillis();
        break;
    case MotionEvent.ACTION_UP:
        //抬起時計算與按下時時間的差
        long tillTime=System.currentTimeMillis()-downTime;
        //時間差在允許范圍內時,視為一次單擊事件成立
        if(tillTime<250){
            //處理單擊事件
        }
        //否則不視為一次單擊事件
        break;
}

通過計算按下時與抬起時的時間差來確定是否是一次單擊事件(250ms足夠了),這是基於時間的單擊事件;

基於距離的單點觸摸事件(singleTouchByDistance)

從上面我們知道單點觸摸時也是可能觸發mouse_move事件的,所以mouse_move事件並不能作為一個是否單點移動的標識,而且我們已經知道了單擊也可以是按住某個位置不動,持續一段時間之後再抬起,此時可能時間上已經達到一個足夠長的時間,但其實 點擊地方的坐標並沒有改變 ,這種情況下我將其也視為單擊的一種情況(總會在某些情況下需要處理這種單擊方式)
參考 基於時間的單擊方式 的處理方法,我們可以得到類似的處理方法:

float downX=0;
float downY=0;
switch(event.getAction()){
    case MotionEvent.ACTION_DOWN:
        //觸摸時記錄當前觸摸點的坐標
        downX=event.getX();
        downY=event.getY();
        break;
    case MotionEvent.ACTION_UP:
        //抬起時計算與按下時坐標的偏移距離
        float offsetX=Math.abs(event.getX()-downX);
        float offsetY=Math.abs(event.getY()-downY);
        //偏移量差在允許范圍內時,視為一次單擊事件成立
        if(offsetX<20 && offsetY<20){
            //處理單擊事件
        }
        //否則不視為一次單擊事件
        break;
}

以上為兩種單擊方式的處理方式


關於雙擊事件

雙擊事件的檢測邏輯

由於單擊事件存在兩種不同情況,所以雙擊同理衍生出兩種方式, 基於時間和基於距離兩種雙擊事件
不管是哪種方式,原理都是一樣的,基於對應的單擊方式實現第一次單擊,兩次單擊事件就構成了一次雙擊事件;
同時這裡存在一個問題是,雙擊不管從哪個角度來說,都是指兩次時間間隔短暫的單擊事件,所以不管是基於時間還是基於距離的雙擊事件,都是以兩次單擊時間之前的間隔時間不超過某個范圍來確定一次雙擊事件的.

必須指出的是,基於距離的雙擊事件其實也是可以不按時間來處理的,只要兩次單擊事件的距離在一定的偏移值范圍內,可認為是一次雙擊事件(與時間無關);
但此方式存在的問題是,如果是兩次連接發生在同一個位置的單擊事件,此時就無法正確的區分出到底是一次雙擊事件還是兩次單擊事件了.所以並不推薦使用此方式處理,而是按 兩次單擊事件間隔在一定時間差內視為一次雙擊事件

由上可以看出,其實這裡的雙擊事件就不是處理兩種方式了,僅僅是 基於時間的雙擊事件,只是構成該雙擊事件的單擊事件可能是 基於時間的或者是基於距離的 單擊事件

//用於記錄是否已經完成一次單擊事件
boolean isSingleClick=false;
switch(event.getAction()){
    //忽略ACTION_DOWN邏輯
    case MotionEvent.ACTION_UP:
        //達成一次單擊事件操作時,視為一次單擊事件成立
        if(singleClickFinish){
            //判斷是否已經完成了一次單擊事件(在允許的雙擊間隔時間內)
            if(isSingleClick){
                //若已完成了一次單擊事件,此次單擊構成了雙擊事件
                //處理雙擊事件
            }else{
                //僅為一次單擊事件
                //處理單擊事件
                //記錄已經完成了一次單擊事件
                isSingleClick=true;
            }
        }
        //否則不視為一次單擊事件
        break;
}

同時,這裡有一個需要注意的地方是,雙擊事件本質是兩次單擊事件構成的,第一次單擊事件發生時我們無法確定是否是一個正常的單擊事件還是可能會構成一次雙擊事件,所以必須按正常單擊事件響應;

但第二次單擊事件發生時,我們已經可以確定構成了一次雙擊事件,此時不應該再響應單擊事件,而應該響應雙擊事件並結束觸摸事件;

實際的處理事件並沒有這麼簡單,以上是簡單的處理邏輯,具體的實現請參照下文 雙擊事件的優化處理


雙擊事件觸發的時機

雙擊事件觸發的時機是比較重要的.因為雙擊事件是由單擊事件觸發的.必然先檢測單擊事件之後再檢測雙擊事件;
但一旦單擊事件被觸發了,那麼接下來需要做的操作有兩個選擇:

檢測雙擊事件(之後執行雙擊事件) 執行單擊事件

這兩個事件的優先性是必須確定的而且會造成不同的影響.
如果先執行單擊事件,則可能會造成在後續雙擊事件成立的之前,單擊事件會被執行一次.這並不合理,也可能存在一些不安全的因素(如果單擊操作會影響到雙擊操作的情況下)

因此應檢測雙擊事件,一旦雙擊事件成立,直接執行雙擊事件,同時忽略單擊事件;(用戶觸發了雙擊事件本身包含了不需要執行單擊事件的想法,否則直接觸發單擊事件即可)

這也是為什麼事件觸發規則會雙擊事件優先;


關於多點觸摸事件(multiTouch)

多點觸摸事件相對比較復雜,此處只討論 兩點觸摸.
多點觸摸事件的需要通過額外的方式進行檢測並處理事件,無法與單點觸摸事件一樣直接event.getAction()得到的就是相關的觸摸事件;

//分離觸摸事件,使用 MotionEvent.ACTION_MASK
//此方式可以正確分離出多點觸摸事件 ACTION_POINTER_X,也可以正常返回單點觸摸事件 ACTION_X
switch(event.getAction() & MotionEvent.ACTION_MASK){
    case MotionEvent.ACTION_X:
        //單點觸摸事件處理
        break;
    case MotionEvent.ACTION_POINTER_X:
        //多點觸摸事件處理
        brea;
}

兩點觸摸中的移動事件

首先,必須注意的一個點是:

多點觸摸事件中移動時觸發的移動事件也是 ACTION_MOVE

也就是說ACTION_MOVE事件是移動的通用事件,在單點觸摸移動和多點觸摸移動中都存在.


兩點觸摸事件的觸發過程

除以上提及的共用ACTION_MOVE事件之外,多點觸摸事件可能存在的過程是這樣的:

ACTION_DOWN -> ACTION_POINTER_DOWN -> ACTION_MOVE -> ACTION_POINTER_UP -> ACTION_UP
這種情況是在兩點觸摸時,兩個手指剛好 同時按上 -> 移動 -> 同時抬起

很明顯,既然存在同時觸摸,也肯定存在非同時觸摸了.當非同時觸摸時的過程是這樣的:

ACTION_DOWN -> ACTION_MOVE -> ACTION_POINTER_DOWN -> ACTION_MOVE -> ACTION_POINTER_UP -> ACTION_MOVE -> ACTION_UP
這種情況是先單點觸摸,觸發了ACTION_DOWN事件,然後第二個觸摸點按下時,觸發ACTION_POINTER_DOWN,然後當觸摸點抬起時,觸發ACTION_POINTER_UP(多個觸摸點的情況下會多次觸發ACTOIN_PONTER_DOWNACTION_POINTER_UP),之後單點觸摸抬起,觸發ACTION_UP; 至於第一個ACTION_MOVE事件是否會觸發取決於第一個觸摸點與第二個觸摸點之間的時間間距(如果第二次按下的時間與第一次按下時間間隔足夠短,則不會觸發); 同理第二個ACTION_MOVE取決於多點觸摸的按下與抬起的時間差,類似於單擊,多點觸摸按住時,ACTION_MOVE依然是正常觸發,但距離值還是 0. 而第三個ACTION_MOVE是所有多點觸摸抬起後(只剩下單點觸摸時),若還保持單點觸摸(不管有沒有移動)就會觸發第三輪的ACTION_MOVE事件

以上過程可以明確得到:

不管是同時多點觸摸還是間接多點觸摸,ACTION_DOWNACTION_UP兩個事件是必定會觸發並永遠在第一項和最後一項事件

所以,在處理多點觸摸的事件時,必須小心處理ACTION_MOVEACTION_UP事件,因為這兩個事件並不是單點觸摸專屬的事件,而是所有的觸摸事件都會觸發的


兩點觸摸的事件

在兩點觸摸時,一般我們不考慮兩點"單擊"事件,主要是針對兩點觸摸時觸發的移動事件進行處理;往往這種情況需要處理的是類似放大/縮小的功能

如何判斷兩點觸摸事件並處理

對於兩點觸摸事件,這個很好判斷;當ACTION_POINTER_DOWN觸發時,說明觸發了兩點觸摸事件;當ACTION_POINTER_UP觸發時,說明兩點觸摸事件結束; 兩點觸摸事件主要是基於這兩個事件之間,難點在於:

如何區分單點觸摸的移動事件和兩點觸摸的移動事件

根據以上我們確定兩點觸摸時,會觸發ACTION_PONTER_DOWN事件,之後才會觸發兩點觸摸事件的ACTION_MOVE,因此可以通過此事件確定當前的ACTION_MOVE事件是否屬於兩點觸摸的還是單點觸摸的事件

boolean isMultiDown=false;
switch(even.getAction() & MotionEvent.ACTION_MASK){
    case MotionEvent.ACTION_POINTER_DOWN:
        //記錄多點觸摸事件觸發
        isMultiDown=true;
        break;
    case MotionEvent.ACTION_MOVE:
        //檢測是否已經觸發了多點觸摸事件
        if(isMultiDown){
            //多點觸摸移動事件
        }else{
            //單點觸摸移動事件
        }
        break;
}

第二個可能的難點在於:

如何在ACTION_UP事件中區分並處理多點觸摸事件及單點觸摸事件

一般來說,處理多點觸摸事件時只關注多點觸摸事件;處理單點觸摸事件時只關注單點觸摸事件;而兩者都存在的ACTION_UP事件並且都在最後,就可能造成一個不必要的麻煩:

可能在多點觸摸事件結束後,觸發的ACTION_UP事件處理了一次單點觸摸事件

而這可能會導致某些我們不想要的情況發生.所以關於ACTION_UP事件,我們需要小心處理


實現

根據以上的說明,大致的一個觸摸事件流程和需要響應的事件也已經確定下來了.下面是整個觸摸事件及流程的一個實現思路,包括:

變量定義 觸摸事件處理流程

變量定義

因為觸摸事件各種情況相對復雜,先確定需要處理的事件包括如下事件:

以下事件為觸摸事件,提及觸摸事件特指以下五種系統反饋的觸摸事件,單擊/雙擊等非系統預定義事件稱為自定義事件

ACTION_DOWN ACTION_POINTER_DOWN ACTION_POINTER_UP ACTION_UP ACTION_MOVE

需要的變量包括:

//是否單點觸摸按下,用於單擊事件的檢測
boolean isSingleDown;
//是否多點觸摸按下,用於區分處理事件
boolean isMultDown;
//多點觸摸按下的次數,對應多點觸摸的個數
//盡管我們只處理兩點觸摸,但實際可能多達N點觸摸
int multiTouchCount;
//是否進行了移動,不區分多點移動還是單點移動
boolean isInMotionMove;
//是否進行了單點觸摸移動(優化處理單點觸摸與多點觸摸的切換)
boolean isSingleMove;
//是否完成一次單擊事件
boolean isSingleClikEvent;

以上為主要的需要變量,用於記錄各種不同的狀態以區分多點觸摸與單點觸摸及之間的單擊/雙擊/移動事件


觸摸事件流程

自定義事件計時方案

首先需要知道的一個事情是:

關於單擊/雙擊事件中時間間隔的計時我們通過Handler來處理

由於Hanlder可以發送Delay的消息,我們可以通過指定發送延時的消息交給Handler去取消事件或者消費事件;如下例子:

Hanlder mHandler=new Handler{
    @Override
    public void handleMessage(Message msg){
        if(msg.what==CANCLE_EVENT){
            //處理對應的消息取消操作
        }
    }
}
//250ms後發送取消事件消息
mHandler.sendEmptyMessageDelayed(CANCLE_EVENT,250);

通過此方法,我們可以在ACTION_DOWN事件觸發後設置一個按下的標識,然後發送一個延遲的取消按下事件消息,在ACTION_UP中直接檢測按下事件標識是否有效,有效則達成一次單擊事件,無效則說明已經超時,單擊事件無法觸摸;

必須注意:不管ACTION_UP事件中按下標識是否有效,已經發生了ACTION_UP必定發生過ACTION_DOWN,按下識別是作為單擊事件的檢測標識而不是ACTION_DOWN的觸發標識


自定義事件觸發區域

對於以上提及的不同的觸摸事件中,不同的事件可能會觸發不同的自定義事件

基於時間的單擊事件:由ACTION_DOWN/ACTION_UP事件觸發 基於距離的單擊事件:由ACTION_DOWN/ACTION_MOVE/ACTION_UP事件觸發 雙擊事件:由ACTION_DOWN/ACTION_UP事件中被觸發

觸摸事件處理規則

任何時候觸發雙擊事件不再響應其它事件(單擊或者UP等其它事件) 任何時候觸發多點觸摸事件則不再響應單點觸摸的MOVE事件

對於每個觸摸事件,除非被其它事件消費或者攔截(如雙擊事件會攔截其它後續事件),否則都會進行一次回調提供給子類進行處理,其中:

ACTION_DOWN/ACTION_UP
回調事件:onSingleTouchEventHandle(MotionEvent,int) ACTION_POINTER_DOWN/ACTION_POINTER_UP
回調事件:onMultiTouchEventHandle(MotionEvent,int) ACTION_MOVE比較特殊,存在兩個回調可能
單點觸摸移動事件回調:
onSingleTouchEventHandle(MotionEvent,int) 多點觸摸移動事件回調:
onMultiTouchEventHandle(MotionEvent,int)

關於回調的方法

統一回調的方法分為單點觸摸事件回調,多點觸摸事件回調;回調的時機是每一個對應的MotionEvent觸發時,在處理所有事件(單擊雙擊等)之後都會回調對應的事件以通知子類自定義處理.

onSingleTouchEventHandle(MotionEvent, int)//單點觸摸事件回調
onMultiTouchEventHandle(MotionEvent, int)//多點觸摸事件回調

各事件的回調對應如下:

//省略參數
switch(event.getAction() & MotionEvent.MASK){
    case MotionEvent.ACTION_DOWN:
        onSingleTouchEventHandle();
        break;
    case MotionEvent.ACTION_POINTER_DOWN:
        onMultiTouchEventHandle();
        break;
    case MotionEvent.ACTION_UP:
        onSingleTouchEventHandle();
        break;
    case MotionEvent.ACTION_POINTER_UP:
        onMultiTouchEventHandle();
        break;
    case MotionEvent.ACTION_MOVE:
        //move事件是共用的,所以需要區分回調事件的類型
        onSingleTouchEventHandle() || onMultiTouchEventHandle();
        break;
}

參數意義:

參數1為: 觸摸事件
參數2為: 建議處理的觸摸事件類型

參數1很好理解,只是傳送了系統分發的觸摸事件變量而已,包括了觸摸點的坐標,觸摸狀態等;
參數2是一個比較關鍵的參數;其存在是的意義是,建議以某個事件去處理當前的事件而不是直接按觸發的事件處理當前的事件.其使用的場景如下:

由於任何時候觸發了多點觸摸事件則不再處理單點觸摸事件的MOVE事件(觸發規則).
所以當多點觸摸事件ACTION_POINTER_DOWN發生之後,所有的ACTION_MOVE轉為多點觸摸的移動事件;
因此如果之前存在單點觸摸的ACTION_MOVE事件時,將結束該事件回調onSingleTouchEventHandle()並不再處理;

因此,此時回調該事件時,將通知子類處理事件時建議處理為ACTION_UP事件(因為從這個時候開始整個單點觸摸事件已經結束了,之後也不會再響應任何單點觸摸事件)

這裡回調時只是建議而不會修改MotionEvent的事件參數,另外建議處理為ACTION_UP的原因是,單點觸摸事件結束的標志是ACTION_UP,很可能子類需要在這個時候處理某些數據或者保存工作,由於切換為多點觸摸之後不再響應單點觸摸事件;而最終事件結束時的ACTION_UP事件中的參數也很可能與此時的參數不一致(最主要的就是觸摸點的坐標了),因此此回建議處理為ACTION_UP事件

case ACTION_MOVE:
    //若已經觸發了多點觸摸事件且保持在多點觸摸狀態
    //當 multiTouchCount=0 時說明已經退了多點觸摸狀態,恢復到單點觸摸狀態
    //但之後依然不會響應單點觸摸的 MOVE 事件
    if (mIsMultiDown && mMultiTouchCount > 0) {
        //若此前是單點觸摸的移動狀態時
        if (mIsSingleMove) {
            //按單點觸摸的結束狀態處理並不再響應單點觸摸移動狀態
            showMsg("單擊 move 結束");
            //結束單點觸摸事件,並建議處理為 UP 事件
            this.onSingleTouchEventHandle(event, MotionEvent.ACTION_UP);
            mIsSingleMove = false;
        }
        //正常直接多點移動操作
        showMsg("多點觸控 move");
        this.onMultiTouchEventHandle(event, MOTION_EVENT_NOTHING);
    }
break;

此處的實際應用場景在於:當界面被拖動移動時(依賴於MOVE事件),切換到多點觸摸狀態時可以保證界面的正常(依賴於UP事件保存移動後的位置)而不會突然轉到移動到某個位置


觸摸事件處理源碼

以下為觸摸事件處理的完整流程(還有其它的處理邏輯,但屬於輔助性的邏輯)

switch (event.getAction() & MotionEvent.ACTION_MASK) {
    case MotionEvent.ACTION_DOWN:
        //進入單點單擊處理
        showMsg("單點觸摸 down ");
        mIsSingleDown = true;
        //發送延遲取消按下標識消息
        mHandle.sendEmptyMessageDelayed(HANDLE_SINGLE_DOWN, SINGLE_CLICK_INTERVAL);

        //記錄按下坐標
        mDownX = event.getX();
        mDownY = event.getY();
        this.onSingleTouchEventHandle(event, MOTION_EVENT_NOTHING);
        break;


    case MotionEvent.ACTION_POINTER_DOWN:
        //開始多點單擊事件
        showMsg("多點觸控 down");
        mIsMultiDown = true;
        //每發生一次多點觸摸此事件會觸發一次
        //通過此事件可以記錄多點觸摸的次數及判斷是否已經退出多點觸摸狀態(當變量為0時)
        mMultiTouchCount += 1;
        this.onMultiTouchEventHandle(event, MOTION_EVENT_NOTHING);
        break;


    case MotionEvent.ACTION_UP:
        showMsg("單點觸摸 up");
        //任何一種事件中,只要觸發了雙擊事件,則結束事件
        //TODO: 雙擊事件檢測並處理,觸發 break;否則執行單點觸摸抬起事件

        //在處理單擊事件up中,任何時候只要在結束up之前產生任何的多點觸控,都不將此次的事件處理為單點觸摸up
        //因為這時候單點觸摸事件已經不完整了,混合了其它的事件且多點觸摸可能導致原本的單點觸摸事件的坐標數據不正常,所以不再處理單點觸摸事件
        if (!mIsMultiDown && mMultiTouchCount <= 0) {
            //此處分為兩種情況
            //一種是未進行任何多點觸摸狀態的,那麼必定為單,事件必須響應
            //在事件響應處兩個判斷條件是:1.用戶快速單擊,沒有move事件,此時 isInMotionMove=false;
            if (!mIsInMotionMove
                    //2. 用戶慢速單產生了move事件但仍沒有造成多點觸摸事件,此時 isInMotionMove=true 且 isSingleMove=true;
                    || (mIsInMotionMove && mIsSingleMove)) {
                showMsg("單擊 up");
                this.onSingleTouchEventHandle(evenMOTION_EVENT_NOTHING);
            } else {
        //一種是進行了多點觸摸,且在多點觸摸結束之後保持單點觸摸的狀態,此時以多點觸摸按下的時刻處理觸摸事件(即在move中已經按up處理掉事件了)
        //則在完成所有事件之後的up中將不再處理該事事件,即下面的"不處理"
                showMsg("單擊 up 不處理");
            }

        //處理觸摸結束事件,重置變量
        this.finishTouchEvent();
        break;


    case MotionEvent.ACTION_POINTER_UP:
        //當確認進入多點單擊狀態,則執行多點單擊抬起事件
        if (mMultiTouchCount > 0) {
            showMsg("多點觸控 up");
            this.onMultiTouchEventHandle(event, MOTION_EVENT_NOTHING);
        }
        //每次多點觸摸抬起觸發一次,多點觸摸次數-1(直到只剩單點觸摸為止不再觸發此事件,此時變量值為0)
        mMultiTouchCount -= 1;
        break;


    case MotionEvent.ACTION_MOVE:
        //進入移動狀態
        mIsInMotionMove = true;
        //當前不是多點單擊狀態,則進行移動操作
        //若觸發了多點觸摸事件,則結束單點移動事件,進入多點觸摸移動事件

        //結束單點移動操作後在觸摸事件結束之前都不會再執行單點移動操作
        //這種情況是為了避免有可能有用戶單擊移動之後再進行多點觸控,這種情況無法處理為用戶需要移動還是需要縮放
        //而且引起的坐標變化可能導致一些錯亂
        if (!mIsMultiDown && mMultiTouchCount <= 0) {
            showMsg("單點觸摸 move");
            this.onSingleTouchEventHandle(event, MOTION_EVENT_NOTHING);
            mIsSingleMove = true;
            //多點觸摸事件觸發了,進入多點觸摸移動事件
        } else if (mIsMultiDown && mMultiTouchCount > 0) {
            //若此前是單點觸摸的移動狀態時
            if (mIsSingleMove) {
                //按單點觸摸的結束狀態處理並不再響應單點觸摸移動狀態
                showMsg("單擊 move 結束");
                this.onSingleTouchEventHandle(event, MotionEvent.ACTION_UP);
                mIsSingleMove = false;
            }
            //正常直接多點移動操作
            showMsg("多點觸控 move");
            this.onMultiTouchEventHandle(event, MOTION_EVENT_NOTHING);
        }
        break;
}

雙擊事件的優化處理

前面提到,關於雙擊事件的處理邏輯是這樣的:完成一次單擊事件記錄單擊事件標識,第二次觸發單擊事件時,根據此前是否存在第一次單擊事件來確定是否觸發雙擊事件

在這裡需要注意的一個點是,單擊事件存在兩種方式,而每一種方式的完成都是一次單擊事件.
我們定義的雙擊事件是:

兩次一定時間間隔內連續發生的單擊事件即為一次雙擊事件,這裡單擊事件的觸發方式是任意的

而由於單擊事件在ACTION_UP事件中檢測並觸發,兩種方式的單擊事件都需要檢測及處理,所以這個過程可能導致單擊事件會被觸發兩次

基於時間的單擊事件觸發一次 基於距離的單擊事件觸發一次

這種情況下就需要用不同的變量來識別單擊事件的觸發了.需要處理的包括:

是否已經觸發了一次單擊事件(即上一次單擊事件是否還在有效時間間隔內) 當次觸摸事件中是否觸發了單擊事件(不管該事件由哪種方式觸發)

如何檢測當次觸摸事件的單擊事件

由於兩種方式的單擊事件都需要檢測一次,所以可能存在一種情況:(不管哪種方式優先檢測)

假設上一次單擊事件成立(最後一次單擊標識為true) 當第一種方式單擊成立之後,本次觸摸事件單擊事件成功,標識置為true; 當第二種方式檢測時,最後一次單擊標識為true,且本次觸摸事件已經為true(上一個方式已經確定),則會觸發一次雙擊事件 這個過程明顯是存在BUG

因此需要設置不同標識在不同方式的單擊事件中使用;

//是否觸發基於時間的單擊事件
boolean isFireTimeClickEvent=false;
//是否觸發基於距離的單擊事件
boolean isFireDistanceClickEvent=false;
//上一次單擊事件是否被觸發了
boolean isFireLastClickEvent=false;

如何檢測觸發雙擊事件

雙擊事件是基於單擊事件的,兩次連續觸發的單擊事件才構成一次雙擊事件;因此雙擊事件的檢測:

必須在單擊事件檢測之後才檢測雙擊事件 雙擊事件的檢測必須優先於單擊事件的響應 一旦雙擊事件被觸發,則忽略其它所有事件(也不再響應單擊事件)

任何一次單擊事件檢測完畢之後都需要檢測雙擊事件,再執行單擊事件;單擊事件(兩種方式)需要檢測兩次,因此雙擊事件是必須檢測兩次的.
雙擊事件被觸發時,其它事件 ACTON_UP 事件是不會觸發的

//檢測是否觸發雙擊事件
//檢測本次觸摸事件中是否觸發了(任何一種)單擊事件
if((isFireTimeClickEvent || isFireDistanceClickEvent) 
    //檢測上一次單擊事件是否觸發了
    && isFireLastClickEvent){
    //條件成立,觸發雙擊事件
    //同時重置所有相關變量
    //因為雙擊事件已經觸發,保留變量狀態會影響下一次判斷
    isFireTimeClickEvent=false;
    isFireDistanceClickEvent=false;
    isFireLastClickEvent=false;
    //處理了雙擊事件則不再響應任何其它事件
    break;//或者return;
}
//若處理了時間單擊事件,對應標識置為true
isFireTimeClickEvent=true;

//同上檢測雙擊事件
//若處理了距離單擊事件,對應標識置為true
isFireDistanceClickEvent=true;

//保存此次單擊狀態
isFireLastClickEvent = isFireTimeClickEvent || isFireDistanceClickEvent;

輔助補充邏輯

在以上的觸摸處理事件中,我們提到單擊分為兩中方式:

基於時間的單擊事件 基本距離的單擊事件

基於時間的單擊事件處理已經在ACTION_DOWN事件中操作了,通過延時發送取消按下標識,再從ACTION_UP事件中進行判斷是否處理為一次基於時間的單擊事件
基於距離的單擊事件在ACTION_DOWN中沒有任何處理,因為距離本身跟時間沒有任何關系.

在單點觸摸中按下之後,保持任何時間(甚至無窮長),只要在ACTION_UP事件中抬起的坐標與按下坐標值距離在允許范圍內即為一次基於距離的單擊事件

因為存在這種特殊的單擊方式,所以基於距離的單擊事件只跟ACTION_DOWN/ACTION_UP有關;但這是不完善的.

存在一種可能,在單點觸摸中按下之後,觸摸點進行了移動ACTION_MOVE事件,然後再移動回到按下的位置,即ACTION_DOWN的位置坐標,此時在ACTION_UP事件中,觸摸點的坐標沒有變化,ACTION_MOVE中所有的操作對ACTION_UP是透明無效的,這可能會違背了我們需要處理的單擊事件
查看單擊事件的兩種方式

所以需要修正這種可能存在的錯誤;修正方式也很簡單,即然是在ACTION_MOVE中產生的問題,在ACTION_MOVE修正;
修正方式如下:

針對距離單擊事件

//事先定義用於標識觸摸點是否產生超過單擊的允許范圍的移動事件(以下稱為非法事件)
//**針對距離單擊事件**
booelan mIsClickDistanceMove = false;
case MotionEvent.ACTION_MOVE:
    //當前移動過程中,觸摸點未產生非法移動事件
    if (!mIsClickDistanceMove) {
        //進行檢測
        float moveDistanceX = event.getX() - mUpX;
        float moveDistanceY = event.getY() - mUpY;
        //此處稍微增大了移動時的偏移量范圍,因為手指容易抖動,增大容錯率
        int offsetDistance = SINGLE_CLICK_OFFSET_DISTANCE + 20;
        //觸摸點移動超過單擊允許范圍的偏移量
        if (Math.abs(moveDistanceX) > offsetDistance
                || Math.abs(moveDistanceY) > offsetDistance) {
            //產生非法移動事件
            //一旦產生了非法移動事件,則不需要再次檢測了
            mIsClickDistanceMove = true;
        }
    }

小結

以上為所有的觸摸事件的處理方案

主要解決的事件有:單擊/雙擊/移動及所有觸摸事件對應的回調 其中單擊事件分為:
基於時間的單擊事件 基於距離的單擊事件 調整和修正各個事件之間的沖突關系

使用方式

直接繼承此類,重寫所有抽象方法即可.


源碼

/**
 * Created by CT in 2015-08-14
 * 

抽像類,處理觸摸事件,區分單擊及多點觸摸事件

*

此類中使用到handler,請確保使用在UI線程或者是自定義looper的線程中(一般也沒有人會把觸摸事件放在非UI線程吧 =_=)

*/ public abstract class AbsTouchEventHandle implements View.OnTouchListener { /** * 距離雙擊事件 */ public static final int EVENT_DOUBLE_CLICK_BY_TIME = 0; public static final int EVENT_SINGLE_CLICK_BY_TIME = -1; public static final int EVENT_SINGLE_CLICK_BY_DISTANCE = -2; /** * 額外分配的觸摸事件,用於建議優先處理的觸摸事件 */ public static final int MOTION_EVENT_NOTHING = 0; /** * 處理時間單擊事件 */ private static final int CONSUME_LAST_SINGLE_CLICK_EVENT = -1; /** * 處理單點觸摸下的事件 */ private static final int HANDLE_SINGLE_DOWN = 2; private static String TAG = "touch_event"; //已經觸發單擊事件的情況下,是否觸發單點觸摸事件 private boolean mIsTriggerSingleTouchEvent = true; private boolean mIsShowLog = false; private int mMultiTouchCount = 0; //是否開始觸發本次時間單擊事件(整個觸摸事件) private boolean mIsFireTimeClickEvent = false; //是否開始觸發本次距離單擊事件 private boolean mIsFireDistanceClickEvent = false; //是否完成上一次單擊(一次) private boolean mIsFireLastClickEvent = false; //多點觸摸按下 private boolean mIsMultiDown = false; //是否單點觸摸按下 private boolean mIsSingleDown = false; //是否進入單擊移動事件 private boolean mIsSingleMove = false; //是否進入移動事件 private boolean mIsInMotionMove = false; //單次單擊事件中(針對距離單擊),觸摸點是否產生超過單擊的允許范圍的移動事件 private boolean mIsClickDistanceMove = false; //時間單擊事件是否可用 private boolean mIsClickTimeEventEnable = true; //距離單擊事件是否可用 private boolean mIsClickDistanceEventEnable = true; //單擊事件的可持續最長時間間隔(down與up事件之間的間隔) private int SINGLE_CLICK_INTERVAL = 250; //雙擊事件可可持續最長時間間隔(兩次單擊事件之間的間隔) private int DOUBLE_CLICK_INTERVAL = 350; //距離單擊事件最大的允許偏移量大小 private int SINGLE_CLICK_OFFSET_DISTANCE = 10; private float mDownX = 0f; private float mDownY = 0f; private float mUpX = 0f; private float mUpY = 0f; private Handler mHandle = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { //取消完成一次單擊事件的標識(用於識別雙擊事件) case CONSUME_LAST_SINGLE_CLICK_EVENT: mIsFireLastClickEvent = false; break; //取消單點觸摸的有效時間(用於識別基於時間的單擊事件) case HANDLE_SINGLE_DOWN: mIsSingleDown = false; break; } } }; /** * 檢測單擊事件的觸發,觸發單擊事件返回 true,否則返回 false * * @param event 觸摸事件 * @param fireEvent 需要檢測的事件,{@link #EVENT_SINGLE_CLICK_BY_TIME}基於時間的單擊事件; * {@link #EVENT_SINGLE_CLICK_BY_DISTANCE}基於距離的單擊事件 * @return */ private boolean isFireSingleClickEvent(MotionEvent event, int fireEvent) { //不觸發多點觸摸事件的情況下才進行單擊事件的判斷處理 if (!mIsMultiDown) { if (fireEvent == EVENT_SINGLE_CLICK_BY_TIME) { //根據時間處理單擊事件 //觸摸點down之後500ms內觸摸點抬起則認為是一次單擊事件 //兩次單擊事件之間的時間間隔在允許間隔內即為一次雙擊事件 //是否在單點觸摸按下時間間隔內(該變量在按下後指定間隔時間內重置,此處是基於時間的單擊事件) if (mIsSingleDown) { //觸發單擊事件 return true; } } else if (fireEvent == EVENT_SINGLE_CLICK_BY_DISTANCE) { //移動距離單擊處理事件 //觸摸點down事件的坐標與up事件的坐標距離不超過10像素時,認為一次單擊事件(與時間無關) //兩次單擊事件之間的時間間隔在500ms內則認為是一次雙擊事件 mUpX = event.getX(); mUpY = event.getY(); float moveDistanceX = mUpX - mDownX; float moveDistanceY = mUpY - mDownY; //根據觸摸點up與down事件的坐標差判斷是否為單擊事件(不由時間決定) if (Math.abs(moveDistanceX) < SINGLE_CLICK_OFFSET_DISTANCE && Math.abs(moveDistanceY) < SINGLE_CLICK_OFFSET_DISTANCE) { //觸摸單擊事件 return true; } } } return false; } /** * 處理一次單擊事件,此過程不負責單擊事件的檢測,只負責執行 * * @param event * @param handleEvent */ private void handleSingleClickEvent(MotionEvent event, int handleEvent) { if (handleEvent == EVENT_SINGLE_CLICK_BY_TIME) { //完成一次時間單擊事件 showMsg("單擊事件 single"); this.onSingleClickByTime(event); //記錄本次觸摸了單擊事件 mIsFireTimeClickEvent = true; //處理事件為距離單擊事件且在移動過程中不可超過允許范圍 } else if (handleEvent == EVENT_SINGLE_CLICK_BY_DISTANCE && !mIsClickDistanceMove) { //完成一次距離單擊事件 showMsg("單擊事件(距離) single"); this.onSingleClickByDistance(event); //記錄本次觸摸了單擊事件 mIsFireDistanceClickEvent = true; } } /** * 處理一次點擊事件,完整的一個過程,包括檢測/執行/反饋;處理事件包括單擊事件/雙擊事件
* 若觸發雙擊事件,返回true,其它情況返回false * * @param isDistanceEnable 基於距離的單擊事件是否可用 * @param isTimeEnable 基於時間的單擊事件是否可用 * @param fireEvent 需要觸發的事件,一次只能處理一個事件;{@link #EVENT_SINGLE_CLICK_BY_TIME}基於時間的單擊事件 * {@link #EVENT_SINGLE_CLICK_BY_DISTANCE}基於距離的單擊事件 * @param event 觸摸事件 * @return 若觸發雙擊事件, 返回true, 其它情況返回false */ private boolean handleClickEvent(boolean isDistanceEnable, boolean isTimeEnable, int fireEvent, MotionEvent event) { //檢測指定事件是否觸發 if (this.isFireSingleClickEvent(event, fireEvent)) { //若事件觸發,檢測是否已經執行過一次單擊事件且在有效時間間隔內 //並且必須不在是本次觸摸事件中觸發的單擊事件 //mIsFireTimeClickEvent與mIsFireDistanceCLickEvent用於檢測本次是否觸發單擊事件 //mIsFireLastClickEvent用於檢測上一次單擊事件是否在有效時間范圍內 if ((mIsFireTimeClickEvent || mIsFireDistanceClickEvent) && mIsFireLastClickEvent) { //觸摸雙擊事件 showMsg("雙擊事件(時間) double"); this.onDoubleClickByTime(); //取消雙擊事件的標識 this.cancelDoubleClickEvent(EVENT_DOUBLE_CLICK_BY_TIME); //返回已觸發雙擊事件 return true; } else { //未執行過一次單擊事件 //觸發對應的單擊事件 if (fireEvent == EVENT_SINGLE_CLICK_BY_TIME && isTimeEnable) { this.handleSingleClickEvent(event, fireEvent); } else if (fireEvent == EVENT_SINGLE_CLICK_BY_DISTANCE && isDistanceEnable) { this.handleSingleClickEvent(event, fireEvent); } } } return false; } /** * 結束觸摸事件,重置所有應該重置的變量 */ private void finishTouchEvent() { //取消移動狀態的記錄 mIsInMotionMove = false; //多點單擊的標志必須在此處才可以被重置 //因為多點單擊的抬起事件優先處理於單擊的抬起事件 //如果在多點單擊的抬起事件時重置該變量則會導致上面的判斷百分百是成立的 mIsMultiDown = false; mIsSingleDown = false; mIsSingleMove = false; mIsFireTimeClickEvent = false; mIsClickDistanceMove = false; mMultiTouchCount = 0; //記錄此觸摸事件中是否產生了單擊事件(用於後續的雙擊事件判斷) mIsFireLastClickEvent = mIsFireTimeClickEvent || mIsFireDistanceClickEvent; //重置本次單擊事件的標識 mIsFireTimeClickEvent = false; mIsFireDistanceClickEvent = false; //發送延遲消費記錄的單擊事件標識 mHandle.sendEmptyMessageDelayed(CONSUME_LAST_SINGLE_CLICK_EVENT, DOUBLE_CLICK_INTERVAL); //mIsFireLastClickEvent 此變量不可以重置,這是保存已經完成一次單擊事件的標識,用於後續識別雙擊事件 } @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: //進入單點單擊處理 showMsg("單點觸摸 down "); mIsSingleDown = true; mHandle.sendEmptyMessageDelayed(HANDLE_SINGLE_DOWN, SINGLE_CLICK_INTERVAL); mDownX = event.getX(); mDownY = event.getY(); this.onSingleTouchEventHandle(event, MOTION_EVENT_NOTHING); break; case MotionEvent.ACTION_POINTER_DOWN: //開始多點單擊事件 showMsg("多點觸控 down"); mIsMultiDown = true; mMultiTouchCount += 1; this.onMultiTouchEventHandle(event, MOTION_EVENT_NOTHING); break; case MotionEvent.ACTION_UP: showMsg("單點觸摸 up"); //任何一種事件中,只要觸發了雙擊事件,則結束事件 //優先檢測基於距離的單擊事件 if (this.handleClickEvent(mIsClickDistanceEventEnable, mIsClickTimeEventEnable, EVENT_SINGLE_CLICK_BY_DISTANCE, event)) { this.finishTouchEvent(); break; } //檢測基於時間的單擊事件 if (this.handleClickEvent(mIsClickDistanceEventEnable, mIsClickTimeEventEnable, EVENT_SINGLE_CLICK_BY_TIME, event)) { this.finishTouchEvent(); break; } //允許觸發單點觸摸事件 if (mIsTriggerSingleTouchEvent) { //在處理單擊事件up中,任何時候只要在結束up之前產生任何的多點觸控,都不將此次的事件處理為單擊up //因為這時候單點觸摸事件已經不完整了,混合了其它的事件 //而且多點觸摸可能導致原本的單點觸摸事件的坐標數據等獲取不正常,所以不再處理單點觸摸事件 if (!mIsMultiDown && mMultiTouchCount <= 0) { //此處分為兩種情況 //一種是未進行任何多點觸摸狀態的,那麼必定為單點觸摸,事件必須響應 //在事件響應處兩個判斷條件是:1.用戶快速單擊,不產生move事件;此時 isInMotionMove=false if (!mIsInMotionMove //2. 用戶慢速單擊, 產生了move事件但仍沒有造成多點觸摸事件; //此時 isInMotionMove=true 且 isSingleMove=true || (mIsInMotionMove && mIsSingleMove)) { showMsg("單擊 up"); this.onSingleTouchEventHandle(event, MOTION_EVENT_NOTHING); } else { //一種是進行了多點觸摸,且在多點觸摸之後保持著單點觸摸的狀態,此時以多點觸摸按下的時刻處理掉單點觸摸事件(即在move中已經按up處理掉事件了) //則在完成所有事件之後的up中將不再處理該事件,即下面的"不處理" showMsg("單擊 up 不處理"); } } } //處理觸摸結束事件,重置變量 this.finishTouchEvent(); break; case MotionEvent.ACTION_POINTER_UP: //當確認進入多點單擊狀態,則執行多點單擊抬起事件 if (mMultiTouchCount > 0) { showMsg("多點觸控 up"); this.onMultiTouchEventHandle(event, MOTION_EVENT_NOTHING); } mMultiTouchCount -= 1; //此處不重置mIsMultiDown變量是因為後面檢測單擊事件的up與多點觸控的up需要 //而且此處不重置並不會對其它的部分造成影響 break; case MotionEvent.ACTION_MOVE: //進入移動狀態 mIsInMotionMove = true; //當前不是多點單擊狀態,則進行移動操作 //若觸發了多點觸摸事件,則結束單點移動事件,進入多點觸摸移動事件 //結束單點移動操作後在觸摸事件結束之前都不會再執行單點移動操作 //這種情況是為了避免有可能有用戶單擊移動之後再進行多點觸控,這種情況無法處理為用戶需要移動還是需要縮放 //而且引起的坐標變化可能導致一些錯亂 if (!mIsMultiDown && mMultiTouchCount <= 0) { showMsg("單點觸摸 move"); this.onSingleTouchEventHandle(event, MOTION_EVENT_NOTHING); mIsSingleMove = true; //多點觸摸事件觸發了,進入多點觸摸移動事件 } else if (mIsMultiDown && mMultiTouchCount > 0) { //若此前是單點觸摸的移動狀態時 if (mIsSingleMove) { //按單點觸摸的結束狀態處理並不再響應單點觸摸移動狀態 showMsg("單點觸摸 move 結束"); this.onSingleTouchEventHandle(event, MotionEvent.ACTION_UP); mIsSingleMove = false; } //正常直接多點移動操作 showMsg("多點觸控 move"); this.onMultiTouchEventHandle(event, MOTION_EVENT_NOTHING); } //當可能發生一次距離單擊事件時,需要檢測是否產生了超過偏移量的移動距離 //此處不在up事件中判斷是因為: //存在一種可能是(由於距離單擊事件有足夠長的時間),在move的時候移動距離超過偏移量 //但之後又移動到單擊位置的坐標,在up事件中move所引起的坐標變化是不可見的 //所以於對up事件,down的坐標與up的坐標偏移量是允許范圍內,會處理為一次距離單擊事件 //但實際上這是一次移動事件 if (!mIsClickDistanceMove) { float moveDistanceX = event.getX() - mUpX; float moveDistanceY = event.getY() - mUpY; int offsetDistance = SINGLE_CLICK_OFFSET_DISTANCE + 20; if (Math.abs(moveDistanceX) > offsetDistance || Math.abs(moveDistanceY) > offsetDistance) { //一旦取消了距離單擊事件有效性的標識,則不需要再次檢測了 mIsClickDistanceMove = true; } } break; } return true; } /** * 設置單擊有效的時間間隔(down與up事件的最長允許時間間隔),默認250ms * * @param interval */ public void setSingleClickInterval(int interval) { this.SINGLE_CLICK_INTERVAL = interval; } /** * 設置雙擊有效時間間隔(兩次單擊事件的最長允許時間間隔),默認350ms * * @param interval */ public void setDoubleClickInterval(int interval) { this.DOUBLE_CLICK_INTERVAL = interval; } /** * 設置單擊允許的最大偏移量范圍(坐標偏移像素值),默認10像素 * * @param offsetDistance */ public void setSingleClickOffsetDistance(int offsetDistance) { this.SINGLE_CLICK_OFFSET_DISTANCE = offsetDistance; } /** * 基於時間的單擊事件是否可響應,若為true則事件觸發時回調響應;若為false事件觸發時不回調 * * @param isEnabled */ public void setIsEnableSingleClickByTime(boolean isEnabled) { this.mIsClickTimeEventEnable = isEnabled; } /** * 基於距離的單擊事件是否可響應,若為true則事件觸發時回調響應;若為false事件觸發時不回調 * * @param isEnabled */ public void setIsEnableSingleClickByDistance(boolean isEnabled) { this.mIsClickDistanceEventEnable = isEnabled; } /** * 設置在觸發單擊事件時是否同時觸發單點觸摸事件;默認觸發 *

單擊事件本身屬於單點觸摸事件之中的一種,只是觸摸時間在500ms以內則認為是單擊事件,但同時是滿足觸發單點觸摸事件的(此處僅指up事件)

* 在up事件中,事件優先處理級如下: 雙擊 > 單擊 > 普通的UP事件 * * @param isTrigger true為同時觸發,false為忽略單點觸摸事件 */ public void setIsTriggerSingleTouchEvent(boolean isTrigger) { this.mIsTriggerSingleTouchEvent = isTrigger; } /** * 設置是否顯示log * * @param isShowLog * @param tag tag為顯示log的標志,可為null,tag為null時使用默認標志"touch_event" */ public void setIsShowLog(boolean isShowLog, String tag) { if (tag != null) { TAG = tag; } else { TAG = "touch_event"; } this.mIsShowLog = isShowLog; } /** * 打印默認的log,默認標志為:touch_event * * @param msg 打印消息 */ public void showMsg(String msg) { if (mIsShowLog) { Log.i(TAG, msg); } } /** * 打印log * * @param tag 標志tag * @param msg 打印信息 */ public void showMsg(String tag, String msg) { if (mIsShowLog) { Log.i(tag, msg); } } /** * 取消事件有效性,目前僅對雙擊事件有效{@link #onDoubleClickByTime()}
* 每一次單擊事件之後會有一個暫存的延遲標識,在允許時間內再次觸發單擊事件時,此時不會響應單擊事件,而是轉化成雙擊事件 * * @param event 需要取消的事件 */ public void cancelDoubleClickEvent(int event) { switch (event) { case EVENT_DOUBLE_CLICK_BY_TIME: //一旦取消雙擊事件,所有有關的變量都重置 this.mIsFireLastClickEvent = false; this.mIsFireTimeClickEvent = false; this.mIsFireDistanceClickEvent = false; break; } } /** * 單點觸摸事件處理 * * @param event 單點觸摸事件 * @param extraMotionEvent 建議處理的額外事件,如果不需要進行額外處理則該參數值為{@link #MOTION_EVENT_NOTHING} *

存在此參數是因為可能用戶進行單點觸摸並移動之後,會再進行多點觸摸(此時並沒有松開觸摸),在這種情況下是無法分辨需要處理的是單點觸摸事件還是多點觸摸事件. * 此時會傳遞此參數值為單點觸摸的{@link MotionEvent#ACTION_UP},建議按抬起事件處理並結束事件

*/ public abstract void onSingleTouchEventHandle(MotionEvent event, int extraMotionEvent); /** * 多點觸摸事件處理(兩點觸摸,暫沒有做其它任何多點觸摸) * * @param event 多點觸摸事件 * @param extraMotionEvent 建議處理的額外事件,如果不需要進行額外處理則該參數值為{@link #MOTION_EVENT_NOTHING} */ public abstract void onMultiTouchEventHandle(MotionEvent event, int extraMotionEvent); /** * 單擊事件處理,由於只要觸摸到屏幕且時間足夠長,就可以產生move事件,並不一定需要移動觸摸才能產生move事件, * 所以產生單擊事件的同時也會觸發up事件{@link #onSingleTouchEventHandle(MotionEvent, int)}, *

單擊事件僅僅只能控制觸摸時間少於500ms的觸摸事件,超過500ms將不會觸摸單擊事件

* * @param event 單擊觸摸事件 */ public abstract void onSingleClickByTime(MotionEvent event); /** * 單擊事件處理,觸摸點down的坐標與up坐標距離差不大於10像素則認為是一次單擊,與時間無關 * * @param event 單擊觸摸事件 */ public abstract void onSingleClickByDistance(MotionEvent event); /** * 雙擊事件處理,每次單擊判斷由時間決定,參考{@link #onSingleClickByTime(MotionEvent)} */ public abstract void onDoubleClickByTime(); }

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved