`

Android事件分发onInterceptTouchEvent与onTouchEvent

 
阅读更多

最近在做一个项目,需要定制一个View,类似于Launcher中水平滚动效果。当初仿照Workspace类来实现。然而在针对一个子View响应Scroll事件时,这个子View死活都不响应触摸事件。分析了一天,最后给这个子View的属性中添加android:clickable="true", 问题就被搞定了。

Android平台事件分发的两个函数onInterceptTouchEvent与onTouchEvent,还是比较复杂的。关键点还是在于其返回值。现将网上的一些帖子借鉴,使用一个简单的demo,进行一些分析。
onInterceptTouchEvent()是ViewGroup的一个方法,目的是在系统向该ViewGroup及其各个childView触发onTouchEvent()之前对相关事件进行一次拦截.
1.down事件首先会传递到onInterceptTouchEvent()方法。
2.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return false,那么后续的move,up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标view的onTouchEvent()处理。
3.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理。并且,childView将接收不到任何事件。
4.如果最终需要处理事件的view的onTouchEvent()返回了false,表示事件没有被消耗,那么该事件将被传递至其上一层次的view的onTouchEvent()处理。
5.如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。
通过一个简单的demo,对上述几条信息进行逐一讲解。三个类自定义类分别继承于LinearLayout,ALayout BLayout CLayout(仅仅贴出ALayout类的代码,其他类的代码类似)
public class ALayout extends LinearLayout{
public ALayout(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
 
public ALayout(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
 
public ALayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
 
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
Log.e("ALayout","onInterceptTouchEvent ACTION_DOWN called,return = " + super.onInterceptTouchEvent(ev));
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
}
return super.onInterceptTouchEvent(ev);
}
 
@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
final int action = event.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
Log.e("ALayout","onTouchEvent ACTION_DOWN called,return = " + super.onTouchEvent(event));
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
}
return super.onTouchEvent(event);
}
 
}
 
布局文件main.xml如下:
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.touchtest.ALayout
android:id="@+id/view_a"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ffff0000"
android:gravity="center" >
<com.example.touchtest.BLayout
android:id="@+id/view_b"
android:layout_width="250dip"
android:layout_height="250dip"
android:background="#ff00ff00"
android:gravity="center" >
<com.example.touchtest.CLayout
android:id="@+id/view_c"
android:layout_width="150dip"
android:layout_height="150dip"
android:background="#ff0000ff"
android:gravity="center" >
</com.example.touchtest.CLayout>
</com.example.touchtest.BLayout>
</com.example.touchtest.ALayout>
</RelativeLayout>
程序运行后效果如下所示:

 

应用运行效果如左图所示。红色背景为ALayout,绿色为BLayout,蓝色为CLayout.点击蓝色(CLayout)区域时,查看log如下:

 

E/ALayout(10407): onInterceptTouchEvent ACTION_DOWN called,return = false

E/BLayout(10407): onInterceptTouchEvent ACTION_DOWN called,return = false

E/CLayout(10407): onInterceptTouchEvent ACTION_DOWN called,return = false

E/CLayout(10407): onTouchEvent ACTION_DOWN called,return = false

E/BLayout(10407): onTouchEvent ACTION_DOWN called,return = false

E/ALayout(10407): onTouchEvent ACTION_DOWN called,return = false

通过log得知,ACTION_DOWN事件最开始是由onInterceptTouchEvent函数进行处理。之后传递给onTouchEvent函数。这两个函数默认的返回值为false。由于A,B和C三个View均没有消耗掉这个ACTION_DOWN事件,则这个事件也就不了了之了。
现在修改一点内容,将BLayout的属性中添加一条android:clickable="true",即布局文件为:
<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" >
<com.example.touchtest.ALayout
android:id="@+id/view_a"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ffff0000"
android:gravity="center" >
<com.example.touchtest.BLayout
android:id="@+id/view_b"
android:layout_width="250dip"
android:layout_height="250dip"
android:clickable="true"
android:background="#ff00ff00"
android:gravity="center" >
<com.example.touchtest.CLayout
android:id="@+id/view_c"
android:layout_width="150dip"
android:layout_height="150dip"
android:background="#ff0000ff"
android:gravity="center" >
</com.example.touchtest.CLayout>
</com.example.touchtest.BLayout>
</com.example.touchtest.ALayout>
</RelativeLayout>
运行后,点击蓝色区域,显示的log信息为:
E/ALayout(10764): onInterceptTouchEvent ACTION_DOWN called,return = false
E/BLayout(10764): onInterceptTouchEvent ACTION_DOWN called,return = false
E/CLayout(10764): onInterceptTouchEvent ACTION_DOWN called,return = false
E/CLayout(10764): onTouchEvent ACTION_DOWN called,return = false
E/BLayout(10764): onTouchEvent ACTION_DOWN called,return = true
由于只有BLayout响应click事件,onTouchEvent函数捕获到DOWN事件后,消耗掉此事件。接下来的MOVE和UP事件均传递给BLayout了。由此可以得出结论:
onTouchEvent 中,返回值是 true ,则说明消耗掉了这个事件,返回值是 false ,则没有消耗掉,会继续传递下去。
接下来,做第三个测试。在CLayout上,添加一个Button。布局文件如下所示:
<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" >
<com.example.touchtest.ALayout
android:id="@+id/view_a"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ffff0000"
android:gravity="center" >
<com.example.touchtest.BLayout
android:id="@+id/view_b"
android:layout_width="250dip"
android:layout_height="250dip"
android:clickable="true"
android:background="#ff00ff00"
android:gravity="center" >
<com.example.touchtest.CLayout
android:id="@+id/view_c"
android:layout_width="150dip"
android:layout_height="150dip"
android:clickable="false"
android:background="#ff0000ff"
android:gravity="center" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
</com.example.touchtest.CLayout>
</com.example.touchtest.BLayout>
</com.example.touchtest.ALayout>
</RelativeLayout>
响应的Activity中添加Button响应事件处理
mBtn = (Button) findViewById(R.id.button1);
mBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
Log.e("","mBtn was click...");
}
});
运行后,效果如图所示


点击Button后,log信息如下

E/ALayout(10856):onInterceptTouchEvent ACTION_DOWN called,return = false

E/BLayout(10856): onInterceptTouchEvent ACTION_DOWN called,return = false

E/CLayout(10856): onInterceptTouchEvent ACTION_DOWN called,return = false

E/(10856): mBtn was click...

从log分析得知,Button处于最上层的一个View,有Btn截获点击事件,不在传递给B,C,A。

第三个测试,说明了"江湖流言"4,5两条

 

4.如果最终需要处理事件的view的onTouchEvent()返回了false,表示事件没有被消耗,那么该事件将被传递至其上一层次的view的onTouchEvent()处理。
5.如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。
接着做第四个测试。依旧使用上面的代码,但要在ALayout的View中添加一条属性。android:clickable="true"。并且ALayout中的onInterceptTouchEvent函数返回值为true。其修改后的代码如下所示:
ALayout.Java文件
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
Log.e("ALayout","onInterceptTouchEvent ACTION_DOWN called,return = true");
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
}
//return super.onInterceptTouchEvent(ev);
return true;
}
布局文件为:
<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" >
<com.example.touchtest.ALayout
android:id="@+id/view_a"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ffff0000"
android:clickable="true"
android:gravity="center" >
<com.example.touchtest.BLayout
android:id="@+id/view_b"
android:layout_width="250dip"
android:layout_height="250dip"
android:clickable="true"
android:background="#ff00ff00"
android:gravity="center" >
<com.example.touchtest.CLayout
android:id="@+id/view_c"
android:layout_width="150dip"
android:layout_height="150dip"
android:clickable="true"
android:background="#ff0000ff"
android:gravity="center" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
</com.example.touchtest.CLayout>
</com.example.touchtest.BLayout>
</com.example.touchtest.ALayout>
</RelativeLayout>
运行后,点击Button,log信息如下所示:
12-19 22:49:34.722: E/ALayout(11032): onInterceptTouchEvent ACTION_DOWN called,return = true
12-19 22:49:34.722: E/ALayout(11032): onTouchEvent ACTION_DOWN called,return = true
由第四个测试印证了"江湖流言"2,3两条。
2.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标view的onTouchEvent()处理。
3.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理。并且,childView将接收不到任何事件
PS:关于事件分发机制的深入研究,最好还是从源码着手。以上测试例子,仅仅从结果入手,并没有做到“知其所以然”。


原文:http://blog.csdn.net/coder80/article/details/8333904

分享到:
评论

相关推荐

    android事件分发机制测试demo

    安卓事件分发机制测试代码,事件传递从Activity--&gt;ViewGroup--&gt;View。dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent这三个函数的返回值不同,代表的事件传递的不同。

    安卓Android事件分发处理 scrollview套scrollview+scrollview.其中viewpager下套listview 带悬浮框效果

    安卓Android事件分发处理 ...主要处理事件分发 和事件消费dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent 3个方法对事件的拦截与否。10分我都觉得少,下不起就别用这么牛逼的自定义,让产品经理换需求。

    Android事件分发小结

    onInterceptTouchEvent方法我们返回了true,在这里就将事件拦截了,所以他不会继续分发给View(Button)了,反而交给自身的onTouchEvent方法执行了理所当然,最后执行的就是ViewGroup的点击事件了。

    Android 事件分发详解及示例代码

    Android中与事件分发相关的方法主要包括dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent三个方法,而事件分发一般会经过三种容器,分别为Activity、ViewGroup、View。下表对这三种容器分别拥有的事件分发...

    完全理解android事件分发机制

    但是源码的复杂往往会让新手产生畏惧难以理解,于是笔者最终还是打算使用实例log来让读者理解android事件分发。 #重要函数 笔者此次主要提及最常用的几个函数: (其间区别看源码很容易理解,此处直接给上结果) **...

    Android事件分发与消费

    事件分发和消费我们主要涉及到以下三个方法:dispatchTouchEvent():分发事件onInterceptTouchEvent():拦截事件onTouchEvent():处理事件还需要注意常用的两个接口对以上方法的影响:OnClickListener:点击事件监听...

    详解Android事件的分发、拦截和执行

    Android的触摸事件分发过程由三个很重要的方法来共同完成:dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent。我先将这三个方法大体的介绍一下。  •public boolean dispatchTouchEvent(MotionEvent ev)...

    Android 事件分发机制 讲解

    1、分发事件的组件 分发事件的组件,也称为分发事件者,包括Activity、ViewGroup和View。它们三者的一般结构为: 从上图中可以看出,Activity包括了ViewGroup,ViewGroup又可以包含多个View。 2、分发的核心方法 ...

    Android View 事件分发机制详解

    Android开发,触控无处不在。...说白了这些触控的事件分发机制就是弄清楚三个方法,dispatchTouchEvent(),OnInterceptTouchEvent(),onTouchEvent(),和这三个方法与n个ViewGroup和View堆叠在一起的问题,再复杂的

    Android View的事件分发机制

    一.Android View框架提供了3个对事件的主要操作概念。 1、事件的分发机制,dispatchTouchEvent。主要是parent根据触摸事件的产生位置,以及child是否愿意负责处理该系列事件等状态,向其child分发事件的机制。 2、...

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

    Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev);能够响应这些方法的控件包括:ViewGroup、View、Activity。...

    点击事件分发

    这个案例可以深度理解点击事件的分发理解,在log可以看到哪个方法拦截事件 @Override public boolean dispatchTouchEvent(MotionEvent ev) { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { ...

    安卓touch事件的分发和消费机制

    Android中与Touch事件相关的方法包括:dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev);能够响应这些方法的控件包括:ViewGroup、View、Activity。继承...

    Demo:我的演示和我的测试

    演示OnTouchListener、OnClickListener和OnLongClickListener的触发,2014-08-17Android触屏事件演示Demo,演示触屏事件在view的onInterceptTouchEvent和onTouchEvent方法中分发的逻辑,2013-09-12Android Studio...

    Android View的事件分发详解

     近两天学习了一下view的事件分发,把自己的理解总结了一遍,只表达了自己认为需要明白的地方,毕竟是菜鸟一枚,不对的地方还请大神们多指教! 2.三个方法 public boolean dispatchTouchEvent(MotionEvent ev) ...

    30分钟搞清楚Android Touch事件分发机制

    Touch事件分发中只有两个主角:...ViewGroup的相关事件有三个:onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent。View的相关事件只有两个:dispatchTouchEvent、onTouchEvent。 先分析ViewGroup的处理流程

    android中view手势滑动冲突的解决方法

    Android手势事件的冲突跟点击事件的分发过程息息相关,由三个重要的方法来共同完成,分别是:dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent。 public boolean dispatchTouchEvent(MotionEvent ev) 这个...

    Android触摸事件传递图解

     android TouchEvent相关事件有  1 dispatchTouchEvent 这个方法用来分发TouchEvent  2 onInterceptTouchEvent 这个方法用来拦截TouchEvent  3 onTouchEvent 方法用来处理TouchEvent 比较特殊一点的是...

    事件机制测试Demo

    开篇语:最近程序在做一个小效果,要用到touch,结果整得云里面雾里的,干脆就好好把android touch机制好好看了一下,呵呵。...3)public boolean onTouchEvent(MotionEvent ev) 这个方法用来处理TouchEvent

Global site tag (gtag.js) - Google Analytics