Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev);可以响应这些方法的控件包括:ViewGroup 及其子类、Activity。方法与控件的对应关系以下表所示:android
Touch 事件相关方法 | 方法功能 | ViewGroup | Activity |
public boolean dispatchTouchEvent(MotionEvent ev) | 事件分发 | Yes | Yes |
public boolean onInterceptTouchEvent(MotionEvent ev) | 事件拦截 | Yes | No |
public boolean onTouchEvent(MotionEvent ev) | 事件响应 | Yes | Yes |
从这张表中咱们能够看到 ViewGroup 及其子类对与 Touch 事件相关的三个方法均能响应,而 Activity 对 onInterceptTouchEvent(MotionEvent ev) 也就是事件拦截不进行响应。另外须要注意的是 View 对 dispatchTouchEvent(MotionEvent ev) 和 onInterceptTouchEvent(MotionEvent ev) 的响应的前提是能够向该 View 中添加子 View,若是当前的 View 已是一个最小的单元 View(好比 TextView),那么就没法向这个最小 View 中添加子 View,也就没法向子 View 进行事件的分发和拦截,因此它没有 dispatchTouchEvent(MotionEvent ev) 和 onInterceptTouchEvent(MotionEvent ev),只有 onTouchEvent(MotionEvent ev)。git
1、Touch 事件分析github
▐ 事件分发:public boolean dispatchTouchEvent(MotionEvent ev)app
Touch 事件发生时 Activity 的 dispatchTouchEvent(MotionEvent ev) 方法会以隧道方式(从根元素依次往下传递直到最内层子元素或在中间某一元素中因为某一条件中止传递)将事件传递给最外层 View 的 dispatchTouchEvent(MotionEvent ev) 方法,并由该 View 的 dispatchTouchEvent(MotionEvent ev) 方法对事件进行分发。dispatchTouchEvent 的事件分发逻辑以下:工具
▐ 事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev) 布局
在外层 View 的 dispatchTouchEvent(MotionEvent ev) 方法返回系统默认的 super.dispatchTouchEvent(ev) 状况下,事件会自动的分发给当前 View 的 onInterceptTouchEvent 方法。onInterceptTouchEvent 的事件拦截逻辑以下:测试
▐ 事件响应:public boolean onTouchEvent(MotionEvent ev)spa
在 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev) 而且 onInterceptTouchEvent 返回 true 或返回 super.onInterceptTouchEvent(ev) 的状况下 onTouchEvent 会被调用。onTouchEvent 的事件响应逻辑以下:xml
到这里,与 Touch 事件相关的三个方法就分析完毕了。下面的内容会经过各类不一样的的测试案例来验证上文中三个方法对事件的处理逻辑。继承
2、Touch 案例介绍
一样在开始进行案例分析以前,我须要说明测试案例的结构,由于全部的测试都是针对这一个案例来进行的,测试中只是经过修改每一个控件中与 Touch 事件相关的三个方法的返回值来体现不一样的状况。先来看张图:
上面的图为测试案例的布局文件 UI 显示效果,布局文件代码以下:
<?xml version="1.0" encoding="utf-8"?> <cn.sunzn.tevent.TouchEventFather xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#468AD7" android:gravity="center" android:orientation="vertical" > <cn.sunzn.tevent.TouchEventChilds android:id="@+id/childs" android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="center" android:background="#E1110D" android:text="@string/hello" /> </cn.sunzn.tevent.TouchEventFather>
蓝色背景为一个自定义控件 TouchEventFather,该控件为外层 View,继承自 LinearLayout,实现代码以下:
package cn.sunzn.tevent; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.widget.LinearLayout; public class TouchEventFather extends LinearLayout { public TouchEventFather(Context context) { super(context); } public TouchEventFather(Context context, AttributeSet attrs) { super(context, attrs); } public boolean dispatchTouchEvent(MotionEvent ev) { Log.e("sunzn", "TouchEventFather | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction())); return super.dispatchTouchEvent(ev); } public boolean onInterceptTouchEvent(MotionEvent ev) { Log.i("sunzn", "TouchEventFather | onInterceptTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction())); return super.onInterceptTouchEvent(ev); } public boolean onTouchEvent(MotionEvent ev) { Log.d("sunzn", "TouchEventFather | onTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction())); return super.onTouchEvent(ev); } }
红色背景为一个自定义控件 TouchEventChilds,该控件为内层 View,为 TouchEventFather 的子 View,一样继承自 LinearLayout,实现代码以下:
package cn.sunzn.tevent; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.widget.LinearLayout; public class TouchEventChilds extends LinearLayout { public TouchEventChilds(Context context) { super(context); } public TouchEventChilds(Context context, AttributeSet attrs) { super(context, attrs); } public boolean dispatchTouchEvent(MotionEvent ev) { Log.e("sunzn", "TouchEventChilds | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction())); return super.dispatchTouchEvent(ev); } public boolean onInterceptTouchEvent(MotionEvent ev) { Log.i("sunzn", "TouchEventChilds | onInterceptTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction())); return super.onInterceptTouchEvent(ev); } public boolean onTouchEvent(MotionEvent ev) { Log.d("sunzn", "TouchEventChilds | onTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction())); return super.onTouchEvent(ev); } }
接着实现 Activity 的代码,由于控件全部的事件都是经过 Activity 的 dispatchTouchEvent 进行分发的;除此以外还须要重写 Activity 的 onTouchEvent 方法,这是由于若是一个控件直接从 Activity 获取到事件,这个事件会首先被传递到控件的 dispatchTouchEvent 方法,若是这个方法 return false,事件会以冒泡方式返回给 Activity 的 onTouchEvent 进行消费。实现代码以下:
package cn.sunzn.tevent; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.MotionEvent; public class TouchEventActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } public boolean dispatchTouchEvent(MotionEvent ev) { Log.w("sunzn", "TouchEventActivity | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction())); return super.dispatchTouchEvent(ev); } public boolean onTouchEvent(MotionEvent event) { Log.w("sunzn", "TouchEventActivity | onTouchEvent --> " + TouchEventUtil.getTouchAction(event.getAction())); return super.onTouchEvent(event); } }
最后再附上 TouchEventUtil 的代码,TouchEventUtil 中并无作多少事情,只是将以上 2 个自定义控件中各个方法的 MotionEvent 集中到一个工具类中并将其对应的动做以 String 形式返回,这样处理更便于实时观察控件的事件。代码以下:
package cn.sunzn.tevent; import android.view.MotionEvent; public class TouchEventUtil { public static String getTouchAction(int actionId) { String actionName = "Unknow:id=" + actionId; switch (actionId) { case MotionEvent.ACTION_DOWN: actionName = "ACTION_DOWN"; break; case MotionEvent.ACTION_MOVE: actionName = "ACTION_MOVE"; break; case MotionEvent.ACTION_UP: actionName = "ACTION_UP"; break; case MotionEvent.ACTION_CANCEL: actionName = "ACTION_CANCEL"; break; case MotionEvent.ACTION_OUTSIDE: actionName = "ACTION_OUTSIDE"; break; } return actionName; } }