Android的Touch事件处理机制

时间:2022-07-12 17:46:48

转载请注明出处:http://blog.csdn.net/gutaocslg/article/details/20044361

由于之前工作一直很忙,没有时间来写博客,正好利用找工作间隙,来写这篇文章。文章的内容主要是分析事件的分发机制。在日常的开发工作中和看别人的博客(特别是郭霖大神)中遇到事件处理的问题。觉得应该加上自己的理解写一篇关于事件处理的文章,希望能够给一些人提供帮助。并且希望以后能够有时间来写一些文章,给自己的android学习提供一个记载。另外在此希望各位大神给予批评意见!

下面进入正题:在android的开发过程中我们会遇到点击控件(可点击和不可点击)及Linearlayout这一类(ViewGroup)这其中就会接触到dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent这三个重要的方法。
那这些方法是什么作用了?
dispatchTouchEvent 事件分发
ViewGroup(YES)View(YES) Activity(YES)
onInterceptTouchEvent 事件拦截
ViewGroup(YES)View(YES)Activity(NO)
onTouchEvent事件响应
ViewGroup(YES)View(YES)Activity(YES)
从上面我们可以看到ViewGroup和View对与Touch事件相关的三个方法均能响应,而Activity对onInterceptTouchEvent(MotionEvent ev)也是事件拦截不进行响应.
另外需要注意的是View对dispatchTouchEvent(MotionEvent ev)和onInterceptTouchEvent(MotionEvent ev)
的响应的前提是可以向该View中添加子View,如果当前的View已经是一个最小的单元View(比如TextView,button),那么就无法向这个最小View中添加子View也就无法向子View进行事件的分发和拦截,所以它没有dispatchTouchEvent(MotionEventev)和onInterceptTouchEvent(MotionEvent ev),只有onTouchEvent(MotionEvent ev).
下面就结合实例和源码进行分析:
备注(上面分析时原因可能说的不太全,你可以也看下面的总结然后看过程可能更好理解一点)
Touch事件发生时Activity的dispatchTouchEvent(MotionEvent ev)方法会以隧道方式将事件传递给最外层View的dispatchTouchEvent(MotionEvent ev) 方法,并由该 View 的 并由该View 的 dispatchTouchEvent(MotionEvent ev) 方法对事件进行分发。但是在activity中重写dispatchTouchEvent(MotionEvent ev) 时,当你不调用super时,无论你点击那个地方,只要返回结果是false还是true,所有的事件都会被拦截!不会传递。
02-27 13:23:51.600: I/System.out(23353): action_down--ACTIVITY--DISPATCH
02-27 13:23:51.616: I/System.out(23353): action_move--ACTIVITY--DISPATCH
02-27 13:23:51.632: I/System.out(23353): action_move--ACTIVITY--DISPATCH
02-27 13:23:51.686: I/System.out(23353): action_move--ACTIVITY--DISPATCH
02-27 13:23:51.710: I/System.out(23353): action_up--ACTIVITY--DISPATCH

而且也没有办法调用到activity的onTouchEvent(MotionEvent ev)。这样的情况下一般我们不会在activity中重写而做一些操作。

Android的Touch事件处理机制

那我们直接运用activity默认的这个方法下面事件是怎么处理的了,为了清楚的看见效果,我分别继承Linearlayout创建一个新的ALinearLayout,和继承framelayout创建一个新的BF,并且实现它们的那三个方法:

public class ALinearLayout extends LinearLayout {

public ALinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}


@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
System.out.println(MainActivity.getAction(ev)+"--AL--INTERCEPT");

