您当前的位置: 首页 >  android

xiangzhihong8

暂无认证

  • 0浏览

    0关注

    1324博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

细说Android事件传递

xiangzhihong8 发布时间:2016-05-04 18:55:36 ,浏览量:0

一、View的dispatchTouchEvent和onTouchEvent
探讨Android事件传递机制前,明确android的两大基础控件类型:View和ViewGroup。View即普通的控件,没有子布局的,如Button、TextView. ViewGroup继承自View,表示可以有子控件,如Linearlayout、Listview这些。而事件即MotionEvent,最重要的有3个:
(1)MotionEvent.ACTION_DOWN  按下View,是所有事件的开始
(2)MotionEvent.ACTION_MOVE   滑动事件
(3)MotionEvent.ACTION_UP       与down对应,表示抬起
另外,明确事件传递机制的最终目的都是为了触发执行View的点击监听和触摸监听:
******.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub Log.i(tag, "testLinelayout---onClick..."); } });
*******.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub return false; } });
我们简称为onClick监听和onTouch监听,一般程序会注册这两个监听。从上面可以看到,onTouch监听里默认return false。不要小看了这个return false,后面可以看到它有大用。
对于View来说,事件传递机制有两个函数:dispatchTouchEvent负责分发事件,在dispatch***里又会调用onTouchEvent表示执行事件,或者说消费事件,结合源码分析其流程。事件传递的入口是View的dispatchTouchEvent()函数:
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1.     /** 
  2.      * Pass the touch screen motion event down to the target view, or this 
  3.      * view if it is the target. 
  4.      * 
  5.      * @param event The motion event to be dispatched. 
  6.      * @return True if the event was handled by the view, false otherwise. 
  7.      */  
  8.     public boolean dispatchTouchEvent(MotionEvent event) {  
  9.         if (mInputEventConsistencyVerifier != null) {  
  10.             mInputEventConsistencyVerifier.onTouchEvent(event, 0);  
  11.         }  
  12.   
  13.         if (onFilterTouchEventForSecurity(event)) {  
  14.             //noinspection SimplifiableIfStatement  
  15.             ListenerInfo li = mListenerInfo;  
  16.             if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED  
  17.                     && li.mOnTouchListener.onTouch(this, event)) {  
  18.                 return true;  
  19.             }  
  20.   
  21.             if (onTouchEvent(event)) {  
  22.                 return true;  
  23.             }  
  24.         }  
  25.   
  26.         if (mInputEventConsistencyVerifier != null) {  
  27.             mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);  
  28.         }  
  29.         return false;  
  30.     }  
找到这个判断:
            if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED                     && li.mOnTouchListener.onTouch(this, event)) {                 return true;             }
他会执行View的OnTouchListener.onTouch这个函数,也就是上面说的onTouch监听。里面有三个判断,如果三个都为1,就会执行return true,不往下走了。而默认的onTouch监听返回false,只要一个是false,就不会返回true。接着往下看,程序执行onTouchEvent:
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1.             if (onTouchEvent(event)) {  
  2.                 return true;  
  3.             }  
onTouchEvent的源码比较多,贴最重要的:
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1.                         if (!mHasPerformedLongPress) {  
  2.                             // This is a tap, so remove the longpress check  
  3.                             removeLongPressCallback();  
  4.   
  5.                             // Only perform take click actions if we were in the pressed state  
  6.                             if (!focusTaken) {  
  7.                                 // Use a Runnable and post this rather than calling  
  8.                                 // performClick directly. This lets other visual state  
  9.                                 // of the view update before click actions start.  
  10.                                 if (mPerformClick == null) {  
  11.                                     mPerformClick = new PerformClick();  
  12.                                 }  
  13.                                 if (!post(mPerformClick)) {  
  14.                                     performClick();  
  15.                                 }  
  16.                             }  
  17.                         }  
