安卓触摸事件传递机制

来源:互联网 发布:php aes解密 乱码 编辑:程序博客网 时间:2024/06/06 02:06

概述

安卓的触摸事件传递大体上是视图收到事件后进行决定是否要拦截,不拦截可以继续向内传递,拦截了不消费也可以回传给上层视图。

事件类型主要有
ACTION_DOWN(按下)
ACTION_MOVE(移动)
ACTION_UP(抬起)
ACTION_CANCEL(取消)
前面三个很好理解,共同组成一次touch事件,有时候可能没有ACTION_MOVE。ACTION_CANCEL由系统自动产生,后面会介绍到。

touch事件传递机制涉及到View中的三个方法:
1)事件分发:
dispatchTouchEvent(MotionEvent ev);
2)事件拦截:ViewGroup才有
onInterceptTouchEvent(MotionEvent ev);
3)事件响应
onTouchEvent(MotionEvent ev);

注:OnTouchListener会优先于onTouchEvent()调用,此处先不予考虑。

调用顺序是
dispatchTouchEvent() ->onInterceptTouchEvent() -> onTouchEvent()。
根据具体的返回值(都为布尔型)可能传递过程有差异。


下面具体介绍下这几个方法:

1、事件分发 dispatchTouchEvent()

事件传递到一个view时最先执行dispatchTouchEvent()方法,

  1. 如果dispatchTouchEvent()返回true,则事件不进行分发;
  2. 如果dispatchTouchEvent()返回false,则事件由上级ViewGroup的OnTouchEvent()处理;
  3. 如果dispatchTouchEvent()返回super.dispatchTouchEvent(),则由当前View处理,又分为:
    当前是ViewGroup,传递到OnInterceptTouchEvent()决定是否要拦截
    当前是普通View,直接传递到onTouchEvent()(因为ViewGroup才有OnInterceptTouchEvent()方法)。

2、事件拦截onInterceptTouchEvent()

这个方法时ViewGroup才有的,如果事件传递到当前ViewGroup,并且dispatchTouchEvent执行后确定要分发给当前view处理(返回super.dispatchTouchEvent()),则事件会传递到onInterceptTouchEvent()。
根据返回值不同,可分为两种情况:

  1. 返回true,则确定要拦截事件并交给当前view的onTouchEvent()处理;
  2. 返回false,则不进行拦截,事件传递给子视图,子视图再进行分发、拦截、响应。
    super.onInterceptTouchEvent()也是返回false。

有一个特例:
当ACTION_DOWN被当前view消费后,后续(同一次按下抬起过程中)的ACTION_MOVE和ACTION_UP会直接交给当前view的onTouchEvent(),不进入onInterceptTouchEvent(),不过还是要经过分发dispatchTouchEvent()。

这里的被当前view消费包括onInterceptTouchEvent()返回true直接消费和返回false传递给子view,但是子view不处理又传递给当前的view的onTouchEvent()处理。

3、触摸事件响应onTouchEvent()

跋山(分发)涉水(拦截),事件终于传递到当前view的onTouchEvent()来进行响应了,也是really 不容易。根据不同返回值:

  1. 返回true,代表事件已消费,不再进行传递;
  2. 返回false,代表不消费此次事件,将事件传递给上一层的onTouchEvent()来处理,如果上层不消费,继续往上传递。

到这里触摸事件传递的基本流程已经讲完了,下面介绍几个特殊情况。

=======================我是分割线=========================

4、触摸事件监听器OnTouchListener

如果设置了onTouchListener,则所有传递给onTouchEvent()的事件会优先传递给onTouchListener的onTouch()方法。
onTouch() return true: 事件已消费
onTouch() return false: 事件传递给onTouchEvent()

5、重叠视图的事件传递

假设有如下布局:

<FrameLayout            android:id="@+id/parentA"            android:layout_width="match_parent"            android:layout_height="match_parent">            <TestView                android:id="@+id/childB"                android:layout_width="match_parent"                android:layout_height="match_parent"/>            <TextView                android:id="@+id/childA"                android:layout_width="match_parent"                android:layout_height="50dp" />        </FrameLayout>

FrameLayout parentA中有两个子视图, childA和childB,A覆盖于B之上(z轴重叠)。
注:接下来的分析都假设parentA不进行事件拦截。
当触摸childA所在区域,ACTION_DOWN事件先从parentA向childA传递。
如果事件成功被childA消费(onTouchEvent() 返回true),则以后的ACTION_MOVE和ACTION_UP也是传递给childA。
反之childA没有消费该ACTION_DOWN事件的话,则该事件继续传递给childA下面的childB,如果childB消费了ACTION_DOWN,则以后的ACTION_MOVE和ACTION_UP也是传递给childB。

总结:当视图发生重叠时,后续ACTION_MOVE和ACTION_UP的接收对象与ACTION_DOWN的接收对象保持一致。

6、ACTION_CANCEL何时产生

ACTION_CANCEL与前面介绍的几种事件不一样, 不由触摸直接产生,通常不会遇到。
假设父视图parentA中有子视图childA,按下后假设ACTION_DOWN被childA消费了, 之后parentA拦截了ACTION_MOVE和ACTION_UP,则childA会收到ACTION_CANCEL事件(从事件分发dispatchTouchEvent()开始),产生该ACTION_CANCEL事件的ACTION_MOVE或者ACTION_UP事件也会停止,不传递到parentA的onTouchEvent(),即使他已经拦截了。
但是后续如果还有move或者up事件,parentA拦截下来还是会传递到onTouchEvent(),通常move事件是连续多次的不会有影响,但是如果一次触摸只有ACTION_DOWN和ACTION_UP,那么刚才这种情况就会导致ACTION_UP出发ACTION_CANCEL,而不被正确处理,需要注意。

总结:
一个视图处理了ACTION_DOWN事件,而没有收到后续的ACTION_MOVE和ACTION_UP(可能被拦截了),那么该视图就会收到ACTION_CANCEL,并且触发ACTION_CANCEL的MOVE或者UP也会立即停止,不继续传递不被消费。


写在最后

安卓touch事件传递流程还是很清晰的,最好学习的方式就是自己定义几个view重写
dispatchTouchEvent(MotionEvent ev);
onInterceptTouchEvent(MotionEvent ev);
onTouchEvent(MotionEvent ev);
这个几个方法,分别对里面的ACTION_DOWN,ACTION_MOVE,ACTION_UP进行不同处理,返回不同值,打出log来分析。
在自定义功能复杂一点的View时,对touch事件的理解还是比较重要的,
还在学习,希望对大家有所帮助。

END.

1 0
原创粉丝点击