return super.onInterceptTouchEvent(ev);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
System.out.println(MainActivity.getAction(event)+"--AL--ONTOUCH");
return super.onTouchEvent(event);

}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
System.out.println(MainActivity.getAction(ev)+"--AL--DISPATCH");
return super.dispatchTouchEvent(ev);

}


}
public class BF extends FrameLayout {public BF(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {return super.onInterceptTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) {System.out.println(MainActivity.getAction(event)+"--BF-ONTOUCH");return super.onTouchEvent(event);}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {System.out.println(MainActivity.getAction(ev)+"--BF--DISPATCH");return super.dispatchTouchEvent(ev);}}

layout_main布局如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<com.gutao.activity.ALinearLayout
android:layout_width="match_parent"
android:layout_height="400dip"
android:background="#0000FF" >
<com.gutao.activity.BF
android:layout_width="match_parent"
android:layout_height="300dip"
android:background="#CCFF00" >
<Button
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.gutao.activity.BF>
</com.gutao.activity.ALinearLayout>
</RelativeLayout>
此时都以默认的情况下点击button按钮时
02-27 13:36:17.585: I/System.out(23739): action_down--ACTIVITY--DISPATCH
02-27 13:36:17.585: I/System.out(23739): action_down--AL--DISPATCH
02-27 13:36:17.585: I/System.out(23739): action_down--AL--INTERCEPT
02-27 13:36:17.593: I/System.out(23739): action_down--BF--DISPATCH
02-27 13:36:17.593: I/System.out(23739): class android.widget.Button
02-27 13:36:17.655: I/System.out(23739): action_move--ACTIVITY--DISPATCH
02-27 13:36:17.655: I/System.out(23739): action_move--AL--DISPATCH
02-27 13:36:17.655: I/System.out(23739): action_move--AL--INTERCEPT
02-27 13:36:17.655: I/System.out(23739): action_move--BF--DISPATCH
02-27 13:36:17.655: I/System.out(23739): class android.widget.Button
02-27 13:36:17.655: I/System.out(23739): action_move--ACTIVITY--DISPATCH
02-27 13:36:17.655: I/System.out(23739): action_move--AL--DISPATCH
02-27 13:36:17.655: I/System.out(23739): action_move--AL--INTERCEPT
02-27 13:36:17.663: I/System.out(23739): action_move--BF--DISPATCH
02-27 13:36:17.663: I/System.out(23739): class android.widget.Button
02-27 13:36:17.674: I/System.out(23739): action_up--ACTIVITY--DISPATCH
02-27 13:36:17.674: I/System.out(23739): action_up--AL--DISPATCH
02-27 13:36:17.674: I/System.out(23739): action_up--AL--INTERCEPT
02-27 13:36:17.674: I/System.out(23739): action_up--BF--DISPATCH
02-27 13:36:17.674: I/System.out(23739): class android.widget.Button
02-27 13:36:17.678: I/System.out(23739): btn+onClick
这是正常的情况。当点击白色区域时,
02-27 13:37:46.764: I/System.out(23739): action_down--ACTIVITY--DISPATCH
02-27 13:37:46.764: I/System.out(23739): action_down--ACTIVITY--ONTOUCH
02-27 13:37:46.780: I/System.out(23739): action_move--ACTIVITY--DISPATCH
02-27 13:37:46.780: I/System.out(23739): action_move--ACTIVITY--ONTOUCH
02-27 13:37:46.831: I/System.out(23739): action_move--ACTIVITY--DISPATCH
02-27 13:37:46.835: I/System.out(23739): action_move--ACTIVITY--ONTOUCH
02-27 13:37:46.853: I/System.out(23739): action_up--ACTIVITY--DISPATCH
02-27 13:37:46.858: I/System.out(23739): action_up--ACTIVITY--ONTOUCH
打印结果所示
点击蓝色区域:
02-27 13:40:21.813: I/System.out(23739): action_down--ACTIVITY--DISPATCH
02-27 13:40:21.813: I/System.out(23739): action_down--AL--DISPATCH
02-27 13:40:21.813: I/System.out(23739): action_down--AL--INTERCEPT
02-27 13:40:21.813: I/System.out(23739): action_down--AL--ONTOUCH
02-27 13:40:21.813: I/System.out(23739): action_down--ACTIVITY--ONTOUCH
02-27 13:40:21.835: I/System.out(23739): action_move--ACTIVITY--DISPATCH
02-27 13:40:21.835: I/System.out(23739): action_move--ACTIVITY--ONTOUCH
02-27 13:40:21.889: I/System.out(23739): action_up--ACTIVITY--DISPATCH
02-27 13:40:21.889: I/System.out(23739): action_up--ACTIVITY--ONTOUCH
点击黄色区域:
02-27 13:40:57.830: I/System.out(23739): action_down--ACTIVITY--DISPATCH
02-27 13:40:57.830: I/System.out(23739): action_down--AL--DISPATCH
02-27 13:40:57.830: I/System.out(23739): action_down--AL--INTERCEPT
02-27 13:40:57.830: I/System.out(23739): action_down--BF--DISPATCH
02-27 13:40:57.830: I/System.out(23739): action_down--BF-ONTOUCH
02-27 13:40:57.830: I/System.out(23739): action_down--AL--ONTOUCH
02-27 13:40:57.830: I/System.out(23739): action_down--ACTIVITY--ONTOUCH
02-27 13:40:57.881: I/System.out(23739): action_move--ACTIVITY--DISPATCH
02-27 13:40:57.881: I/System.out(23739): action_move--ACTIVITY--ONTOUCH
02-27 13:40:57.905: I/System.out(23739): action_up--ACTIVITY--DISPATCH
02-27 13:40:57.905: I/System.out(23739): action_up--ACTIVITY--ONTOUCH
上面可能有人有个疑问除了button,为什么down对应的区域点击可以传递到每一层次,而move和up不可以了 有下面两个原因:
一:
/**
* Called to process touch screen events. You can override this to
* intercept all touch screen events before they are dispatched to the
* window. Be sure to call this implementation for touch screen events
* that should be handled normally.
*
* @param ev The touch screen event.
*
* @return boolean Return true if this event was consumed.
*/
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}