可以看到有个performClick(),它的源码里有这么一句 li.mOnClickListener.onClick(this);
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1.     public boolean performClick() {  
  2.         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);  
  3.   
  4.         ListenerInfo li = mListenerInfo;  
  5.         if (li != null && li.mOnClickListener != null) {  
  6.             playSoundEffect(SoundEffectConstants.CLICK);  
  7.             li.mOnClickListener.onClick(this);  
  8.             return true;  
  9.         }  
  10.   
  11.         return false;  
  12.     }  
终于对上了,它执行了我们注册的onClick监听。当然执行前会经过一系列判断,是否注册了监听等。 总结:
1、事件入口是dispatchTouchEvent(),它会先执行注册的onTouch监听,如果一切顺利的话,接着执行onTouchEvent,在onTouchEvent里会执行onClick监听。
2、无论是dispatchTouchEvent还是onTouchEvent,如果返回true表示这个事件已经被消费、处理了,不再往下传了。在dispathTouchEvent的源码里可以看到,如果onTouchEvent返回了true,那么它也返回true。如果dispatch***在执行onTouch监听的时候,onTouch返回了true,那么它也返回true,这个事件提前被onTouch消费掉了。就不再执行onTouchEvent了,更别说onClick监听了。
3、我们通常在onTouch监听了设置图片一旦被触摸就改变它的背景、透明度之类的,这个onTouch表示事件的时机。而在onClick监听了去具体干某些事。
下面通过代码来说明,自定义一个TestButton继承自Button,重写它的dispath***和onTouchEvent方法,为了简单只关注down和up事件。
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. package org.yanzi.ui;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.util.Log;  
  6. import android.view.MotionEvent;  
  7. import android.widget.Button;  
  8.   
  9. public class TestButton extends Button {  
  10.     private final static String tag = "yan";  
  11.     public TestButton(Context context, AttributeSet attrs) {  
  12.         super(context, attrs);  
  13.         // TODO Auto-generated constructor stub  
  14.     }  
  15.     @Override  
  16.     public boolean onTouchEvent(MotionEvent event) {  
  17.         // TODO Auto-generated method stub  
  18.         switch(event.getAction()){  
  19.         case MotionEvent.ACTION_DOWN:  
  20.             Log.i(tag, "TestButton-onTouchEvent-ACTION_DOWN...");  
  21.             break;  
  22.         case MotionEvent.ACTION_UP:  
  23.             Log.i(tag, "TestButton-onTouchEvent-ACTION_UP...");  
  24.             break;  
  25.         default:break;  
  26.         }  
  27.         return super.onTouchEvent(event);  
  28.     }  
  29.   
  30.     @Override  
  31.     public boolean dispatchTouchEvent(MotionEvent event) {  
  32.         // TODO Auto-generated method stub  
  33.         switch(event.getAction()){  
  34.         case MotionEvent.ACTION_DOWN:  
  35.             Log.i(tag, "TestButton-dispatchTouchEvent-ACTION_DOWN...");  
  36.             break;  
  37.         case MotionEvent.ACTION_UP:  
  38.             Log.i(tag, "TestButton-dispatchTouchEvent-ACTION_UP...");  
  39.             break;  
  40.         default:break;  
  41.         }  
  42.           
  43.         return super.dispatchTouchEvent(event);  
  44.     }  
  45.   
  46. }  
  47.   
在Activity里注册两个监听:
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1.       testBtn.setOnClickListener(new View.OnClickListener() {  
  2.   
  3.             @Override  
  4.             public void onClick(View v) {  
  5.                 // TODO Auto-generated method stub  
  6.                 Log.i(tag, "testBtn---onClick...");  
  7.             }  
  8.         });  
  9.         testBtn.setOnTouchListener(new View.OnTouchListener() {  
  10.   
  11.             @Override  
  12.             public boolean onTouch(View v, MotionEvent event) {  
  13.                 // TODO Auto-generated method stub  
  14.                 switch(event.getAction()){  
  15.                 case MotionEvent.ACTION_DOWN:  
  16.                     Log.i(tag, "testBtn-onTouch-ACTION_DOWN...");  
  17.                     break;  
  18.                 case MotionEvent.ACTION_UP:  
  19.                     Log.i(tag, "testBtn-onTouch-ACTION_UP...");  
  20.                     break;  
  21.                 default:break;  
  22.   
  23.                 }  
  24.                 return false;  
  25.             }  
  26.         });  
