当前位置: 代码迷 >> Android >> 初学者进阶之Android Touch事件传递(二)
  详细解决方案

初学者进阶之Android Touch事件传递(二)

热度:87   发布时间:2016-04-28 02:18:50.0
菜鸟进阶之Android Touch事件传递(二)

这是touch事件传递系列博客的第二篇,如果想了解touch和click的那些事,请浏览投产事件传递系列的第一篇。http://blog.csdn.net/bingospunky/article/details/43603397

理理思路,我发现touch传递这部分的内容很多,所以每篇博客介绍一个方面比较好。这篇博客主要介绍touch事件传递的现象,一个简单的demo,让大家可以看到touch一步一步传递的过程。下篇博客中在研究源码是怎么实现的。再后面的博客会试图改变这篇文章看到的touch的传递过程,比如viewpager里嵌套listview。

demo

代码A:

[java] view plaincopyprint?
  1. public class LayoutView1 extends  {  
  2.   
  3.     private final String TAG = "qingtian";  
  4.   
  5.     public LayoutView1(Context context, AttributeSet attrs) {  
  6.         super(context, attrs);  
  7.     }  
  8.   
  9.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  10.         int action = ev.getAction();  
  11.         switch (action) {  
  12.         case MotionEvent.ACTION_DOWN:  
  13.             Log.d(TAG, "LayoutView1  Intercept  DOWN");  
  14.             break;  
  15.         case MotionEvent.ACTION_MOVE:  
  16.             Log.d(TAG, "LayoutView1  Intercept  MOVE");  
  17.             break;  
  18.         case MotionEvent.ACTION_UP:  
  19.             Log.d(TAG, "LayoutView1  Intercept  UP");  
  20.             break;  
  21.         case MotionEvent.ACTION_CANCEL:  
  22.             Log.d(TAG, "LayoutView1  Intercept  CANCEL");  
  23.             break;  
  24.         }  
  25.         return false;  
  26.     }  
  27.   
  28.     @SuppressLint("ClickableViewAccessibility")  
  29.     public boolean onTouchEvent(MotionEvent ev) {  
  30.         int action = ev.getAction();  
  31.         switch (action) {  
  32.         case MotionEvent.ACTION_DOWN:  
  33.             Log.d(TAG, "LayoutView1  Touch  DOWN");  
  34.             break;  
  35.         case MotionEvent.ACTION_MOVE:  
  36.             Log.d(TAG, "LayoutView1  Touch  MOVE");  
  37.             break;  
  38.         case MotionEvent.ACTION_UP:  
  39.             Log.d(TAG, "LayoutView1  Touch  UP");  
  40.             break;  
  41.         case MotionEvent.ACTION_CANCEL:  
  42.             Log.d(TAG, "LayoutView1  Touch  CANCEL");  
  43.             break;  
  44.         }  
  45.     }  
  46.       
  47.     @Override  
  48.     public boolean dispatchTouchEvent(MotionEvent ev) {  
  49.         Log.d(TAG, "LayoutView1  dispatch   " );  
  50.         boolean r = super.dispatchTouchEvent(ev);  
  51.         Log.d(TAG, "LayoutView1  dispatch 结果   "+r);  
  52.         return r;  
  53.     }  
  54.   
  55. }  

代码B:

[java] view plaincopyprint?
  1. public class LayoutView2 extends LinearLayout {  
  2.   
  3.     private final String TAG = "qingtian";  
  4.   
  5.     public LayoutView2(Context context, AttributeSet attrs) {  
  6.         super(context, attrs);  
  7.     }  
  8.   
  9.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  10.         int action = ev.getAction();  
  11.         switch (action) {  
  12.         case MotionEvent.ACTION_DOWN:  
  13.             Log.d(TAG, "LayoutView2  Intercept  DOWN");  
  14.             break;  
  15.         case MotionEvent.ACTION_MOVE:  
  16.             Log.d(TAG, "LayoutView2  Intercept  MOVE");  
  17.             break;  
  18.         case MotionEvent.ACTION_UP:  
  19.             Log.d(TAG, "LayoutView2  Intercept  UP");  
  20.             break;  
  21.         case MotionEvent.ACTION_CANCEL:  
  22.             Log.d(TAG, "LayoutView2  Intercept  CANCEL");  
  23.             break;  
  24.         }  
  25.         return false;  
  26.     }  
  27.   
  28.     public boolean onTouchEvent(MotionEvent ev) {  
  29.         int action = ev.getAction();  
  30.         switch (action) {  
  31.         case MotionEvent.ACTION_DOWN:  
  32.             Log.d(TAG, "LayoutView2  Touch  DOWN");  
  33.             break;  
  34.         case MotionEvent.ACTION_MOVE:  
  35.             Log.d(TAG, "LayoutView2  Touch  MOVE");  
  36.             break;  
  37.         case MotionEvent.ACTION_UP:  
  38.             Log.d(TAG, "LayoutView2  Touch  UP");  
  39.             break;  
  40.         case MotionEvent.ACTION_CANCEL:  
  41.             Log.d(TAG, "LayoutView2  Touch  CANCEL");  
  42.             break;  
  43.         }  
  44.         return false;  
  45.     }  
  46.   
  47.     @Override  
  48.     public boolean dispatchTouchEvent(MotionEvent ev) {  
  49.         Log.d(TAG, "LayoutView2  dispatch  ");  
  50.         boolean r = super.dispatchTouchEvent(ev);  
  51.         Log.d(TAG, "LayoutView2  dispatch  结果  "+r);  
  52.         return r;  
  53.     }  
  54. }  
代码C:

[java] view plaincopyprint?
  1. public class MyTextView extends TextView {  
  2.   
  3.     private final String TAG = "qingtian";  
  4.   
  5.     public MyTextView(Context context, AttributeSet attrs) {  
  6.         super(context, attrs);  
  7.     }  
  8.   
  9.     public boolean onTouchEvent(MotionEvent ev) {  
  10.         int action = ev.getAction();  
  11.         switch (action) {  
  12.         case MotionEvent.ACTION_DOWN:  
  13.             Log.d(TAG, "MyTextView  Touch   DOWN");  
  14.             break;  
  15.         case MotionEvent.ACTION_MOVE:  
  16.             Log.d(TAG, "MyTextView  Touch   MOVE");  
  17.             break;  
  18.         case MotionEvent.ACTION_UP:  
  19.             Log.d(TAG, "MyTextView  Touch   UP");  
  20.             break;  
  21.         case MotionEvent.ACTION_CANCEL:  
  22.             Log.d(TAG, "MyTextView  Touch   CANCEL");  
  23.             break;  
  24.         }  
  25.         return false;  
  26.     }  
  27.       
  28.     @Override  
  29.     public boolean dispatchTouchEvent(MotionEvent ev) {  
  30.         Log.d(TAG, "MyTextView  dispatch  " );  
  31.         boolean r = super.dispatchTouchEvent(ev);  
  32.         Log.d(TAG, "MyTextView  dispatch   结果  "+r);  
  33.         return r;  
  34.     }  
  35.   
  36. //  public void onClick(View v) {  
  37. //      Log.d(TAG, "onClick");  
  38. //  }  
  39. //  
  40. //  public boolean onLongClick(View v) {  
  41. //      Log.d(TAG, "onLongClick");  
  42. //      return false;  
  43. //  }  
  44.   
  45. }  
布局文件D:

[html] view plaincopyprint?
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <com.example.testontouch.LayoutView1 xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <com.example.testontouch.LayoutView2  
  8.         android:layout_width="fill_parent"  
  9.         android:layout_height="fill_parent"  
  10.         android:gravity="center"  
  11.         android:orientation="vertical" >  
  12.   
  13.         <com.example.testontouch.MyTextView  
  14.             android:layout_width="200dp"  
  15.             android:layout_height="150dp"  
  16.             android:background="#FFFFFF"  
  17.             android:text="Hello World"  
  18.             android:textColor="#0000FF"  
  19.             android:textSize="40sp"  
  20.             android:textStyle="bold" />  
  21.     </com.example.testontouch.LayoutView2>  
  22.   
  23. </com.example.testontouch.LayoutView1>  