button是可点击,所以moveup会得到执行,这个可以看一下郭大神的博客http://blog.csdn.net/guolin_blog/article/details/9097463解释很清楚

下面我要进行默认值的修改了:
先将BF中dispatch这个方法改为return true 点击每一个会怎么样了?
其中点击Button和黄色区域有了变化
button点击:
02-27 13:47:25.405: I/System.out(23856): action_down--ACTIVITY--DISPATCH
02-27 13:47:25.405: I/System.out(23856): action_down--AL--DISPATCH
02-27 13:47:25.405: I/System.out(23856): action_down--AL--INTERCEPT
02-27 13:47:25.405: I/System.out(23856): action_down--BF--DISPATCH
02-27 13:47:25.422: I/System.out(23856): action_move--ACTIVITY--DISPATCH
02-27 13:47:25.422: I/System.out(23856): action_move--AL--DISPATCH
02-27 13:47:25.422: I/System.out(23856): action_move--AL--INTERCEPT
02-27 13:47:25.428: I/System.out(23856): action_move--BF--DISPATCH
02-27 13:47:25.475: I/System.out(23856): action_move--ACTIVITY--DISPATCH
02-27 13:47:25.475: I/System.out(23856): action_move--AL--DISPATCH
02-27 13:47:25.483: I/System.out(23856): action_move--AL--INTERCEPT
02-27 13:47:25.483: I/System.out(23856): action_move--BF--DISPATCH
02-27 13:47:25.491: I/System.out(23856): action_move--ACTIVITY--DISPATCH
02-27 13:47:25.499: I/System.out(23856): action_move--AL--DISPATCH
02-27 13:47:25.499: I/System.out(23856): action_move--AL--INTERCEPT
02-27 13:47:25.507: I/System.out(23856): action_move--BF--DISPATCH
02-27 13:47:25.514: I/System.out(23856): action_up--ACTIVITY--DISPATCH
02-27 13:47:25.514: I/System.out(23856): action_up--AL--DISPATCH
02-27 13:47:25.522: I/System.out(23856): action_up--AL--INTERCEPT
02-27 13:47:25.522: I/System.out(23856): action_up--BF--DISPATCH
黄色区域:
02-27 13:46:29.862: I/System.out(23856): action_down--ACTIVITY--DISPATCH
02-27 13:46:29.862: I/System.out(23856): action_down--AL--DISPATCH
02-27 13:46:29.862: I/System.out(23856): action_down--AL--INTERCEPT
02-27 13:46:29.862: I/System.out(23856): action_down--BF--DISPATCH
02-27 13:46:29.914: I/System.out(23856): action_move--ACTIVITY--DISPATCH
02-27 13:46:29.914: I/System.out(23856): action_move--AL--DISPATCH
02-27 13:46:29.914: I/System.out(23856): action_move--AL--INTERCEPT
02-27 13:46:29.914: I/System.out(23856): action_move--BF--DISPATCH
02-27 13:46:29.936: I/System.out(23856): action_up--ACTIVITY--DISPATCH
02-27 13:46:29.936: I/System.out(23856): action_up--AL--DISPATCH
02-27 13:46:29.936: I/System.out(23856): action_up--AL--INTERCEPT
02-27 13:46:29.936: I/System.out(23856): action_up--BF--DISPATCH
此时由于BFdispatch返回了truedispatchTouchEvent 方法进行消费,同时事件会停止向下传递;
当返回false了?
点击白色:
02-27 13:53:32.421: I/System.out(23982): action_down--ACTIVITY--DISPATCH
02-27 13:53:32.421: I/System.out(23982): action_down--ACTIVITY--ONTOUCH
02-27 13:53:32.452: I/System.out(23982): action_move--ACTIVITY--DISPATCH
02-27 13:53:32.452: I/System.out(23982): action_move--ACTIVITY--ONTOUCH
02-27 13:53:32.475: I/System.out(23982): action_up--ACTIVITY--DISPATCH
02-27 13:53:32.475: I/System.out(23982): action_up--ACTIVITY--ONTOUCH
蓝色区域:
02-27 13:54:34.272: I/System.out(23982): action_down--ACTIVITY--DISPATCH
02-27 13:54:34.272: I/System.out(23982): action_down--AL--DISPATCH
02-27 13:54:34.272: I/System.out(23982): action_down--AL--INTERCEPT
02-27 13:54:34.272: I/System.out(23982): action_down--AL--ONTOUCH
02-27 13:54:34.272: I/System.out(23982): action_down--ACTIVITY--ONTOUCH
02-27 13:54:34.304: I/System.out(23982): action_move--ACTIVITY--DISPATCH
02-27 13:54:34.304: I/System.out(23982): action_move--ACTIVITY--ONTOUCH
02-27 13:54:34.321: I/System.out(23982): action_move--ACTIVITY--DISPATCH
02-27 13:54:34.321: I/System.out(23982): action_move--ACTIVITY--ONTOUCH
02-27 13:54:34.344: I/System.out(23982): action_up--ACTIVITY--DISPATCH
02-27 13:54:34.344: I/System.out(23982): action_up--ACTIVITY--ONTOUCH
由于ALinearLayout的dispatch返回默认事件会自动的分发给当前View的onInterceptTouchEvent方法。由于onInterceptTouchEvent返回默super.onInterceptTouchEvent(ev),事件默认会被拦截,并将拦截到的事件交由当前 View 的 onTouchEvent 进行处理。
点击黄色区域:
02-27 14:15:18.343: I/System.out(23982): action_down--ACTIVITY--DISPATCH
02-27 14:15:18.350: I/System.out(23982): action_down--AL--DISPATCH
02-27 14:15:18.350: I/System.out(23982): action_down--AL--INTERCEPT
02-27 14:15:18.350: I/System.out(23982): action_down--BF--DISPATCH
02-27 14:15:18.350: I/System.out(23982): action_down--AL--ONTOUCH
02-27 14:15:18.350: I/System.out(23982): action_down--ACTIVITY--ONTOUCH
02-27 14:15:18.398: I/System.out(23982): action_move--ACTIVITY--DISPATCH
02-27 14:15:18.398: I/System.out(23982): action_move--ACTIVITY--ONTOUCH
02-27 14:15:18.421: I/System.out(23982): action_up--ACTIVITY--DISPATCH
02-2714:15:18.421: I/System.out(23982): action_up--ACTIVITY--ONTOUCH

  如果返回了true则会接收并消费该事件.由于BF的dispatch 返回了false此时则会将事件返回给父View(ALinearlayout) 的 onTouchEvent 进行消费。
