Android Touch事件传递机制详解 下

尊重原创:http://blog.csdn.net/yuanzeyao/article/details/38025165

资源下载:http://download.csdn.net/detail/yuanzeyao2008/7660997

 

在前一篇文章中,我主要讲解了Android源码中的Touch事件的传递过程,现在我想使用一个demo以及一个实例来学习一下Andorid中的Touch事件处理过程。

在Android系统中,和Touch事件分发和处理紧密相关的三个函数如下:
(1) public boolean dispatchTouchEvent(MotionEvent ev)
(2) public boolean onInterceptTouchEvent(MotionEvent ev)
(3) public boolean onTouchEvent(MotionEvent event)


这三个方法我在前一篇文章中都对他们的源码进行了分析:方法1主要是对Touch事件进行分发,方法2主要是对Touch事件进行拦截,方法3是对Touch事件进行处理

这三个方法主要存在于ViewGroup,View,Activity中,具体情况如下图:

 

 

 

ViewGroup

View

Activity

dispatchTouchEvent

onInterceptTouchEvent

onTouchEvent

下面我们就使用一个demo来看看这些方法的执行流程:
自定义一个类:MyLayoutFirst.java

 

  1. public class MyLayoutFirst extends LinearLayout  
  2. {  
  3.   private static final String TAG = "MyLayoutFirst";  
  4.   public MyLayoutFirst(Context context, AttributeSet attrs)  
  5.   {  
  6.     super(context, attrs);  
  7.   }  
  8.     
  9.   @Override  
  10.   public boolean onInterceptTouchEvent(MotionEvent ev)  
  11.   {  
  12.     Log.w("yzy", "MyLayoutFirst->onInterceptTouchEvent->"+MyUtils.getActionName(ev));  
  13.     return super.onInterceptTouchEvent(ev);  
  14.   }  
  15.     
  16.   @Override  
  17.   public boolean onTouchEvent(MotionEvent event)  
  18.   {  
  19.     Log.e("yzy", "MyLayoutFirst->onTouchEvent->"+MyUtils.getActionName(event));  
  20.     return super.onTouchEvent(event);  
  21.   }  
  22.     
  23.   @Override  
  24.   public boolean dispatchTouchEvent(MotionEvent ev)  
  25.   {  
  26.     Log.i("yzy", "MyLayoutFirst->dispatchTouchEvent->"+MyUtils.getActionName(ev));  
  27.     return super.dispatchTouchEvent(ev);  
  28.   }  
  29.     
  30. }  


自定义一个类;MyLayoutSecond.java

 

  1. public class MyLayoutSecond extends LinearLayout  
  2. {  
  3.   private static final String TAG = "MyLayoutSecond";  
  4.   public MyLayoutSecond(Context context, AttributeSet attrs)  
  5.   {  
  6.     super(context, attrs);  
  7.   }  
  8.     
  9.   @Override  
  10.   public boolean onTouchEvent(MotionEvent event)  
  11.   {  
  12.     Log.e("yzy", "MyLayoutSecond->MyLayoutSecond->"+MyUtils.getActionName(event));  
  13.     return super.onTouchEvent(event);  
  14.   }  
  15.     
  16.   @Override  
  17.   public boolean onInterceptTouchEvent(MotionEvent ev)  
  18.   {  
  19.     Log.w("yzy", "MyLayoutSecond->onInterceptTouchEvent->"+MyUtils.getActionName(ev));  
  20.     return super.onInterceptTouchEvent(ev);  
  21.   }  
  22.     
  23.   @Override  
  24.   public boolean dispatchTouchEvent(MotionEvent ev)  
  25.   {  
  26.     Log.i("yzy", "MyLayoutSecond->dispatchTouchEvent->"+MyUtils.getActionName(ev));  
  27.     return super.dispatchTouchEvent(ev);  
  28.   }  
  29.   
  30.    
  31. }  


加入到main_layout.xml中

 

  1. <RelativeLayout 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.   
  7.     <com.event.demo.MyLayoutFirst   
  8.         android:id="@+id/layout_first"  
  9.         android:layout_width="match_parent"  
  10.         android:layout_height="match_parent"  
  11.         android:background="#FF0000"  
  12.         >  
  13.         <com.event.demo.MyLayoutSecond  
  14.             android:id="@+id/layout_second"  
  15.             android:layout_width="320dip"  
  16.             android:layout_height="120dip"  
  17.             android:layout_gravity="center"  
  18.             android:background="#0000FF"  
  19.         >  
  20.           
  21.     </com.event.demo.MyLayoutSecond>  
  22.     </com.event.demo.MyLayoutFirst>  
  23.   
  24. </RelativeLayout>  


MainActivity中加入onTouchEvent方法

 

  1. public class MainActivity extends Activity  
  2. {  
  3.   
  4.   @Override  
  5.   protected void onCreate(Bundle savedInstanceState)  
  6.   {  
  7.     super.onCreate(savedInstanceState);  
  8.     setContentView(R.layout.activity_main);  
  9.   }  
  10.   
  11.   @Override  
  12.   public boolean onCreateOptionsMenu(Menu menu)  
  13.   {  
  14.     // Inflate the menu; this adds items to the action bar if it is present.  
  15.     getMenuInflater().inflate(R.menu.main, menu);  
  16.     return true;  
  17.   }  
  18.     
  19.   @Override  
  20.   public boolean dispatchTouchEvent(MotionEvent ev)  
  21.   {  
  22.     Log.i("yzy", "MainActivity->dispatchTouchEvent->"+MyUtils.getActionName(ev));  
  23.     return super.dispatchTouchEvent(ev);  
  24.   }  
  25.     
  26.   @Override  
  27.   public boolean onTouchEvent(MotionEvent event)  
  28.   {  
  29.     Log.e("yzy", "MainActivity->onTouchEvent->"+MyUtils.getActionName(event));  
  30.     return super.onTouchEvent(event);  
  31.   }  
  32. }  


