Android 事件分发机制详解(1)-View

首先参考郭霖大神两篇很详细的关于事件分发机制的文章

http://blog.csdn.net/guolin_blog/article/details/9097463

http://blog.csdn.net/guolin_blog/article/details/9153747

文章有点长,所以自行整理重点方便以后自行查阅

View.dispatchTouchEvent()详解

只要你触摸到了任何一个控件,就一定会调用该控件的dispatchTouchEvent方法,从当前类一直往继承关系向上查找dispatchTouchEvent方法,默认情况下View实现dispatchTouchEvent这个方法,如下

  1. public boolean dispatchTouchEvent(MotionEvent event) {  
  2.     if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
  3.            mOnTouchListener.onTouch(this, event)) {  
  4.               return true
  5.     }  
  6.     return onTouchEvent(event); 
  7. }  

mOnTouchListener在如下方法中被赋值
  1. public void setOnTouchListener(OnTouchListener l) {  
  2.     mOnTouchListener = l;  
  3. }  

同时说明如果mOnTouchListener.onTouch(this, event)返回true,那么dispatchTouchEvent函数执行完毕,那么接下来执行不到控件的onTouchEvent方法和绑定的onClick方法,当前事件结束。

举例子说明

  1.         myButton.setOnTouchListener(new OnTouchListener() {  
  2.             @Override  
  3.             public boolean onTouch(View v, MotionEvent event) {  
  4.                 int action = event.getAction();  
  5.                 switch (action) {  
  6.                 case MotionEvent.ACTION_DOWN:// 0  
  7.                     Log.d(TAG, "onTouch ACTION_DOWN");  
  8.                     break;  
  9.                 case MotionEvent.ACTION_UP:// 1  
  10.                     Log.d(TAG, "onTouch ACTION_UP");  
  11.                     break;  
  12.                 default:  
  13.                     break;  
  14.                 }  
  15.                 return true;
  16.             }  
  17.         });  
  18.         myButton.setOnClickListener(new OnClickListener() {  
  19.             @Override  
  20.             public void onClick(View v) {  
  21.                 // TODO Auto-generated method stub  
  22.                 Log.d(TAG, "onClick");  
  23.             }  
  24.         });  

那么onClick永远也打印不了,同时自定义的myButton的onTouchEvent也永远执行不了


  1. @Override  
  2. public boolean onTouchEvent(MotionEvent event) {  
  3.     // TODO Auto-generated method stub  
  4.     Log.d("Bill""onTouchEvent " + event.getAction());  
  5.     //如果注释了此句,那么不管这个方法返回true-false,都不会执行到onClick方法,因为dispatchTouchEvent不会调用View的onTouchEvent方法   
  6.     super.onTouchEvent(event);
  7.     return false;//此时返回true-false无所谓,因为调用了super.onTouchEvent(event),会执行View的onTouchEvent也就会执行到
  8.     //performClick->onClick方法  
  9. }  

