Android 中Touch(触屏)事件传递机制

Posted wuhongqi0012

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 中Touch(触屏)事件传递机制相关的知识,希望对你有一定的参考价值。

转载来自:http://blog.csdn.net/wangjinyu501/article/details/22584465

版本:2.0 日期:2014.3.21  2014.3.29   版权:© 2014 kince 转载注明出处   
一、基本概念
 在实际开发中,经常会遇到与触屏事件有关的问题,最典型的一个就是滑动冲突。比如在使用SliddingMenu菜单的时候,可能会与ViewPager或者其他的一些带有滑动事件的View相冲突,再比如ScrollView嵌套ListView相冲突等等。还有就是在自定义控件的时候,需要处理一些事件时候,也必须把逻辑处理好,父 view 和子view 都需要接收事件,然后处理。如果不明白事件传递机制,很难开发出需要的效果。因此就需要对android消息传递机制有一个基本的理解与认识,这样才有可能解决开发过程中的需求与问题。   对于触摸(Touch)触发的事件,在Android中,事件主要包括点按(onClick)、长按(onLongClick)、拖拽(onDrag)、滑动(onScroll)等,点按又包括单击和双击,另外还包括单指操作和多指操作。其中Touch的第一个状态是 ACTION_DOWN, 表示按下了屏幕。之后,touch将会有后续事件,比如移动、抬起等,一个Action_DOWN, n个ACTION_MOVE, 1个ACTION_UP,就构成了Android中众多的事件。
  • 按下(ACTION_DOWN)         //表示用户按下了屏幕
  • 移动(ACTION_MOVE)          //表示用户在屏幕移动
  • 抬起(ACTION_UP)               //表示用户离开屏幕
  • 取消手势(ACTION_CANCEL) //表示,不会由用户产生,而是由程序产生的
  • 划出屏幕(ACTION_OUTSIDE)//表示滑出屏幕了
  当然还有一些其他的事件,如图所示:
     所有的操作事件首先必须执行的是按下操作(ACTIONDOWN),之后所有的操作都是以此作为前提,当按下操作完成后,接下来可能是一段移动(ACTIONMOVE)然后抬起(ACTION_UP),或者是按下操作执行完成后没有移动就直接抬起。因为所有的事件操作都发生在触摸屏上,而在屏幕上与用户交互的就是各种各样的视图组件(View),在Android中,所有的视图都继承于View,另外通过各种布局组件(ViewGroup)来对View进行布局,ViewGroup也继承于View。所有的UI控件例如Button、TextView都是继承于View,而所有的布局控件例如RelativeLayout、容器控件例如ListView都是继承于ViewGroup。所以,事件操作主要就是发生在View和ViewGroup之间,那么View和ViewGroup中主要有哪些方法来对这些事件进行响应呢?有如下3个方法:   1、public boolean dispatchTouchEvent(MotionEvent event)
  2、 public boolean onTouchEvent(MotionEvent event)
  3、 public boolean onInterceptTouchEvent(MotionEvent event)
  在View和ViewGroup中都存在dispatchTouchEvent和onTouchEvent方法,特别的,在ViewGroup中还有一个onInterceptTouchEvent方法。这些方法的返回值全部都是boolean型,都返回true或者是false,这是因为事件传递的过程就是一个接一个,某一个点后根据方法boolean的返回值判断是否要继续往下传递。在Android中,所有的事件都是从开始经过传递到完成事件的消费,这些方法的返回值就决定了某一事件是否是继续往下传,还是被拦截了,或是被消费了。   接下来就是这些方法的参数,都接受了一个MotionEvent类型的参数,MotionEvent继承于InputEvent,用于标记各种动作事件。之前提到的ACTIONDOWN、ACTIONMOVE、ACTION_UP、ACTION_CANCEL都是MotinEvent中定义的常量。我们通过MotionEvent传进来的事件类型来判断接收的是哪一种类型的事件。到现在,这三个方法的返回值和参数你应该都明白了,接下来就解释一下这三个方法分别在什么时候处理事件。
  • dispatchTouchEvent方法用于事件的分发,Android中所有的事件都必须经过这个方法的分发,然后决定是自身消费当前事件还是继续往下分发给子控件处理。返回true表示不继续分发,事件没有被消费。返回false则继续往下分发,如果是ViewGroup则分发给onInterceptTouchEvent进行判断是否拦截该事件。
  •  onInterceptTouchEvent是ViewGroup中才有的方法,View中没有,它的作用是负责事件的拦截,返回true的时候表示拦截当前事件,不继续往下分发,交给自身的onTouchEvent进行处理。返回false则不拦截,继续往下传。这是ViewGroup特有的方法,因为ViewGroup中可能还有子View,而在Android中View中是不能再包含子View的。
  • onTouchEvent方法用于事件的处理,返回true表示消费处理当前事件,返回false则不处理,交给子控件进行继续分发。Android中事件的构成以及事件处理方法的基本概念介绍到这,接下来就通过一系列的测试来验证以及梳理总结。
   二、实验测试    测试的时候,分为以下几种情况,不同的情况下事件的传递机制是不一样的,但是事件传递原理都一样,所以不要混淆。 1、无子控件情况   新建一个工程,在MainActivity里面重写dispatchTouchEvent(MotionEvent ev)以及onTouchEvent(MotionEvent event)方法,代码如下: [html]  view plain copy
  1. package com.example.toucheventdemo;  
  2.   
  3. import android.os.Bundle;  
  4. import android.app.Activity;  
  5. import android.util.Log;  
  6. import android.view.MotionEvent;  
  7. import android.view.View;  
  8. import android.view.View.OnClickListener;  
  9. import android.view.View.OnTouchListener;  
  10.   
  11. public class MainActivity extends Activity   
  12.   
  13.      private CustomButton mButton_top;  
  14.   
  15.      @Override  
  16.      protected void onCreate(Bundle savedInstanceState)   
  17.           super.onCreate(savedInstanceState);  
  18.           setContentView(R.layout.activity_main);  
  19.   
  20.           mButton_top = (CustomButton) this.findViewById(R.id.cusbutton_top);  
  21.           mButton_top.setOnClickListener(this);  
  22.           mButton_top.setOnTouchListener(this);  
  23.        
  24.   
  25.      @Override  
  26.      public boolean dispatchTouchEvent(MotionEvent ev)   
  27.   
  28.           switch (ev.getAction())   
  29.           case MotionEvent.ACTION_DOWN:  
  30.                System.out.println("MainActivity--dispatchTouchEvent:"  
  31.                          + "---MotionEvent.ACTION_DOWN---");  
  32.                break;  
  33.           case MotionEvent.ACTION_MOVE:  
  34.                System.out.println("MainActivity--dispatchTouchEvent:"  
  35.                          + "---MotionEvent.ACTION_MOVE---");  
  36.                break;  
  37.           case MotionEvent.ACTION_UP:  
  38.                System.out.println("MainActivity--dispatchTouchEvent:"  
  39.                          + "---MotionEvent.ACTION_UP---");  
  40.                break;  
  41.           default:  
  42.                break;  
  43.             
  44.           return super.dispatchTouchEvent(ev);  
  45.        
  46.   
  47.      @Override  
  48.      public boolean onTouchEvent(MotionEvent event)   
  49.   
  50.           switch (event.getAction())   
  51.           case MotionEvent.ACTION_DOWN:  
  52.                System.out.println("MainActivity--onTouchEvent:"  
  53.                          + "---MotionEvent.ACTION_DOWN---");  
  54.                break;  
  55.           case MotionEvent.ACTION_MOVE:  
  56.                System.out.println("MainActivity--onTouchEvent:"  
  57.                          + "---MotionEvent.ACTION_MOVE---");  
  58.                break;  
  59.           case MotionEvent.ACTION_UP:  
  60.                System.out.println("MainActivity--onTouchEvent:"  
  61.                          + "---MotionEvent.ACTION_UP---");  
  62.                break;  
  63.   
  64.           default:  
  65.                break;  
  66.             
  67.           return super.onTouchEvent(event);  
  68.        
  69.   
  70.   
   xml布局文件为空,不添加控件,运行程序,点击屏幕,LogCat输出如下:     流程图如下:      通过日志输出可以看到,点击屏幕ACTION_DOWN时候首先执行了Activity的dispatchTouchEvent方法进行事件分发,然后执行了onTouchEvent(MotionEvent event)方法;在屏幕上移动ACTION_MOVE的时候,也先是调用了Activity的dispatchTouchEvent方法,接着调用了onTouchEvent(MotionEvent event)方法,离开屏幕ACTION_UP的时候同理。dispatchTouchEvent方法的返回值是super.dispatchTouchEvent(event),因此调用了父类方法,进入Activity.java的源码中看看具体实现。 Activity.java [html]  view plain copy
  1. /**  
  2.    * Called to process touch screen events.  You can override this to  
  3.    * intercept all touch screen events before they are dispatched to the  
  4.    * window.  Be sure to call this implementation for touch screen events  
  5.    * that should be handled normally.  
  6.    *  
  7.    * @param ev The touch screen event.  
  8.    *  
  9.    * @return boolean Return true if this event was consumed.  
  10.    */  
  11.   public boolean dispatchTouchEvent(MotionEvent ev)   
  12.       if (ev.getAction() == MotionEvent.ACTION_DOWN)   
  13.           onUserInteraction();  
  14.         
  15.       if (getWindow().superDispatchTouchEvent(ev))   
  16.           return true;  
  17.         
  18.       return onTouchEvent(ev);  
  19.     
  20.    
   从源码中可以看到,dispatchTouchEvent方法只处理了ACTIONDOWN事件,前面提到过,所有的事件都是以按下为起点的,所以,Android认为当ACTIONDOWN事件没有执行时,后面的事件都是没有意义的,所以这里首先判断ACTION_DOWN事件。如果事件成立,则调用了onUserInteraction方法,代码如下:
