安卓觸摸事件與點擊事件的區別(Touch&Click)

針對屏幕上的一個View控件,Android如何區分應當觸發onTouchEvent,還是onClick,亦或是onLongClick事件?

Android中,一次用戶操作可以被不同的View按次序分別處理,並將完全響應了用戶一次UI操作稱之爲消費了該事件(consume),那麼Android是按什麼次序將事件傳遞的呢?又在什麼情況下判定爲消費了該事件?

      搞清楚這些問題對於編寫出能正確響應UI操作的代碼是很重要的,尤其當屏幕上的不同View需要針對此次UI操作做出各種不同響應的時候更是如此,一個典型例子就是用戶在桌面上放置了一個Widget,那麼當用戶針對widget做各種操作時,桌面本身有的時候要對用戶的操作做出響應,有時忽略。只有搞清楚事件觸發和傳遞的機制纔有可能保證在界面佈局非常複雜的情況下,UI控件仍然能正確響應用戶操作。

 

1.  onTouchEvent

     onTouchEvent中要處理的最常用的3個事件就是:ACTION_DOWN、ACTION_MOVE、ACTION_UP。

     這三個事件標識出了最基本的用戶觸摸屏幕的操作,含義也很清楚。雖然大家天天都在用它們,但是有一點請留意,ACTION_DOWN事件作爲起始事件,它的重要性是要超過ACTION_MOVEACTION_UP的,如果發生了ACTION_MOVE或者ACTION_UP,那麼一定曾經發生了ACTION_DOWN

     從Android的源代碼中能看到基於這種不同重要性的理解而實現的一些交互機制,SDK中也有明確的提及,例如在ViewGrouponInterceptTouchEvent方法中,如果在ACTION_DOWN事件中返回了true,那麼後續的事件將直接發給onTouchEvent,而不是繼續發給onInterceptTouchEvent

 

2.  onClickonLongClickonTouchEvent

     曾經看過一篇帖子提到,如果在View中處理了onTouchEvent,那麼就不用再處理onClick了,因爲Android只會觸發其中一個方法。這個理解是不太正確的,針對某個view,用戶完成了一次觸碰操作,顯然從傳感器上得到的信號是手指按下和擡起兩個操作,我們可以理解爲一次Click,也可以理解爲發生了一次ACTION_DOWN和ACTION_UP,那麼Android是如何理解和處理的呢?

     在Android中,onClickonLongClick的觸發是和ACTION_DOWNACTION_UP相關的,在時序上,如果我們在一個View中同時覆寫了onClickonLongClickonTouchEvent的話,onTouchEvent是最先捕捉到ACTION_DOWNACTION_UP事件的,其次纔可能觸發onClick或者onLongClick。主要的邏輯在View.java中的onTouchEvent方法中實現的:

 

java代碼:
  1. case MotionEvent.ACTION_DOWN:  
  2.   
  3.     mPrivateFlags |= PRESSED;  
  4.   
  5.     refreshDrawableState();  
  6.   
  7.     if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {  
  8.   
  9.          postCheckForLongClick();   
  10.   
  11.     }  
  12.   
  13.     break;  
  14.   
  15. case MotionEvent.ACTION_UP:  
  16.   
  17.     if ((mPrivateFlags & PRESSED) != 0) {  
  18.   
  19.          boolean focusTaken = false;  
  20.   
  21.          if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {  
  22.   
  23.                focusTaken = requestFocus();  
  24.   
  25.          }  
  26.   
  27.    
  28.   
  29.     if (!mHasPerformedLongPress) {  
  30.   
  31.        if (mPendingCheckForLongPress != null) {  
  32.   
  33.              removeCallbacks(mPendingCheckForLongPress);  
  34.   
  35.        }  
  36.   
  37.        if (!focusTaken) {  
  38.   
  39.               performClick();  
  40.   
  41.        }  
  42.   
  43.     }  
  44.   
  45.     …  
  46.   
  47.     break;  
  48.   
  49.   
  50.      可以看到,Click的觸發是在系統捕捉到ACTION_UP後發生並由performClick()執行的,performClick裏會調用先前註冊的監聽器的onClick()方法:  
  51.   
  52. public boolean performClick() {  
  53.   
  54.     …  
  55.   
  56.     if (mOnClickListener != null) {  
  57.   
  58.         playSoundEffect(SoundEffectConstants.CLICK);  
  59.   
  60.         mOnClickListener.onClick(this);  
  61.   
  62.         return true;  
  63.   
  64.     }  
  65.   
  66.         return false;  
  67.   
  68. }  
  69.   
  70.    
  71.   
  72. LongClick的觸發則是從ACTION_DOWN開始,由postCheckForLongClick()方法完成:  
  73.   
  74. private void postCheckForLongClick() {  
  75.   
  76.      mHasPerformedLongPress = false;  
  77.   
  78.      if (mPendingCheckForLongPress == null) {  
  79.   
  80.          mPendingCheckForLongPress = new CheckForLongPress();  
  81.   
  82.      }  
  83.   
  84.      mPendingCheckForLongPress.rememberWindowAttachCount();  
  85.   
  86.      postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout());  
  87.   
  88. }  
  89.   
  90. 可以看到,在ACTION_DOWN事件被捕捉後,系統會開始觸發一個postDelayed操作,delay的時間在Eclair2.1上爲500ms,500ms後會觸發CheckForLongPress線程的執行:  
  91.   
  92. class CheckForLongPress implements Runnable {  
  93.   
  94. …  
  95.   
  96.         public void run() {  
  97.   
  98.             if (isPressed() && (mParent != null)  
  99.   
  100.                     && mOriginalWindowAttachCount == mWindowAttachCount) {  
  101.   
  102.                 if (performLongClick()) {  
  103.   
  104.                     mHasPerformedLongPress = true;  
  105.   
  106.                 }  
  107.   
  108.             }  
  109.   
  110.         }  
  111.   
  112. …  
  113.   
  114. }  
  115.   
  116.   
  117. 如果各種條件都滿足,那麼在CheckForLongPress中執行performLongClick(),在這個方法中將調用onLongClick():  
  118.   
  119. public boolean performLongClick() {  
  120.   
  121.       …  
  122.   
  123.       if (mOnLongClickListener != null) {  
  124.   
  125.           handled = mOnLongClickListener.onLongClick(View.this);  
  126.   
  127.       }  
  128.   
  129.       …  
  130.   
  131. }  
  132.   
  133.   
  134. 從實現中可以看到onClick()和onLongClick()方法是由ACTION_DOWN和ACTION_UP事件捕捉後根據各種情況最終確定是否觸發的,也就是說如果我們在一個Activity或者View中同時監聽或者覆寫了onClick(),onLongClick()和onTouchEvent()方法,並不意味着只會發生其中一種。  
原文地址:http://sishuok.com/forum/blogPost/list/3813.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章