android中,触摸事件的传递过程主要涉及三个方法:dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent。
  详细了解这三个方法的作用首先要了解以下几个知识点:

  • android中的Touch事件都是从ACTION_DOWN开始的:
    单指:ACTION_DOWN->ACTION_MOVE->ACTION_UP;
    多指:ACTION_DOWN->ACTION_POINTER_DOWN->ACTION_MOVE->ACTION_POINTER_UP->ACTION_UP
  • 一次完整的触摸事件中,Down和Up都只有一个,Move有若干个,可以为0个。当触摸事件被拦截时,Up可能是0个。
  • View在ViewGroup内,ViewGroup也可以在其他ViewGroup内,这时候把内部的ViewGroup当成View来分析。

知道了以上基本知识点以后,就可以开始了~

Activity、ViewGroup、View里的回调方法

在Activity里,有两个回调方法:

public boolean dispatchTouchEvent(MotionEvent ev);    public boolean onTouchEvent(MotionEvent ev);    

在ViewGroup里,有三个回调方法:

public boolean dispatchTouchEvent(MotionEvent ev);    public boolean onInterceptTouchEvent(MotionEvent ev);    public boolean onTouchEvent(MotionEvent ev);  

在View里,和Activity相同,同样有两个回调方法:

public boolean dispatchTouchEvent(MotionEvent ev);    public boolean onTouchEvent(MotionEvent ev);    

总结起来就是:

  • 和事件分发相关的方法共有三个:dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent
  • dispatchTouchEvent和onTouchEvent在Activity、ViewGroup和View中均存在
  • 只有ViewGroup中有onInterceptTouchEvent方法

dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent的区别

  • dispatchTouchEvent:这个方法用来分发TouchEvent。
  • onInterceptTouchEvent:这个方法用来拦截TouchEvent。
  • onTouchEvent:这个方法用来处理TouchEvent。
      一个简单的图可以表示这三个方法的执行过程:


    3.png

      举个例子:使用下面这段代码的xml布局

          

这个布局长这样:节点层次很简单,一个LinearLayout中添加了一个TextView。


4.png

  MainActivity.java的代码:

// import的包不贴了……public class MainActivity extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        Log.e("demo","MainActivity-----------onTouchEvent--------------" + event.toString());        return super.onTouchEvent(event);    }    @Override    public boolean dispatchTouchEvent(MotionEvent event) {        Log.e("demo","MainActivity-----------dispatchTouchEvent--------" + event.toString());        return super.dispatchTouchEvent(event);    }}

ChiaroLinearLayout.java的代码:

// import的包不贴了……public class ChiaroLinearLayout extends LinearLayout {    // 构造方法省略不贴了……    @Override    public boolean onInterceptTouchEvent(MotionEvent event) {        Log.e("demo", "ChiaroLinearLayout-----onInterceptTouchEvent-----" + event.toString());        return super.onInterceptTouchEvent(event);    }    @Override    public boolean dispatchTouchEvent(MotionEvent event) {        Log.e("demo","ChiaroLinearLayout-----dispatchTouchEvent--------" + event.toString());        return super.dispatchTouchEvent(event);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        Log.e("demo","ChiaroLinearLayout-----onTouchEvent--------------" + event.toString());        return super.onTouchEvent(event);    }}

ChiaroTextView.java的代码:

// import的包不贴了……public class ChiaroTextView extends TextView {    // 构造方法省略不贴了……    @Override    public boolean onTouchEvent(MotionEvent event) {        Log.e("demo", "ChiaroTextView---------onTouchEvent--------------" + event.toString());        return super.onTouchEvent(event);    }    @Override    public boolean dispatchTouchEvent(MotionEvent event) {        Log.e("demo","ChiaroTextView---------dispatchTouchEvent--------" + event.toString());        return super.dispatchTouchEvent(event);    }}