点击Button:
02-27 14:18:02.874: I/System.out(23982): action_down--ACTIVITY--DISPATCH
02-27 14:18:02.874: I/System.out(23982): action_down--AL--DISPATCH
02-27 14:18:02.874: I/System.out(23982): action_down--AL--INTERCEPT
02-27 14:18:02.874: I/System.out(23982): action_down--BF--DISPATCH
02-27 14:18:02.874: I/System.out(23982): action_down--AL--ONTOUCH
02-27 14:18:02.874: I/System.out(23982): action_down--ACTIVITY--ONTOUCH
02-27 14:18:02.889: I/System.out(23982): action_move--ACTIVITY--DISPATCH
02-27 14:18:02.889: I/System.out(23982): action_move--ACTIVITY--ONTOUCH
02-27 14:18:02.906: I/System.out(23982): action_move--ACTIVITY--DISPATCH
02-27 14:18:02.906: I/System.out(23982): action_move--ACTIVITY--ONTOUCH
02-27 14:18:02.983: I/System.out(23982): action_up--ACTIVITY--DISPATCH
02-27 14:18:02.983: I/System.out(23982): action_up--ACTIVITY--ONTOUCH

经过以上的分析可以总结为一下这样:


事件分发:public boolean dispatchTouchEvent(MotionEvent ev)
  Touch事件发生时Activity的dispatchTouchEvent(MotionEvent ev)方法会以隧道方式(从根元素依次往下传递直到最内层子元素或在中间某一元素中由于某一条件停止传递)将事件传递给最外层View的 dispatchTouchEvent(MotionEvent ev)方法并由该View的dispatchTouchEvent(MotionEvent ev)方法对事件进行分发dispatchTouchEvent的事件分发逻辑如下:
  如果return true事件会分发给当前View并由dispatchTouchEvent方法进行消费同时事件会停止向下传递
  如果 return false事件分发分为两种情况:
  如果当前View获取的事件直接来自Activity则会将事件返回给Activity的onTouchEvent进行消费。
  如果当前View获取的事件来自外层父控件则会将事件返回给父View的onTouchEvent进行消费。
  如果返回系统默认的super.dispatchTouchEvent(ev)事件会自动的分发给当前View的onInterceptTouchEvent方法。


