安卓触摸事件与点击事件的区别(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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值