近期有一个项目有联系到重写View的,并且对View的触摸事件的控制有很高的要求,以前对onTouchEvent()方法还是有一点了解的,但是对一个View树之中的传递机制不是很了解。今天就来写个test程序来探究一下。
首先来概述一下onTouchEvent()这个方法的作用
SDK这样介绍:
public boolean onTouchEvent (MotionEvent event)
Since:
API Level 1
Implement this method to handle touch screen motion events.
Parameters
event | The motion event. |
---|
Returns
- True if the event was handled, false otherwise.
我们可以很清楚理解他的含义,我们通常通过诸如
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
……//控制代码
break;
case MotionEvent.ACTION_MOVE:
……//控制代码
break;
case MotionEvent.ACTION_UP:
……//控制代码
break;
}
{
case MotionEvent.ACTION_DOWN:
……//控制代码
break;
case MotionEvent.ACTION_MOVE:
……//控制代码
break;
case MotionEvent.ACTION_UP:
……//控制代码
break;
}
之类的语句来控制对触摸的事件的响应。
值得注意的是返回值的作用 sdk 的含义是 返回“true”意味着你接手(handled)了这个“event”的控制,并且这个“event”不会向其他对象传递,而是自己“消化”掉。这个我们可以通过代码来验证。有一个很关键的问题我没有说,就是这个“event”来自于哪里,是直接来自于事件最开始的分配(个人认为android系统中有个一个控制触摸事件的机制,他控制了事件的第一次的发送)呢?还是来自于其他的对象(父结点或者子结点)的传递呢?于是我做了一个实验,这里先把结果告诉大家,事件由最上层的View(子节点)开始(第一次)接受,并将触摸事件逐层向父节点传递(除非某一个节点消化了这个事件)。
下面贴一下代码:
布局文件:main.xml
<?xml version="1.0" encoding="utf-8"?>
<com.yp.touchtest.Layout_one xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<com.yp.touchtest.Layout_two
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<com.yp.touchtest.View_one
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
</com.yp.touchtest.View_one>
</com.yp.touchtest.Layout_two>
</com.yp.touchtest.Layout_one>
其中 com.yp.touchtest.Layout_one和
com.yp.touchtest.Layout_two是重写的两个LinearLayout, com.yp.touchtest.View_one 重写了View.
Avtivity类:TouchTestActivity.class
public class TouchTestActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
Layout_one Layout_two View_one 均只重写了 onTouchEvent(MotionEvent event) 方法
首先说明一下,所有情况 采用的触摸的手势均为 “按下” -->"滑动" -->"抬起" 共3步。
情况一:3个对象均不消化触摸事件,即3个对象的onTouchEvent 不论什么情况都返回“false”。
View_one Layout_one 和 Layout_two 均重写为
@Override
public boolean onTouchEvent(MotionEvent event)
{
String name ="onTouchEvent ";
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
System.out.println(Tag+name+"ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
System.out.println(Tag+name+"ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
System.out.println(Tag+name+"ACTION_UP");
break;
}
return false;
}
其中System.out.println(Tag+name+"ACTION_DOWN"); 打印语句中 Tag 为该类的类名(比如“ View_one”),
name为方法名,此时3个类都一样为“onTouchEvent” 。
运行之后的结果为:
04-24 17:08:37.296: I/System.out(23225): View_one: onTouchEvent ACTION_DOWN
04-24 17:08:37.296: I/System.out(23225): layout_two: onTouchEvent ACTION_DOWN
04-24 17:08:37.296: I/System.out(23225): layout_one: onTouchEvent ACTION_DOWN
04-24 17:08:37.296: I/System.out(23225): layout_two: onTouchEvent ACTION_DOWN
04-24 17:08:37.296: I/System.out(23225): layout_one: onTouchEvent ACTION_DOWN
分析:我们可以清楚的看到 触摸事件的传递顺序为子节点-->父节点。让人惊讶的是,3个对象都没有接受到 move和up事件。原因的大家可以想一下,先向下看,答案自然揭晓。
情况二:基于情况一的代码, 把View_out类中的 的ACTION_DOWN事件 中返回“true”。
case MotionEvent.ACTION_DOWN:
System.out.println(Tag+name+"ACTION_DOWN");
return true;
04-24 17:18:17.921: I/System.out(23323): View_one: onTouchEvent ACTION_DOWN
04-24 17:18:17.944: I/System.out(23323): View_one: onTouchEvent ACTION_MOVE
04-24 17:18:18.030: I/System.out(23323): View_one: onTouchEvent ACTION_MOVE
……好多ACTION_MOVE
04-24 17:18:18.061: I/System.out(23323): View_one: onTouchEvent ACTION_MOVE
04-24 17:18:18.077: I/System.out(23323): View_one: onTouchEvent ACTION_MOVE
04-24 17:18:18.108: I/System.out(23323): View_one: onTouchEvent ACTION_UP
04-24 17:18:17.944: I/System.out(23323): View_one: onTouchEvent ACTION_MOVE
04-24 17:18:18.030: I/System.out(23323): View_one: onTouchEvent ACTION_MOVE
……好多ACTION_MOVE
04-24 17:18:18.061: I/System.out(23323): View_one: onTouchEvent ACTION_MOVE
04-24 17:18:18.077: I/System.out(23323): View_one: onTouchEvent ACTION_MOVE
04-24 17:18:18.108: I/System.out(23323): View_one: onTouchEvent ACTION_UP
分析: ACTION_DOWN 中返回了true 但是ACTION_DOWN都返回了false . 让人惊讶的是 ACTION_DOWN ACTION_MOVE ACTION_UP 3个事件都由View_one处理了。结合情况一,我们可以知道只要有一个对象接手(handled)了这个“event“的ACTION_DOWN事件 就会处理完这个事件,并且连同ACTION_MOVEACTION_UP 都”消化“掉。
情况三: 也是给予情况一的代码,把Layout_two的ACTION_MOVE事件返回“true”
运行之后的结果为:
04-24 18:21:41.249: I/System.out(23605): View_one: onTouchEvent ACTION_DOWN
04-24 18:21:41.249: I/System.out(23605): layout_two: onTouchEvent ACTION_DOWN
04-24 18:21:41.273: I/System.out(23605): layout_two: onTouchEvent ACTION_MOVE
04-24 18:21:41.303: I/System.out(23605): layout_two: onTouchEvent ACTION_MOVE
……好多ACTION_MOVE,省略了
04-24 18:21:41.405: I/System.out(23605): layout_two: onTouchEvent ACTION_MOVE
04-24 18:21:41.444: I/System.out(23605): layout_two: onTouchEvent ACTION_UP
04-24 18:21:41.249: I/System.out(23605): layout_two: onTouchEvent ACTION_DOWN
04-24 18:21:41.273: I/System.out(23605): layout_two: onTouchEvent ACTION_MOVE
04-24 18:21:41.303: I/System.out(23605): layout_two: onTouchEvent ACTION_MOVE
……好多ACTION_MOVE,省略了
04-24 18:21:41.405: I/System.out(23605): layout_two: onTouchEvent ACTION_MOVE
04-24 18:21:41.444: I/System.out(23605): layout_two: onTouchEvent ACTION_UP
分析:这个情况更加印证了情况二分析的结论,这个情况中 Layout_one 处理并 “消化”了这个3事件 并且没有传递给他的父节点。
总结:
重写onTouchEvent() 这个方法,我们可以对用户的触摸事件进行处理。
触摸事件的传递方向为从子节点传向他的父节点,直到有一个结点返回了true
ACTION_DOWN ACTION_MOVE ACTION_UP 此时可以看作一个整体(其实不是)只要对ACTION_DOWN处理时返回了 true 就会把3个事件都处理并“消化”,不传递给他的父节点。
以后我会对和onTouchEvent(MotionEvent event)一个比较相关的方法 onInterceptTouchEvent(MotionEvent ev)做一写相关的实验。