同时复写Activity的dispatch方法和onTouchEvent方法:
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2.     public boolean dispatchTouchEvent(MotionEvent ev) {  
  3.         // TODO Auto-generated method stub  
  4.         switch(ev.getAction()){  
  5.         case MotionEvent.ACTION_DOWN:  
  6.             Log.i(tag, "MainActivity-dispatchTouchEvent-ACTION_DOWN...");  
  7.             break;  
  8.         case MotionEvent.ACTION_UP:  
  9.             Log.i(tag, "MainActivity-dispatchTouchEvent-ACTION_UP...");  
  10.             break;  
  11.         default:break;  
  12.         }  
  13.         return super.dispatchTouchEvent(ev);  
  14.     }  
  15.   
  16.     @Override  
  17.     public boolean onTouchEvent(MotionEvent event) {  
  18.         // TODO Auto-generated method stub  
  19.         switch(event.getAction()){  
  20.         case MotionEvent.ACTION_DOWN:  
  21.             Log.i(tag, "MainActivity-onTouchEvent-ACTION_DOWN...");  
  22.             break;  
  23.         case MotionEvent.ACTION_UP:  
  24.             Log.i(tag, "MainActivity-onTouchEvent-ACTION_UP...");  
  25.             break;  
  26.         default:break;  
  27.         }  
  28.         return super.onTouchEvent(event);  
  29.     }  
最终一次点击,打印信息如下:
Line 33: 01-08 14:59:45.847 I/yan     ( 4613): MainActivity-dispatchTouchEvent-ACTION_DOWN... Line 35: 01-08 14:59:45.849 I/yan     ( 4613): TestButton-dispatchTouchEvent-ACTION_DOWN... Line 37: 01-08 14:59:45.849 I/yan     ( 4613): testBtn-onTouch-ACTION_DOWN... Line 39: 01-08 14:59:45.849 I/yan     ( 4613): TestButton-onTouchEvent-ACTION_DOWN... Line 41: 01-08 14:59:45.939 I/yan     ( 4613): MainActivity-dispatchTouchEvent-ACTION_UP... Line 43: 01-08 14:59:45.941 I/yan     ( 4613): TestButton-dispatchTouchEvent-ACTION_UP... Line 45: 01-08 14:59:45.944 I/yan     ( 4613): testBtn-onTouch-ACTION_UP... Line 47: 01-08 14:59:45.946 I/yan     ( 4613): TestButton-onTouchEvent-ACTION_UP... Line 49: 01-08 14:59:45.974 I/yan     ( 4613): testBtn---onClick...
事件先由Activity的dispatchTouchEvent进行分发,然后TestButton的dispatchTouchEvent进行分发,接着执行onTouch监听,然后执行onTouchEvent。第二次UP动作的时候,在onTouchEvent里又执行了onClick监听。
如果我们想这个TestButton只能执行onTouch监听不能执行onClick监听,方法有很多。在onTouch监听里默认返回false改为true,如下:
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. testBtn.setOnTouchListener(new View.OnTouchListener() {  
  2.   
  3.             @Override  
  4.             public boolean onTouch(View v, MotionEvent event) {  
  5.                 // TODO Auto-generated method stub  
  6.                 switch(event.getAction()){  
  7.                 case MotionEvent.ACTION_DOWN:  
  8.                     Log.i(tag, "testBtn-onTouch-ACTION_DOWN...");  
  9.                     break;  
  10.                 case MotionEvent.ACTION_UP:  
  11.                     Log.i(tag, "testBtn-onTouch-ACTION_UP...");  
  12.                     break;  
  13.                 default:break;  
  14.   
  15.                 }  
  16.                 return true;  
  17.             }  
  18.         });  
