一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->ACTION_MOVE...->ACTION_MOVE->ACTION_UP

当屏幕中包含一个ViewGroup(或者Layout,实际上layout也是viewGroup),而这个ViewGroup又包含一个子view(也可能是viewGroup,此处我们仅讨论view),这个时候android系统如何处理Touch事件呢?到底是 ViewGroup来处理Touch事件,还是子view来处理Touch事件呢?他们的执行过程是怎么样的呢?

我将以实例进行说明:

这里我自定义一个MyLinearLayout继承至LinearLayout,定义一个MyTextView继承至TextView.

MyLinearLayout.java

 

 
      
  1. package com.example.testforevent; 
  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.LinearLayout; 
  8.  
  9. public class MyLinearLayout extends LinearLayout { 
  10.  
  11.     private static final String TAG = "MyLinearLayout"
  12. //  //说明:提到的子viewactivity_main.xml中的MyTextView,当前viewGroup是指activity_main.xml的MyLinearLayout(因为linearLayout实际上也是viewGroup,这里是MyLinearLayout) 
  13.     public MyLinearLayout(Context context) { 
  14.         super(context); 
  15.     } 
  16.  
  17.     public MyLinearLayout(Context context, AttributeSet attrs) { 
  18.         super(context, attrs); 
  19.  
  20.     } 
  21. //dispatchTouchEvent,onInterceptTouchEvent,TouchEvent三者的触发顺序是dispatch最先,intercept 
  22. //次之,Touch最后 
  23.     //返回值为true时当event传到这里时不再下发:情况一仅点击当前所在viewGroup:事件触发顺序:dispatchTouchEvent .actionDown-> 
  24.     //onInterceptTouchEvent.actionDown->onTouchEvent.actionDown->dispatchTouchEvent.actionUp-> 
  25.     //onTouchEvent.actionUp 
  26.     //情况二:点击其子view(这里指当中的MyTextView)。当手指按下屏幕是,事件的触发属性是: 
  27.    //layout的dispatchTouchEvent.action_down->layout的onInterceptTouchEvent.action_down->子View的dispatchTouchEvent.action_down 
  28.     //->子view的onTouchEvent.action_down->layout的onTouchEvent.action_down->layout的dispatchTouchEvent.actionUp->layout的 
  29.     //onTouchEvent.actionUp 
  30.     //返回值为false时,会依次触发action_down,事件会继续分发下去 
  31.     @Override 
  32.     public boolean onTouchEvent(MotionEvent event) { 
  33.  
  34.         int action = event.getAction(); 
  35.         switch (action) { 
  36.         case MotionEvent.ACTION_CANCEL: 
  37.             Log.d(TAG, "onTouchEvent:  action_cancel"); 
  38.             break; 
  39.         case MotionEvent.ACTION_DOWN: 
  40.             Log.d(TAG, "onTouchEvent: action_down"); 
  41.             break; 
  42.         case MotionEvent.ACTION_MASK: 
  43.             Log.d(TAG, "onTouchEvent: action_mask"); 
  44.             break; 
  45.         case MotionEvent.ACTION_MOVE: 
  46.             Log.d(TAG, "onTouchEvent: action_move"); 
  47.             break; 
  48.         case MotionEvent.ACTION_OUTSIDE: 
  49.             Log.d(TAG, "onTouchEvent: action_outside"); 
  50.             break; 
  51.         case MotionEvent.ACTION_UP: 
  52.             Log.d(TAG, "onTouchEvent: action_up"); 
  53.             break; 
  54.         default: 
  55.             break; 
  56.         } 
  57.         return super.onTouchEvent(event); 
  58.     } 
  59. //触发优先级最高,当返回值为true时,事件不再下发(不再传递给onIntercepteEvent,TouchEvent,子view也不会接收到任何事件),即拦截了event 
  60. //若未拦截,事件继续下发,依次的传递顺序是:dispatch->onIntercepteEvent->子view的dispatchEvent->子view的touchEvent->TouchEvent 
  61.     @Override 
  62.     public boolean dispatchTouchEvent(MotionEvent ev) { 
  63.         int action = ev.getAction(); 
  64.         switch (action) { 
  65.         case MotionEvent.ACTION_CANCEL: 
  66.             Log.d(TAG, "dispatchTouchEvent:  action_cancel"); 
  67.             break; 
  68.         //一个简单的单击屏幕事件,一定会最先触发down,手指一按下就触发 
  69.         case MotionEvent.ACTION_DOWN: 
  70.             Log.d(TAG, "dispatchTouchEvent: action_down"); 
  71.             break; 
  72.         case MotionEvent.ACTION_MASK: 
  73.             Log.d(TAG, "dispatchTouchEvent: action_mask"); 
  74.             break; 
  75.             //若是拦截事件(返回true),在手指存在移动时,将会触发,手指不动,将不会不触发 
  76.         case MotionEvent.ACTION_MOVE: 
  77.             Log.d(TAG, "dispatchTouchEvent: action_move"); 
  78.             break; 
  79.         case MotionEvent.ACTION_OUTSIDE: 
  80.             Log.d(TAG, "dispatchTouchEvent: action_outside"); 
  81.             break; 
  82.             //若是拦截事件(返回为true),手指离开屏幕时触发 
  83.         case MotionEvent.ACTION_UP: 
  84.             Log.d(TAG, "dispatchTouchEventt: action_up"); 
  85.             break; 
  86.         default: 
  87.             break; 
  88.         } 
  89.     //  return true; 
  90.     return super.dispatchTouchEvent(ev); 
  91.     } 
  92. //当接收到从dispatch传递过来的event,将触发action_down事件。返回true时将窃取子view的 
  93.     //motionEvent事件,但不会影响当前所在view组的TouchEvent的触发,即子view不会触发子view的 
  94.     //dispatchEvent,TouchEvent;返回为false时,事件将继续下发,子view将触发先后触发子view的dispatchEvent 
  95.     //和TouchEvent,然后再触发当前所在viewGroup的onTouchEvent 
  96.     @Override 
  97.     public boolean onInterceptTouchEvent(MotionEvent ev) { 
  98.  
  99.         int action = ev.getAction(); 
  100.         switch (action) { 
  101.         case MotionEvent.ACTION_CANCEL: 
  102.             Log.d(TAG, "onInterceptTouchEvent:  action_cancel"); 
  103.             break; 
  104.         case MotionEvent.ACTION_DOWN: 
  105.             Log.d(TAG, "onInterceptTouchEvent: action_down"); 
  106.             break; 
  107.         case MotionEvent.ACTION_MASK: 
  108.             Log.d(TAG, "onInterceptTouchEvent: action_mask"); 
  109.             break; 
  110.         case MotionEvent.ACTION_MOVE: 
  111.             Log.d(TAG, "onInterceptTouchEvent: action_move"); 
  112.             break; 
  113.         case MotionEvent.ACTION_OUTSIDE: 
  114.             Log.d(TAG, "onInterceptTouchEvent: action_outside"); 
  115.             break; 
  116.         case MotionEvent.ACTION_UP: 
  117.             Log.d(TAG, "onInterceptTouchEvent: action_up"); 
  118.             break; 
  119.         default: 
  120.             break; 
  121.  
  122.         } 
  123.      
  124.          
  125.         return super.onInterceptTouchEvent(ev); 
  126.     } 
  127.  

MyTextView.java

 

 

 
      
  1. package com.example.testforevent; 
  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.TextView; 
  8.  
  9. public class MyTextView extends TextView { 
  10.  
  11.     private final static String TAG = "MyTextView"
  12.  
  13.     public MyTextView(Context context, AttributeSet attrs) { 
  14.         super(context, attrs); 
  15.     } 
  16.  
  17.     public MyTextView(Context context) { 
  18.         super(context); 
  19.     } 
  20. //返回值true时,layout的onTouchEvent不会触发,被子view拦截了。此时执行的是Layout的 dispatchTouchEvent: action_down 
  21. //->Layout的 onInterceptTouchEvent: action_down->子view的dispatchTouchEvent: action_down->子View的 onTouchEvent: action_down 
  22. //->Layout的 dispatchTouchEventt: action_up->Layout的onInterceptTouchEvent: action_up-> 
  23.     //子View的 dispatchTouchEventt: action_up->子View的OnTouchEventt: action_up 
  24.  
  25.     @Override 
  26.     public boolean onTouchEvent(MotionEvent event) { 
  27.         int action = event.getAction(); 
  28.         switch (action) { 
  29.         case MotionEvent.ACTION_CANCEL: 
  30.             Log.d(TAG, "onTouchEvent:  action_cancel"); 
  31.             break; 
  32.         case MotionEvent.ACTION_DOWN: 
  33.             Log.d(TAG, "onTouchEvent: action_down"); 
  34.             break; 
  35.         case MotionEvent.ACTION_MASK: 
  36.             Log.d(TAG, "onTouchEvent: action_mask"); 
  37.             break; 
  38.         case MotionEvent.ACTION_MOVE: 
  39.             Log.d(TAG, "onTouchEvent: action_move"); 
  40.             break; 
  41.         case MotionEvent.ACTION_OUTSIDE: 
  42.             Log.d(TAG, "onTouchEvent: action_outside"); 
  43.             break; 
  44.         case MotionEvent.ACTION_UP: 
  45.             Log.d(TAG, "onTouchEvent: action_up"); 
  46.             break; 
  47.         default: 
  48.             break; 
  49.         } 
  50.         return true; 
  51.         //return super.onTouchEvent(event); 
  52.     } 
  53. //当返回值为true时,子view的TouchEvent和layout的TouchEvent都不会触发。依次触发的过程是:layout的 
  54.     //dispatchTouchEvent.actionDown->layout的onInterceptTouchEvent.actionDown->子view的dispatchTouch 
  55.     //Event.actionDown->layout的dispatchTouchEvent.actionUp->layout的onInterceptTouchEvent.actionUp-> 
  56.     //子view的dispatchTouchEvent.actionUp  
  57.     // 
  58.     @Override 
  59.     public boolean dispatchTouchEvent(MotionEvent event) { 
  60.  
  61.         int action = event.getAction(); 
  62.         switch (action) { 
  63.         case MotionEvent.ACTION_CANCEL: 
  64.             Log.d(TAG, "dispatchTouchEvent:  action_cancel"); 
  65.             break; 
  66.         case MotionEvent.ACTION_DOWN: 
  67.             Log.d(TAG, "dispatchTouchEvent: action_down"); 
  68.             break; 
  69.         case MotionEvent.ACTION_MASK: 
  70.             Log.d(TAG, "dispatchTouchEvent: action_mask"); 
  71.             break; 
  72.         case MotionEvent.ACTION_MOVE: 
  73.             Log.d(TAG, "dispatchTouchEvent: action_move"); 
  74.             break; 
  75.         case MotionEvent.ACTION_OUTSIDE: 
  76.             Log.d(TAG, "dispatchTouchEvent: action_outside"); 
  77.             break; 
  78.         case MotionEvent.ACTION_UP: 
  79.             Log.d(TAG, "dispatchTouchEventt: action_up"); 
  80.             break; 
  81.         default: 
  82.             break; 
  83.         } 
  84.  
  85. return super.dispatchTouchEvent(event); 
  86.     } 
  87.  

MainActivity.java

 

 

 
      
  1. package com.example.testforevent; 
  2.  
  3. import android.os.Bundle; 
  4. import android.app.Activity; 
  5. import android.view.Menu; 
  6. import android.view.MenuItem; 
  7. import android.support.v4.app.NavUtils; 
  8.  
  9. public class MainActivity extends Activity { 
  10.  
  11.     @Override 
  12.     public void onCreate(Bundle savedInstanceState) { 
  13.         super.onCreate(savedInstanceState); 
  14.         setContentView(R.layout.activity_main); 
  15.     } 
  16.  
  17.     @Override 
  18.     public boolean onCreateOptionsMenu(Menu menu) { 
  19.         getMenuInflater().inflate(R.menu.activity_main, menu); 
  20.         return true; 
  21.     } 
  22. //一个activity中view触发的顺序是外层先获取event事件,然后向内传递给里面包含的子view,若是在外层layout 
  23.    //拦截了event事件,那么不会再传递给子view,子view的TouchEvent将不会触发 
  24.      

 

activity_main.xml

 

 

 
      
  1. <com.example.testforevent.MyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  2.     xmlns:tools="http://schemas.android.com/tools" 
  3.     android:layout_width="match_parent" 
  4.     android:layout_height="match_parent" > 
  5.  
  6.     <com.example.testforevent.MyTextView 
  7.         android:layout_width="wrap_content" 
  8.         android:layout_height="wrap_content" 
  9.         android:layout_centerHorizontal="true" 
  10.         android:layout_centerVertical="true" 
  11.         android:padding="@dimen/padding_medium" 
  12.         android:text="@string/hello_world" 
  13.         tools:context=".MainActivity" /> 
  14.  
  15. </com.example.testforevent.MyLinearLayout> 

先看看运行的效果截图吧:

1.a.点击layout

 

b.单击textview

 

 

2.仅当MyLinearLayout的dispatchTouchEvent返回值为true时:

1>.单击layout(不点击到textview上)

2>.单击textview(textView的所有touch事件都不会触发)

 

3.仅当MyLinearLayout的dispatchTouchEvent返回值为true,它将偷取子view的touch事件,也就是说子view不会接受到任何Touch事件

 1>.单击layout

 

2.>单击textview(事件不分发到子view),看到的结果与点击layout相同,说明子view没有接收到event

 

4.仅当MyLinearLayout的onTouchEvent返回值为true时

1>点击layout

 

2>.点击textview,textview的接收到了事件

 

5.仅当TextView的DispatchTouchEvent返回值为true时,点击textview,textview的onTouchEvent不会被触发

 

6.仅当TextView的onTouchEvent返回值为true时,layout的onTouchEvent不会被触发

 

在ViewGroup(即上面Layout)情况特别分析:

针对由于触摸(Touch)而触发的事件。

Android的Touch的第一个状态肯定是ACTION_DOWN, 表示按下了屏幕。之后,touch将会有后续事件,可能是:

ACTION_MOVE //表示为移动手势

ACTION_UP //表示为离开屏幕

ACTION_CANCEL //表示取消手势,不会由用户产生,而是由程序产生的

一个Action_DOWN, n个ACTION_MOVE, 1个ACTION_UP,就构成了Android中众多的事件。

在Android中,有一类控件是中还可以包含其他的子控件,这类控件是继承于ViewGroup类,例如:ListView, Gallery, GridView。

还有一类控件是不能再包含子控件,例如:TextView。


对于ViewGroup类的控件,有一个很重要的方法,就是onInterceptTouchEvent(),用于处理事件并改变事件的传递方向,它的返回值是一个布尔值,决定了Touch事件是否要向它包含的子View继续传递,这个方法是从父View向子View传递。(返回值为true时,不会向子view传递事件)

而方法onTouchEvent(),用于接收事件并处理,它的返回值也是一个布尔值,决定了事件及后续事件是否继续向上传递,这个方法是从子View向父View传递。(为true时不会向上传递)

Touch事件在 onInterceptTouchEvent()和onTouchEvent以及各个childView间的传递机制完全取决于onInterceptTouchEvent()和onTouchEvent()的返回值。返回值为true表示事件被正确接收和处理了,返回值为false表示事件没有被处理,将继续传递下去(只是传递方向不一样,onInterceptTouchEvent()向子View传,而onTouchEvent()向父View传)。

具体情况如下:

ACTION_DOWN事件会传到某个ViewGroup类的onInterceptTouchEvent,如果返回false,则DOWN事件继续向子ViewGroup类的onInterceptTouchEvent传递,如果子View不是ViewGroup类的控件,则传递给它的onTouchEvent。

如果onInterceptTouchEvent返回了true,则DOWN事件传递给它的onTouchEvent,不再继续传递,并且之后的后续事件也都传递给它的onTouchEvent。

如果某View的onTouchEvent返回了false,则DOWN事件继续向其父ViewGroup类的onTouchEvent传递;如果返回了true,则后续事件会直接传递给其onTouchEvent继续处理。(后续事件只会传递给对于必要事件ACTION_DOWN返回了true的onTouchEvent)

onInterceptTouchEvent可以接受到所有的Touch事件,而onTouchEvent则不一定。