你可以改变这三个方法的返回值,观察touch都会传递到哪里。

PS:网上有很多类似的demo,基本没有改变dispatchTouchEvent的返回值的(虽然实际开发中基本不会改变这个返回值),强烈建议修改这个方法的返回值,尤其对于:如果你了解touch传递的U形,改变这个值会改变U形,不是改变形,而是使U形变长或变短。


总结


Android官方文档上

onInterceptTouchEvent()与onTouchEvent()的机制:

1.down事件首先会传递到onInterceptTouchEvent()方法
2.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标view的onTouchEvent()处理
3.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理,注意,目标view将接收不到任何事件。
4.如果最终需要处理事件的view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理

5.如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。

我的理解

1.touch事件传递与dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent这三个方法相关,可以看到这三个方法都返回boolean类型的值,如果我们能了解这三个方法的返回值的含义,就在自定义组件的时候通过控制返回的值影响touch事件的传递,这是理解的第一个层次。如果能从源码里真正理解来这三个返回值是怎么被使用的,那么我们在自定义view的时候可以重写使用返回值的部分,那么我们就可以改变touch传递的U形,更深的理解touch,比如我一直想弄明白的一个问题:viewpager里嵌套listview,怎么实现区分viewpage响应滑动,还是listview响应滑动,这是我理解的更高的一个层次。当然第二层我还没有达到,写完这个系列的博客后应该可以达到。

2.这篇博客里只有viewgroup和view这两种组件,viewgroup又会存在嵌套的关系,所以你在理解事件传递的时候一定要用点递归的概念。

3.当一个touch事件到达一个viewgroup时,先到达dispatchTouchEvent这个方法,然后到onInterceptTouchEvent方法:

a、如果onInterceptTouchEvent返回true,那么执行该viewgroup的onTouchEvent。由该viewgroup处理touch,不再传递到child组件。viewgroup的onInterceptTouchEvent的返回值根据该viewgroup的onTouchEvent的返回值确定。

b、如果onInterceptTouchEvent返回false,那么寻找对应位置上的child:

b1、如果child存在,执行该child的dispatchTouchEvent:

b1一、如果child的dispatchTouchEvent返回true,表示child消化了该事件,viewgroup不做处理,viewgroup的onInterceptTouchEvent的返回值为true。

b1二、如果child的dispatchTouchEvent返回false,那么再执行viewgroup的onTouchEvent,viewgroup的onInterceptTouchEvent的返回值根据该viewgroup的onTouchEvent的返回值确定。

b2、如果child不存在,那么执行该viewgroup的onTouchEvent,viewgroup的onInterceptTouchEvent的返回值根据该viewgroup的onTouchEvent的返回值确定。

4.当一个touch事件到达一个view时,先到达dispatchTouchEvent这个方法,然后执行view的onTouchEvent方法。

5.这里忽略掉来view的mOnTouchListener,因为这个地方不会影响到dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent,我们可以理解为mOnTouchListener和onTouchEvent是一个方法就行,只是再执行的时候他们二选其一。

6.我们把down->move........move->up成为一个完整的touch事件。对于一个完整的touch事件,touch在被传递的时候看起来是有记录它的传递路径的功能。对于viewgroup和view,我们不去重写它的方法,touch记录传递路径的功能有以下规律:

6a、如果down事件没有被任何一个view消化而被抛到最外面,那么不会有move和up事件。

6b、down事件被哪个view(包含viewgroup,因为viewgroup继承自view)消化,以后的move和up事件都会被这个view消化。

6c、如果down事件被消化了,后续的move和up会被传递到同一个方法里,但是传递的路径可能会不一样,可能会少经历一些方法。


demo下载路径:http://download.csdn.net/detail/u011647962/8440275


写在最后

博客都写来20多篇了,一个回复都木有,很受伤啊,各位道友看到了给个回复啊,作为对一个菜鸟的鼓励。顺便说声:大家,过年好。

1楼best777前天 08:42
支持哈。
Re: u011647962前天 09:52
回复best777n谢谢喽。
  相关解决方案