最后就一个工具类,用来将事件id转换为字符串。

 

  1. public class MyUtils  
  2. {  
  3.   private static final String TAG = "MyUtils";  
  4.   public static String getActionName(MotionEvent event)  
  5.   {  
  6.     String name="";  
  7.     switch(event.getAction())  
  8.     {  
  9.       case MotionEvent.ACTION_DOWN:  
  10.         name="ACTION_DOWN";  
  11.         break;  
  12.       case MotionEvent.ACTION_MOVE:  
  13.         name="ACTION_MOVE";  
  14.         break;  
  15.       case MotionEvent.ACTION_UP:  
  16.         name="ACTION_UP";  
  17.         break;  
  18.     }  
  19.     return name;  
  20.   }  
  21. }  


运行效果如图:

 

其中蓝色部分是MyLayoutSecond.java ,红色部分是MyLayoutFirst.java
现在我点击一下蓝色部分:运行结果如图:

从图中可以看出,事件最先被Activity捕获,然后分发给 MyLayoutFirst,MyLayoutFirst首先调用自身的onInterceptTouchEvent判断是否将该事件拦截,由于默认返回是false,所以没有拦截,从而事件分发给了MyLayoutSecond,MyLayoutSecond同样通过dispatchTouchEvent分发出去,分发出去之前同样检查是否被拦截,默认都是没有被拦截的,但是由于MyLayoutSecond是没有子视图的,所有最终事件有自己处理,调用自身的onTouchEvent方法,由于该方法默认返回的是false,所以认为此事件是没有被消费掉的,继续传递到了MyLayoutFirst中,同样也没有消费这个事件,最终传递到了Mainactivity,继续往后看发现后面的ACTION_MOVE和ACTION_UP并没有传入MyLayoutFirst和MyLayoutSecond,这是因为一旦某一个事件没有被处理,后面的事件是不会被分发的。所以ACTION_MOVE和ACTION_UP直接被MainActivity处理掉了。

下面再看第二种情况:

 

 

MainActivity

MyLayoutFirst

MyLayoutSecond

dispatchTouchEvent

super.dispatchTouchEvent

super.dispatchTouchEvent

super.dispatchTouchEvent

onInterceptTouchEvent

--

true

super.onInterceptTouchEvent(ev)

onTouchEvent

super.onTouchEvent

super.onTouchEvent

super.onTouchEvent

运行结果如下:

从图中可以看出,事件传递到了MyLayoutFirst后没有分发到MyLayoutSecond,直接调用自身的onTouchEvent,由于返回的是false,导致事件没有消费,最终传递给了MainActivity,
而且后续事件也没有传递到MyLayoutFirst和MyLayoutSecond,直接被MainActivity处理

 

第三种情况:

 

MainActivity

MyLayoutFirst

MyLayoutSecond

dispatchTouchEvent

super.dispatchTouchEvent

super.dispatchTouchEvent

super.dispatchTouchEvent

onInterceptTouchEvent

--

true

super.onInterceptTouchEvent(ev)

onTouchEvent

super.onTouchEvent

true

super.onTouchEvent

运行结果:

和情况二不同的是MyLayoutFirst的onTouchEvent返回了true,也就是说MyLayoutFirst消费了此事件,所以ACTION_DOWN也没有再传给MainActivity,并且ACTION_MOVE和ACTION_UP
均传给了MyLayoutFirst

 

第四中情况:

 

MainActivity

MyLayoutFirst

MyLayoutSecond

dispatchTouchEvent

super.dispatchTouchEvent

super.dispatchTouchEvent

super.dispatchTouchEvent

onInterceptTouchEvent

--

super.onInterceptTouchEvent(ev)

super.onInterceptTouchEvent(ev)

onTouchEvent

super.onTouchEvent

super.onTouchEven

true

运行结果:

发现所有的事件都是传递到了MyLayoutSecond后被消费了

 

其实还有很多其他组合方式,大家如果又兴趣可以自己尝试改变每个函数的返回值,查看打印结果,这里我就不一一列举了。。。。。


最后我会提供一个小demo演示如何解决滑动冲突,背景如下:
一个ViewPager里面包含两个Framgent,有一个Fragment里面有一个HorizontalListView ,如何滑动冲突?
我就贴出关键代码吧

 

 

  1. horizontal=(HorizontalListView)view.findViewById(R.id.hscroll);  
  2.     horizontal.setOnTouchListener(new OnTouchListener()  
  3.     {  
  4.         
  5.       @Override  
  6.       public boolean onTouch(View arg0, MotionEvent event)  
  7.       {  
  8.         if(event.getAction()==MotionEvent.ACTION_DOWN)  
  9.         {  
  10.           parent.requestDisallowInterceptTouchEvent(true);  
  11.         }else if(event.getAction()==MotionEvent.ACTION_UP)  
  12.         {  
  13.           parent.requestDisallowInterceptTouchEvent(false);  
  14.         }  
  15.         return false;  
  16.       }  
  17.     });  


加入这段代码就可以避免滑动冲突了,至于为什么大家可以参考我的前以前文章《Android Touch 事件传递机制详解 上》 这两个demo的例子我均会上传下载的

posted on 2016-08-05 14:51  Sun‘刺眼的博客  阅读(608)  评论(0编辑  收藏  举报

导航