View.onTouchEvent()方法详解

  1. public boolean onTouchEvent(MotionEvent event) {  
  2.     final int viewFlags = mViewFlags;  
  3.     if ((viewFlags & ENABLED_MASK) == DISABLED) {  
  4.         // A disabled view that is clickable still consumes the touch  
  5.         // events, it just doesn't respond to them.  
  6.         return (((viewFlags & CLICKABLE) == CLICKABLE ||  
  7.                 (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));  
  8.     }  
  9.     if (mTouchDelegate != null) {  
  10.         if (mTouchDelegate.onTouchEvent(event)) {  
  11.             return true;  
  12.         }  
  13.     }  
  14.     if (((viewFlags & CLICKABLE) == CLICKABLE ||  
  15.            (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {  
  16.         switch (event.getAction()) {  
  17.             case MotionEvent.ACTION_UP
  18.                 boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;  
  19.                 if ((mPrivateFlags & PRESSED) != 0 || prepressed) {  
  20.                     // take focus if we don't have it already and we should in  
  21.                     // touch mode.  
  22.                     boolean focusTaken = false;  
  23.                     if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {  
  24.                         focusTaken = requestFocus();  
  25.                     }  
  26.                     if (!mHasPerformedLongPress) {  
  27.                         // This is a tap, so remove the longpress check  
  28.                         removeLongPressCallback();  
  29.                         // Only perform take click actions if we were in the pressed state  
  30.                         if (!focusTaken) {  
  31.                             // Use a Runnable and post this rather than calling  
  32.                             // performClick directly. This lets other visual state  
  33.                             // of the view update before click actions start.  
  34.                             if (mPerformClick == null) {  
  35.                                 mPerformClick = new PerformClick();  
  36.                             }  
  37.                             if (!post(mPerformClick)) {  
  38.                                 <span style="color:#FF0000;">performClick();</span>  
  39.                             }  
  40.                         }  
  41.                     }  
  42.                     if (mUnsetPressedState == null) {  
  43.                         mUnsetPressedState = new UnsetPressedState();  
  44.                     }  
  45.                     if (prepressed) {  
  46.                         mPrivateFlags |= PRESSED;  
  47.                         refreshDrawableState();  
  48.                         postDelayed(mUnsetPressedState,  
  49.                                 ViewConfiguration.getPressedStateDuration());  
  50.                     } else if (!post(mUnsetPressedState)) {  
  51.                         // If the post failed, unpress right now  
  52.                         mUnsetPressedState.run();  
  53.                     }  
  54.                     removeTapCallback();  
  55.                 }  
  56.                 break;  
  57.             case MotionEvent.ACTION_DOWN:  
  58.                 if (mPendingCheckForTap == null) {  
  59.                     mPendingCheckForTap = new CheckForTap();  
  60.                 }  
  61.                 mPrivateFlags |= PREPRESSED;  
  62.                 mHasPerformedLongPress = false;  
  63.                 postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());  
  64.                 break;  
  65.             case MotionEvent.ACTION_CANCEL:  
  66.                 mPrivateFlags &= ~PRESSED;  
  67.                 refreshDrawableState();  
  68.                 removeTapCallback();  
  69.                 break;  
  70.             case MotionEvent.ACTION_MOVE:  
  71.                 final int x = (int) event.getX();  
  72.                 final int y = (int) event.getY();  
  73.                 // Be lenient about moving outside of buttons  
  74.                 int slop = mTouchSlop;  
  75.                 if ((x < 0 - slop) || (x >= getWidth() + slop) ||  
  76.                         (y < 0 - slop) || (y >= getHeight() + slop)) {  
  77.                     // Outside button  
  78.                     removeTapCallback();  
  79.                     if ((mPrivateFlags & PRESSED) != 0) {  
  80.                         // Remove any future long press/tap checks  
  81.                         removeLongPressCallback();  
  82.                         // Need to switch from pressed to not pressed  
  83.                         mPrivateFlags &= ~PRESSED;  
  84.                         refreshDrawableState();  
  85.                     }  
  86.                 }  
  87.                 break;  
  88.         }  
  89.         return true;
  90.     }  
  91.     return false
  92. }  
说明两个问题,

1. 该控件必须是可点击的才能执行到performClick->onClick,此时onTouchEvent返回true,从而dispatchTouchEvent方法返回true,所以会会触发一系列的ACTION_DOWN,ACTION_MOVE,ACTION_UP等事件,否则被第一事件ACTION_DOWN中断,因为只有当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action

2. performClick->onClick是在MotionEvent.ACTION_UP中调用而不是第一个事件.ACTION_DOWN被调用。

举例说明ImageView默认情况下是不可点击的,所以只会触发ACTION_DOWN事件,除非给ImageView增加一个android:clickable="true"的属性,这样ImageView变成可点击的之后,即使在onTouch里返回了false,ACTION_DOWN之后的其它action也是可以得到执行的。


performClick()方法逻辑

  1. public boolean performClick() {  
  2.     sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);  
  3.     if (mOnClickListener != null) {  
  4.         playSoundEffect(SoundEffectConstants.CLICK);  
  5.         mOnClickListener.onClick(this);
  6.         return true;  
  7.     }  
  8.     return false;  
  9. }  
同上mOnTouchListener
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值