事件流程为:
Line 75: 01-08 15:05:51.627 I/yan     ( 5262): MainActivity-dispatchTouchEvent-ACTION_DOWN... Line 77: 01-08 15:05:51.628 I/yan     ( 5262): TestButton-dispatchTouchEvent-ACTION_DOWN... Line 79: 01-08 15:05:51.629 I/yan     ( 5262): testBtn-onTouch-ACTION_DOWN... Line 81: 01-08 15:05:51.689 I/yan     ( 5262): MainActivity-dispatchTouchEvent-ACTION_UP... Line 83: 01-08 15:05:51.691 I/yan     ( 5262): TestButton-dispatchTouchEvent-ACTION_UP... Line 85: 01-08 15:05:51.695 I/yan     ( 5262): testBtn-onTouch-ACTION_UP...
可以看到压根就没执行onTouchEvent。因为onTouch返回了true,已提前将这个事件消费了,就不往下传了,dispatch流程提前终止。
二、ViewGroup的dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent
再来看ViewGroup,在复写ViewGroup时可以发现它的onTouchEvent在在View里的,表示这两个方法是一样的。但dispatchTouchEvent是在ViewGroup里的,表示和View的dispatchTouchEvent不一样,多了一个onInterceptTouchEvent函数,表示拦截的意思。链接 打个很形象的比喻,这玩意就像个秘书、谋士。为啥View没有呢,因为它级别不够,一个Button里面是不可能有子View的。但LinearLayout(继承ViewGroup)就有孩子(子布局),这个onInterceptTouchEvent就会判断事件要不要通知它的孩子呢。它的源码如下:
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1.     public boolean dispatchTouchEvent(MotionEvent ev) {  
  2.         if (mInputEventConsistencyVerifier != null) {  
  3.             mInputEventConsistencyVerifier.onTouchEvent(ev, 1);  
  4.         }  
  5.   
  6.         boolean handled = false;  
  7.         if (onFilterTouchEventForSecurity(ev)) {  
  8.             final int action = ev.getAction();  
  9.             final int actionMasked = action & MotionEvent.ACTION_MASK;  
  10.   
  11.             // Handle an initial down.  
  12.             if (actionMasked == MotionEvent.ACTION_DOWN) {  
  13.                 // Throw away all previous state when starting a new touch gesture.  
  14.                 // The framework may have dropped the up or cancel event for the previous gesture  
  15.                 // due to an app switch, ANR, or some other state change.  
  16.                 cancelAndClearTouchTargets(ev);  
  17.                 resetTouchState();  
  18.             }  
  19.   
  20.             // Check for interception.  
  21.             final boolean intercepted;  
  22.             if (actionMasked == MotionEvent.ACTION_DOWN  
  23.                     || mFirstTouchTarget != null) {  
  24.                 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;  
  25.                 if (!disallowIntercept) {  
  26.                     intercepted = onInterceptTouchEvent(ev);  
  27.                     ev.setAction(action); // restore action in case it was changed  
  28.                 } else {  
  29.                     intercepted = false;  
  30.                 }  
  31.             } else {  
  32.                 // There are no touch targets and this action is not an initial down  
  33.                 // so this view group continues to intercept touches.  
  34.                 intercepted = true;  
  35.             }  
  36.   
  37.             // Check for cancelation.  
  38.             final boolean canceled = resetCancelNextUpFlag(this)  
  39.                     || actionMasked == MotionEvent.ACTION_CANCEL;  
  40.   
  41.             // Update list of touch targets for pointer down, if needed.  
  42.             final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;  
  43.             TouchTarget newTouchTarget = null;  
  44.             boolean alreadyDispatchedToNewTouchTarget = false;  
  45.             if (!canceled && !intercepted) {  
  46.                 if (actionMasked == MotionEvent.ACTION_DOWN  
  47.                         || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)  
  48.                         || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {  
  49.                     final int actionIndex = ev.getActionIndex(); // always 0 for down  
  50.                     final int idBitsToAssign = split ? 1 
关注
打赏
1482932726
查看更多评论
立即登录/注册

微信扫码登录

0.1474s