事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev)
  在外层View的dispatchTouchEvent(ev)方法返回系统默认的super.dispatchTouchEvent(ev)情况下,事件会自动的分发给当前View的onInterceptTouchEvent方法。onInterceptTouchEvent的事件拦截逻辑如下:
  如果onInterceptTouchEvent返回true,super.onInterceptTouchEvent(ev)则表示将事件进行拦截,并将拦截到的事件交由当前View的onTouchEvent进行处理.
  如果onInterceptTouchEvent返回false则表示将事件放行当前View上的事件会被传递到子View上,再由子View的dispatchTouchEvent来开始这个事件的分发.


事件响应:public boolean onTouchEvent(MotionEvent ev)
  在dispatchTouchEvent返回super.dispatchTouchEvent(ev)并且onInterceptTouchEvent返回true或super.onInterceptTouchEvent(ev)的情况下onTouchEvent会被调用onTouchEvent的事件响应逻辑如下:
  如果事件传递到当前View的onTouchEvent方法而该方法返回了false或super.onTouchEvent(ev)那么这个事件会从当前View向上传递并且都是由上层View的onTouchEvent来接收,如果传递到上面的onTouchEvent也返回false,这个事件就会“消失”,而且接收不到下一次事件.

如果返回true则会接收并消费该事件.


可以先看郭大神的事件分发然后看我这个可能会好一点啊!

      以上是自己的个人分析,欢迎大家批评指正!