[html]  view plain copy
  1. public void onUserInteraction()   
   可以看到该方法是一个空方法,所以其实可以在Activity中被重写,在事件被分发前会调用该方法。该方法的返回值是void型,不会对事件传递结果造成影响,接着会判断getWindow().superDispatchTouchEvent(ev)的执行结果,看看它的源码:
Activity.java [html]  view plain copy
  1. /**  
  2.  * Used by custom windows, such as Dialog, to pass the touch screen event  
  3.  * further down the view hierarchy. Application developers should  
  4.  * not need to implement or call this.  
  5.  *  
  6.  */  
  7. public abstract boolean superDispatchTouchEvent(MotionEvent event);  
   通过源码注释我们可以了解到这是个抽象方法,用于自定义的Window,例如自定义Dialog传递触屏事件,并且提到开发者不需要去实现或调用该方法,系统会完成,如果我们在MainActivity中将dispatchTouchEvent方法的返回值设为true,那么这里的执行结果就为true,从而不会返回执行onTouchEvent(ev),如果这里返回false,那么最终会返回执行onTouchEvent方法,由此可知,接下来要调用的就是onTouchEvent方法了。这也和打印输出的log一致。   那么改一下代码,使dispatchTouchEvent(MotionEvent ev)方法返回true,log输出如下:    流程图如下:

  可以看到,触屏消息已经不会传递到OnTouchEvent(MotionEvent event)方法里面了,因为 dispatchTouchEvent方法返回 true,说明消息不会继续分发到onTouchEvent()方法 。然后在这个两个方法的返回之前加入输出,也就是把父类方法的返回值打印出来, [html]  view plain copy
  1. System.out.println( "super.dispatchTouchEvent(ev):"+ super.dispatchTouchEvent(ev)+"" );  
   log输出如下:
   这说明默认情况下,如果消息没有被消费(处理),会返回false,接着事件会向下传递。如果最后也没有被处理消费,消息会向上返回回去,直到完成一个传递的过程。
2、加入显示控件情况   自定义一个Button:CustomButton,代码如下: [html]  view plain copy
    Touch事件传递机制 Android

    Android Touch事件传递机制解析 (推荐)

    html5构建触屏网站之touch事件

    Android Touch事件传递机制详解

    Android Touch事件传递机制解析

    Android Touch事件传递机制