可以看出,这段代码只是简单的打出所有的log。直接运行并点击一下TextView可以看到log如下。可以看到,这个ACTION_DOWN事件一直传递到了ChiaroTextView,但是最终是被MainActivity的onTouchEvent处理的,但是ACTION_UP只传递到了MainActivity,最终也是由MainActivity处理的。

 …… E/demo: MainActivity-----------dispatchTouchEvent--------MotionEvent { action=ACTION_DOWN …… …… E/demo: ChiaroLinearLayout-----dispatchTouchEvent--------MotionEvent { action=ACTION_DOWN …… …… E/demo: ChiaroLinearLayout-----onInterceptTouchEvent-----MotionEvent { action=ACTION_DOWN …… …… E/demo: ChiaroTextView---------dispatchTouchEvent--------MotionEvent { action=ACTION_DOWN …… …… E/demo: ChiaroTextView---------onTouchEvent--------------MotionEvent { action=ACTION_DOWN …… …… E/demo: ChiaroLinearLayout-----onTouchEvent--------------MotionEvent { action=ACTION_DOWN …… …… E/demo: MainActivity-----------onTouchEvent--------------MotionEvent { action=ACTION_DOWN …… …… E/demo: MainActivity-----------dispatchTouchEvent--------MotionEvent { action=ACTION_UP …… …… E/demo: MainActivity-----------onTouchEvent--------------MotionEvent { action=ACTION_UP ……

详细分析

onInterceptTouchEvent-事件拦截

onInterceptTouchEvent这个方法的返回值是最简单的,及是否拦截事件,放在最前面讲。

  • 如果返回值是true,代表事件在当前的viewGroup中会被处理,向下传递之路被截断(所有子控件将没有机会参与Touch事件),同时把事件传递给当前的控件的onTouchEvent()继续进行传递或处理。
  • 如果返回值是false,即不拦截当前传递来的事件,会继续向下传递,把事件交给子控件的onInterceptTouchEvent()。
      如果将ChiaroLinearLayout中的onInterceptTouchEvent方法的返回值改为true,则log为:
 …… E/demo: MainActivity-----------dispatchTouchEvent--------MotionEvent { action=ACTION_DOWN …… …… E/demo: ChiaroLinearLayout-----dispatchTouchEvent--------MotionEvent { action=ACTION_DOWN …… …… E/demo: ChiaroLinearLayout-----onInterceptTouchEvent-----MotionEvent { action=ACTION_DOWN ……// 这里的ChiaroTextView没了  因为被父控件ChairoLinearLayout拦截了 …… E/demo: ChiaroLinearLayout-----onTouchEvent--------------MotionEvent { action=ACTION_DOWN …… …… E/demo: MainActivity-----------onTouchEvent--------------MotionEvent { action=ACTION_DOWN …… …… E/demo: MainActivity-----------dispatchTouchEvent--------MotionEvent { action=ACTION_UP …… …… E/demo: MainActivity-----------onTouchEvent--------------MotionEvent { action=ACTION_UP ……

可以看到,这个事件在ChiaroLinearLayout就被打断了,没有继续传递给ChiaroTextView,而是由ChiaroLinearLayout的onTouchEvent继续传递给MainActivity的OnTouchEvent,最终由MainActivity的OnTouchEvent处理了。

onTouchEvent-事件处理

  • 如果返回值是true,表示消费(consume)了这个事件。以ACTION_DOWN为例,如果某个控件的onTouchEvent返回值为true,则后续的n个ACTION_MOVE与1个ACTION_UP都会逐层传递到这个控件的onTouchEvent进行处理。
  • 由于触摸事件都是连续的,所以这个地方感谢网友【@再不侃】的提问,要特别说明一下。如果ACTION_MOVE传递到子控件,而子控件的onTouchEvent返回值是false,即没有处理该ACTION_MOVE事件,则后续的ACTION_UP就不会传到该子控件来了。这个原因就造成了最开始没有对代码进行任何变更时,ACTION_DOWN事件一直传递到了ChiaroTextView,但是最终是被MainActivity的onTouchEvent处理的,而且ACTION_UP只传递到了MainActivity,最终也是由MainActivity处理的。的情况。
  • 这里要注意是逐层,也就是说每层的拦截器还是可以拦截到后续的ACTION_MOVE与ACTION_UP。如果后续的ACTION_MOVE与ACTION_UP被某层的拦截器拦截,则后续的事件将不会再传递给之前处理onTouchEvent的子控件,而是逐层传递给由拦截消息的这个控件的onTouchEvent函数进行处理,并且会向其之前接收事件的子控件发送一个ACTION_CANCEL,表示后续事件被取消了。
  • 如果返回值是false,则会将ACTION_DOWN传递给其父ViewGroup的onTouchEvent进行处理,直到由哪一层ViewGroup消费了ACTION_DOWN事件为止。
      如果讲上面的代码还原,并且将ChiaroTextView的onTouchEvent方法的返回值改为true,则log:
 …… E/demo: MainActivity-----------dispatchTouchEvent--------MotionEvent { action=ACTION_DOWN ……  …… E/demo: ChiaroLinearLayout-----dispatchTouchEvent--------MotionEvent { action=ACTION_DOWN ……  …… E/demo: ChiaroLinearLayout-----onInterceptTouchEvent-----MotionEvent { action=ACTION_DOWN ……  …… E/demo: ChiaroTextView---------dispatchTouchEvent--------MotionEvent { action=ACTION_DOWN ……  …… E/demo: ChiaroTextView---------onTouchEvent--------------MotionEvent { action=ACTION_DOWN ……  …… E/demo: MainActivity-----------dispatchTouchEvent--------MotionEvent { action=ACTION_UP ……  …… E/demo: ChiaroLinearLayout-----dispatchTouchEvent--------MotionEvent { action=ACTION_UP ……  …… E/demo: ChiaroLinearLayout-----onInterceptTouchEvent-----MotionEvent { action=ACTION_UP ……  …… E/demo: ChiaroTextView---------dispatchTouchEvent--------MotionEvent { action=ACTION_UP ……  …… E/demo: ChiaroTextView---------onTouchEvent--------------MotionEvent { action=ACTION_UP …… 

可以看到,所有的事件都传递给ChiaroTextView处理了。包括ACTION_DOWN和ACTION_UP。

dispatchTouchEvent-事件分发

dispatchTouchEvent比较复杂,可以按照下面这张图分析:ViewGroup和View组成了一棵树形结构,最顶层为Activity的ViewGroup,下面有若干的ViewGroup节点,每个节点之下又有若干的ViewGroup节点或者View节点,依次类推。


5.png

  当一个Touch事件依次下发,下发的过程是调用子View(ViewGroup)的dispatchTouchEvent方法实现的。简单来说,就是ViewGroup遍历它包含着的子View,调用每个View的dispatchTouchEvent方法,而当子View为ViewGroup时,又会通过调用ViewGroup的dispatchTouchEvent方法继续调用其内部的View的dispatchTouchEvent方法。上述例子中的消息下发顺序是这样的:①-②-⑤-⑥-⑦-③-④。
  dispatchTouchEvent方法只负责事件的分发,它拥有boolean类型的返回值,当返回为true时,顺序下发会中断。在上述例子中如果⑤的dispatchTouchEvent返回结果为true,那么⑥-⑦-③-④将都接收不到本次Touch事件。
  ViewGroup的dispatchTouchEvent是真正在执行“分发”工作,而View的dispatchTouchEvent方法,并不执行分发工作,或者说它分发的对象就是自己,决定是否把touch事件交给自己处理,而处理的方法,便是onTouchEvent事件,事实上子View的dispatchTouchEvent方法真正执行的代码是这样的:

public boolean dispatchTouchEvent(MotionEvent ev){     ....//其他处理,在此不管     return onTouchEvent(event); }

一般情况下,我们不该在普通View内重写dispatchTouchEvent方法,因为它并不执行分发逻辑。当Touch事件到达View时,我们该做的就是是否在onTouchEvent事件中处理它。

更多相关文章

  1. [Android(安卓)Studio导入第三方类库方法] Error:(19, 23) 错误:
  2. android 存储联系人(save contact) 总结
  3. Android(安卓)中数据库查询方法query()中的selectionArgs的用法
  4. android --相机使用详解概述
  5. 2011.07.01——— android GridView 长按事件不管用
  6. Android开发之InstanceState详解
  7. android对话框大全
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. Android Preference置灰显示
  2. android 支持webrtc
  3. Android(安卓)Paint和Color类绘画实例
  4. Android文件关联
  5. Android编译报Errors running builder 'A
  6. 使用Android Studio打包指定名称的Apk
  7. Android中Crash(闪退,崩溃)的一般问题与解
  8. Android实现九宫格图案解锁
  9. Android Studio中集成OpenCV——只需4步
  10. Android开发环境搭建之HelloWorld