二、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; } }
三、Touch 案例分析
Case 1
拦截条件 | |||||
控件名称 | dispatchTouchEvent 返回值 | onInterceptTouchEvent 返回值 | onTouchEvent 返回值 | ||
TouchEventActivity | super.dispatchTouchEvent(ev) | --- | super.onTouchEvent(ev) | ||
TouchEventFather | false | super.onInterceptTouchEvent(ev) | super.onTouchEvent(ev) | ||
TouchEventChilds | super.dispatchTouchEvent(ev) | super.onInterceptTouchEvent(ev) | super.onTouchEvent(ev) | ||
运行结果 | |||||
Level | Time | PID | Application | Tag | Text |
W | 05-10 03:41:19.743 | 414 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_DOWN |
E | 05-10 03:41:19.743 | 414 | cn.sunzn.tevent | sunzn | TouchEventFather | dispatchTouchEvent --> ACTION_DOWN |
W | 05-10 03:41:19.743 | 414 | cn.sunzn.tevent | sunzn | TouchEventActivity | onTouchEvent --> ACTION_DOWN |
W | 05-10 03:41:19.743 | 414 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE |
W | 05-10 03:41:19.743 | 414 | cn.sunzn.tevent | sunzn | TouchEventActivity | onTouchEvent --> ACTION_MOVE |
W | 05-10 03:41:19.743 | 414 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE |
W | 05-10 03:41:19.743 | 414 | cn.sunzn.tevent | sunzn | TouchEventActivity | onTouchEvent --> ACTION_MOVE |
W | 05-10 03:41:19.743 | 414 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_UP |
W | 05-10 03:41:19.743 | 414 | cn.sunzn.tevent | sunzn | TouchEventActivity | onTouchEvent --> ACTION_UP |
结果分析 | |||||
代码运行后,事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分发给 TouchEventFather 控件的dispatchTouchEvent,而该控件的 dispatchTouchEvent 返回 false,表示对获取到的事件停止向下传递,同时也不对该事件进行消费。由于 TouchEventFather 获取的事件直接来自 TouchEventActivity ,则会将事件返回给 TouchEventActivity 的 onTouchEvent 进行消费,最后直接由 TouchEventActivity 来响应手指移动和抬起事件。 |
Case 2
拦截条件 | |||||
控件名称 | dispatchTouchEvent 返回值 | onInterceptTouchEvent 返回值 | onTouchEvent 返回值 | ||
TouchEventActivity | super.dispatchTouchEvent(ev) | --- | super.onTouchEvent(ev) | ||
TouchEventFather | true | super.onInterceptTouchEvent(ev) | super.onTouchEvent(ev) | ||
TouchEventChilds | super.dispatchTouchEvent(ev) | super.onInterceptTouchEvent(ev) | super.onTouchEvent(ev) | ||
运行结果 | |||||
Level | Time | PID | Application | Tag | Text |
W | 05-10 03:41:19.743 | 414 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_DOWN |
E | 05-10 03:41:19.743 | 414 | cn.sunzn.tevent | sunzn | TouchEventFather | dispatchTouchEvent --> ACTION_DOWN |
W | 05-10 03:41:19.743 | 414 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE |
E | 05-10 03:41:19.743 | 414 | cn.sunzn.tevent | sunzn | TouchEventFather | dispatchTouchEvent --> ACTION_MOVE |
W | 05-10 03:41:19.743 | 414 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE |
E | 05-10 03:41:19.743 | 414 | cn.sunzn.tevent | sunzn | TouchEventFather | dispatchTouchEvent --> ACTION_MOVE |
W | 05-10 03:41:19.743 | 414 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE |
E | 05-10 03:41:19.743 | 414 | cn.sunzn.tevent | sunzn | TouchEventFather | dispatchTouchEvent --> ACTION_MOVE |
W | 05-10 03:41:19.743 | 414 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_UP |
E | 05-10 03:41:19.743 | 414 | cn.sunzn.tevent | sunzn | TouchEventFather | dispatchTouchEvent --> ACTION_UP |
结果分析 | |||||
代码运行后,事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分发给 TouchEventFather 控件的 dispatchTouchEvent,而该控件的 dispatchTouchEvent 返回 true,表示分发事件到 TouchEventFather 控件并由该控件的 dispatchTouchEvent 进行消费;TouchEventActivity 不断的分发事件到 TouchEventFather 控件的dispatchTouchEvent,而 TouchEventFather 控件的 dispatchTouchEvent 也不断的将获取到的事件进行消费。 |
Case 3
拦截条件 | |||||
控件名称 | dispatchTouchEvent 返回值 | onInterceptTouchEvent 返回值 | onTouchEvent 返回值 | ||
TouchEventActivity | super.dispatchTouchEvent(ev) | --- | super.onTouchEvent(ev) | ||
TouchEventFather | super.dispatchTouchEvent(ev) | true | super.onTouchEvent(ev) | ||
TouchEventChilds | super.dispatchTouchEvent(ev) | super.onInterceptTouchEvent(ev) | super.onTouchEvent(ev) | ||
运行结果 | |||||
Level | Time | PID | Application | Tag | Text |
W | 05-10 05:34:46.333 | 481 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_DOWN |
E | 05-10 05:34:46.333 | 481 | cn.sunzn.tevent | sunzn | TouchEventFather | dispatchTouchEvent --> ACTION_DOWN |
I | 05-10 05:34:46.333 | 481 | cn.sunzn.tevent | sunzn | TouchEventFather | onInterceptTouchEvent --> ACTION_DOWN |
D | 05-10 05:34:46.333 | 481 | cn.sunzn.tevent | sunzn | TouchEventFather | onTouchEvent --> ACTION_DOWN |
W | 05-10 05:34:46.333 | 481 | cn.sunzn.tevent | sunzn | TouchEventActivity | onTouchEvent --> ACTION_DOWN |
W | 05-10 05:34:46.343 | 481 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE |
W | 05-10 05:34:46.343 | 481 | cn.sunzn.tevent | sunzn | TouchEventActivity | onTouchEvent --> ACTION_MOVE |
W | 05-10 05:34:46.423 | 481 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE |
W | 05-10 05:34:46.423 | 481 | cn.sunzn.tevent | sunzn | TouchEventActivity | onTouchEvent --> ACTION_MOVE |
W | 05-10 05:34:46.433 | 481 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE |
W | 05-10 05:34:46.433 | 481 | cn.sunzn.tevent | sunzn | TouchEventActivity | onTouchEvent --> ACTION_MOVE |
W | 05-10 05:34:46.442 | 481 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_UP |
W | 05-10 05:34:46.442 | 481 | cn.sunzn.tevent | sunzn | TouchEventActivity | onTouchEvent --> ACTION_UP |
结果分析 | |||||
代码运行后,事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分发给 TouchEventFather 控件的 dispatchTouchEvent,而该控件的 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev),表示对事件进行分发并向下传递给 TouchEventFather 控件的 onInterceptTouchEvent 方法,该方法返回 true 表示对所获取到的事件进行拦截并将事件传递给 TouchEventFather 控件的 onTouchEvent 进行处理,TouchEventFather 控件的 onTouchEvent 返回 super.onTouchEvent(ev) 表示对事件没有做任何处理直接将事件返回给上级控件,由于 TouchEventFather 获取的事件直接来自 TouchEventActivity,所以 TouchEventFather 控件的 onTouchEvent 会将事件以冒泡方式直接返回给 TouchEventActivity 的 onTouchEvent 进行消费,后续的事件则会跳过 TouchEventFather 直接由 TouchEventActivity 的 onTouchEvent 消费来自 TouchEventActivity 自身分发的事件。 |
Case 4
拦截条件 | |||||
控件名称 | dispatchTouchEvent 返回值 | onInterceptTouchEvent 返回值 | onTouchEvent 返回值 | ||
TouchEventActivity | super.dispatchTouchEvent(ev) | --- | super.onTouchEvent(ev) | ||
TouchEventFather | super.dispatchTouchEvent(ev) | false | super.onTouchEvent(ev) | ||
TouchEventChilds | super.dispatchTouchEvent(ev) | super.onInterceptTouchEvent(ev) | super.onTouchEvent(ev) | ||
运行结果 | |||||
Level | Time | PID | Application | Tag | Text |
W | 05-10 06:31:47.565 | 512 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_DOWN |
E | 05-10 06:31:47.565 | 512 | cn.sunzn.tevent | sunzn | TouchEventFather | dispatchTouchEvent --> ACTION_DOWN |
I | 05-10 06:31:47.565 | 512 | cn.sunzn.tevent | sunzn | TouchEventFather | onInterceptTouchEvent --> ACTION_DOWN |
E | 05-10 06:31:47.565 | 512 | cn.sunzn.tevent | sunzn | TouchEventChilds | dispatchTouchEvent --> ACTION_DOWN |
I | 05-10 06:31:47.565 | 512 | cn.sunzn.tevent | sunzn | TouchEventChilds | onInterceptTouchEvent --> ACTION_DOWN |
D | 05-10 06:31:47.565 | 512 | cn.sunzn.tevent | sunzn | TouchEventChilds | onTouchEvent --> ACTION_DOWN |
D | 05-10 06:31:47.565 | 512 | cn.sunzn.tevent | sunzn | TouchEventFather | onTouchEvent --> ACTION_DOWN |
W | 05-10 06:31:47.565 | 512 | cn.sunzn.tevent | sunzn | TouchEventActivity | onTouchEvent --> ACTION_DOWN |
W | 05-10 06:31:47.652 | 512 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE |
W | 05-10 06:31:47.652 | 512 | cn.sunzn.tevent | sunzn | TouchEventActivity | onTouchEvent --> ACTION_MOVE |
W | 05-10 06:31:47.732 | 512 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE |
W | 05-10 06:31:47.732 | 512 | cn.sunzn.tevent | sunzn | TouchEventActivity | onTouchEvent --> ACTION_MOVE |
W | 05-10 06:31:47.812 | 512 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE |
W | 05-10 06:31:47.812 | 512 | cn.sunzn.tevent | sunzn | TouchEventActivity | onTouchEvent --> ACTION_MOVE |
W | 05-10 06:31:47.892 | 512 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_UP |
W | 05-10 06:31:47.892 | 512 | cn.sunzn.tevent | sunzn | TouchEventActivity | onTouchEvent --> ACTION_UP |
结果分析 | |||||
代码运行后,事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分发给 TouchEventFather 控件的 dispatchTouchEvent,而该控件的 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev),表示对事件进行分发并向下传递给 TouchEventFather 控件的 onInterceptTouchEvent 方法,该方法返回 false 表示事件会被放行并传递到子控件 TouchEventChilds 的 dispatchTouchEvent 方法,同样 TouchEventChilds 的 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev),表示对事件进行分发并向下传递给 TouchEventChilds 控件的 onInterceptTouchEvent 方法,TouchEventChilds 的 onInterceptTouchEvent 方法返回 super.onInterceptTouchEvent(ev) 默认会将事件传递给 TouchEventChilds 的 onTouchEvent 进行处理,TouchEventChilds 的 onTouchEvent 返回 super.onTouchEvent(ev) 表示对事件没有做任何处理直接将事件返回给上级控件,由于 TouchEventChilds 获取的事件直接来自 TouchEventFather,所以 TouchEventChilds 控件的 onTouchEvent 会将事件以冒泡方式直接返回给 TouchEventFather 的 onTouchEvent 进行消费,而 TouchEventFather 的 onTouchEvent 也返回了 super.onTouchEvent(ev),同样 TouchEventFather 的 onTouchEvent 也会将事件返回给上级控件,而 TouchEventFather 获取的事件直接来自 TouchEventActivity,所以 TouchEventFather 控件的 onTouchEvent 会将事件以冒泡方式直接返回给 TouchEventActivity 的 onTouchEvent 进行消费,后续的事件则会跳过 TouchEventFather 和 TouchEventChilds 直接由 TouchEventActivity 的 onTouchEvent 消费来自 TouchEventActivity 自身分发的事件。 |
Case 5
拦截条件 | |||||
控件名称 | dispatchTouchEvent 返回值 | onInterceptTouchEvent 返回值 | onTouchEvent 返回值 | ||
TouchEventActivity | super.dispatchTouchEvent(ev) | --- | super.onTouchEvent(ev) | ||
TouchEventFather | super.dispatchTouchEvent(ev) | false | super.onTouchEvent(ev) | ||
TouchEventChilds | true | super.onInterceptTouchEvent(ev) | super.onTouchEvent(ev) | ||
运行结果 | |||||
Level | Time | PID | Application | Tag | Text |
W | 05-10 08:11:18.403 | 574 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_DOWN |
E | 05-10 08:11:18.403 | 574 | cn.sunzn.tevent | sunzn | TouchEventFather | dispatchTouchEvent --> ACTION_DOWN |
I | 05-10 08:11:18.403 | 574 | cn.sunzn.tevent | sunzn | TouchEventFather | onInterceptTouchEvent --> ACTION_DOWN |
E | 05-10 08:11:18.403 | 574 | cn.sunzn.tevent | sunzn | TouchEventChilds | dispatchTouchEvent --> ACTION_DOWN |
W | 05-10 08:11:19.652 | 574 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_MOVE |
E | 05-10 08:11:19.652 | 574 | cn.sunzn.tevent | sunzn | TouchEventFather | dispatchTouchEvent --> ACTION_MOVE |
I | 05-10 08:11:19.652 | 574 | cn.sunzn.tevent | sunzn | TouchEventFather | onInterceptTouchEvent --> ACTION_MOVE |
E | 05-10 08:11:19.652 | 574 | cn.sunzn.tevent | sunzn | TouchEventChilds | dispatchTouchEvent --> ACTION_MOVE |
W | 05-10 08:11:19.872 | 574 | cn.sunzn.tevent | sunzn | TouchEventActivity | dispatchTouchEvent --> ACTION_UP |
E | 05-10 08:11:19.872 | 574 | cn.sunzn.tevent | sunzn | TouchEventFather | dispatchTouchEvent --> ACTION_UP |
I | 05-10 08:11:19.872 | 574 | cn.sunzn.tevent | sunzn | TouchEventFather | onInterceptTouchEvent --> ACTION_UP |
E | 05-10 08:11:19.872 | 574 | cn.sunzn.tevent | sunzn | TouchEventChilds | dispatchTouchEvent --> ACTION_UP |
结果分析 | |||||
代码运行后,事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分发给 TouchEventFather 控件的 dispatchTouchEvent,该控件的 dispatchTouchEvent 返回super.dispatchTouchEvent(ev),事件会分发到 TouchEventFather 的 onInterceptTouchEvent,onInterceptTouchEvent 返回 false 表示放行当先事件;事件会被传递到子控件 TouchEventChilds 的 dispatchTouchEvent 方法,dispatchTouchEvent 返回 true 表示事件被分发到 TouchEventChilds 控件并由该控件的 dispatchTouchEvent 方法消费。后续的事件也会不断的重复上面的逻辑最终被 TouchEventChilds 的 dispatchTouchEvent 消费。 |