Android touch事件学习笔记

概要

本文主要是借助于Android源码分析Activity、ViewGroup和View中touch事件的分发和消费机制以及它是如何通过touch事件来处理click点击事件的。

Android中touch事件很长时间下都很糊涂,以前也没有想深入下去,用的时候也是随便搜搜资料看一下,反而又把自己搞晕了,这次抽点时间把相关知识点整理一下,以备日后温习巩固。
Android中处理touch事件的方法主要包括下面三个:

  • public boolean dispatchTouchEvent(MotionEvent ev)
  • public boolean onInterceptTouchEvent(MotionEvent ev)
  • public boolean onTouchEvent(MotionEvent event)
Touch 事件相关方法 方法功能 Activity ViewGroup View
public boolean dispatchTouchEvent(MotionEvent ev) 事件分发 Yes Yes Yes
public boolean onInterceptTouchEvent(MotionEvent ev) 事件拦截 No Yes No
public boolean onTouchEvent(MotionEvent ev) 事件响应 Yes Yes Yes

有关touch事件都被封装成MotionEvent对象,包括touch的历史记录、记录等。

  • 常用事件类型分为ACTION_DOWN,ACTION_UP,ACTION_MOVE,ACTION_CANCEL, 每一个完整的事件以ACTION_DOWN开始ACTION_UP结束,并且ACTION_CANCEL只能由代码引起,一般对于CANCEL的处理和UP的相同。 如:手指在移动的过程中突然移动到了边界外,那么这时ACTION_UP事件了,所以这是的CANCEL和UP的处理是一致的。
  • 事件从Activity的dispatchTouchEvent()开始传递,只要没有停止拦截,就会从最上层(ViewGroup)开始一直往下传递,子View通过onTouchEvent()消费事件(隧道式向下分发);
  • 如果时间从上往下一直传递到最底层的子View,但是该View没有消费该事件,那么该事件会反序网上传递(从该View传递给自己的ViewGroup,然后再传给更上层的ViewGroup直至传递给Activity.onTouchEvent())(冒泡式向上处理);
  • 如果View没有消费ACTION_DOWN事件,之后其他的MOVE、UP等事件都不会传递过来;
  • 事件由父View(ViewGroup)传递给子View,ViewGroup可以通过onInterceptTouchEvent()方法对事件进行拦截,停止其往下传递,如果拦截(返回true)后该事件会直接走到该ViewGroup中的onTouchEvent()中,不会再往下传递给子View.如果从DOWN开始,之后的MOVE、UP都会直接在该ViewGroup.onTouchEvent()中进行处理。如果子View之前在处理某个事件,但是后续被ViewGroup拦截,那么子View会接收到ACTION_CANCEL;
  • OnTouchListener优先于onTouchEvent()对事件进行消费;
  • 任何一次touch事件都被消费掉,如果子不消费,最终会向上冒泡给事件源(Activity)并被消费掉;
  • 多数情况下不会重写dispatchTouchEvent,但是其余两个方法会在使用中常被重写;
  • Activity有关事件处理方法 有两个分别是dispatchTouchEvent、onTouchEvent,View中分别是dispatchTouchEvent、onTouchEvent,ViewGroup中为dispatchTouchEvent、onInterceptTouchEvent。

部分源码分析

Activity中相关处理事件

代码一看能感觉出来DOWN事件比较特殊。我们继续走到onUserInteraction()代码中。

但是该方法是空方法,没有具体实现。 我们往下看getWindow().superDispatchTouchEvent(ev).getWindow()获取到当前Window对象,表示顶层窗口,管理界面的显示和事件的响应;每个Activity 均会创建一个Window对象,是Activity和整个View系统交互的接口,但是该类是一个抽象类。接下来看一下文档描述:

The only existing implementation of this abstract class is android.policy.PhoneWindow, which you should instantiate when needing a Window.

所以我们找到PhoneWindow类,查看它的superDispatchTouchEvent()方法。

该方法又是调用了mDecor.superDispatchTouchEvent(event), mDecor是什么呢? 从名字中我们大概也能猜出来是当前窗口最顶层的DecorView,Window界面的最顶层的View对象。

它集成子FrameLayout所有很多时候我们在用布局工具查看的时候发现Activity的布局FrameLayout的。就是这个原因。
好了,我们接着看DecorView中的superDispatchTouchEvent()方法。

是调用了super.dispatchTouchEvent(),而DecorView的父类是FrameLayout所以我们找到FrameLayout.dispatchTouchEvent().我们看到FrameLayout中没有重写dispatchTouchEvent()方法,所以我们再找到FrameLayout的父类ViewGroup.看ViewGroup.dispatchTouchEvent()实现。

接下来还要说说dispatchTransformedTouchEvent()方法,虽然上面也说了大体功能,但是看一下源码能说明另一个问题:

上面讲了ViewGroup的dispatchTouchEveent()有些地方会调用super.dispatchTouchEveent(),而ViewGroup的父类就是View,接下来我们看一下View.dispatchTouchEveent()方法:

通过上面的分析我们看到View.dispatchTouchEvent()里面会调用到onTouchEvent()来消耗事件。那么onTouchEvent()是如何处理的呢?下面我们看一下
View.onTouchEvent()源码:

面讲了Touch事件的分发和处理,随便说一下点击事件:我们平时使用的时候都知道给View设置点击事件是setOnClickListener()

那什么地方会调用mListenerInfo.mOnClickListener呢?

讲到这里就明白了。onTouchEvent()中的ACTION_UP中会调用performClick()方法。

小结

本篇核心内容转载自Android Touch事件分发详解,更多总结性的言语也就不再发表了,比较让人头痛的Android touch事件的处理机制今天总算明了一些了,最近在看Android下拉刷新控件的源码,总是被搞得晕来转去的,下面应该就可以静下心来好好的研究一下了,明天就是双11了,本想搭上节日气氛买些东西的,发现自己的淘宝竟然要安全验证却也一直收不到验证信息,快到凌晨了,也只能洗洗睡吧…

本文地址www.sunnyang.com/326.html

示例源代码下载

参考资料

Android Touch事件分发详解

Android 编程下 Touch 事件的分发和消费机制

Android触摸屏事件派发机制详解与源码分析一

发表评论