Android Touch事件的处理流程

作为触屏手机,触摸事件是最基本的事件,没有之一。在实际的触屏事件响应的过程中,public boolean onInterceptTouchEvent(MotionEvent ev)和public boolean onTouchEvent(MotionEvent event)是最重要的两个函数。下面将通过代码来展示一次触摸事件的处理流程,一次完整的触摸包括DOWN,MOVE,UP

    首先,先说下public boolean onInterceptTouchEvent(MotionEvent ev)这个函数,官方文档就不贴在这了,因为即使是官方文档其说明也是很复杂的,这里先只说总结性的一句话,onInterceptTouchEvent本身不会消耗任何触摸事件,只能影响touch事件的处理顺序,touch事件在被onTouchEvent返回true表示消耗之前会一直传递下去当viewGroup收到触摸事件时,onInterceptTouchEvent会在onTouchEvent之前被调用,onInterceptTouchEvent的返回值决定了触摸事件是否立即被viewGroup本身的onTouchEvent响应。具体看下面。(相关代码已经上传http://www.oschina.net/code/snippet_272860_17818,有兴趣可以下载自己做实验研究)

    首先自定义三个layout,ALayout,BLayout,CLayout均继承自RelativeLayout。,其中在相关函数中打印出日志:

    ALayout

?
1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
     // TODO Auto-generated method stub
     Log.e( "sssssssssssssssss" , "InterceptTouchEvent  A " + Common.getAction(ev));
     return false ;
}
 
@Override
public boolean onTouchEvent(MotionEvent event) {
     // TODO Auto-generated method stub
     Log.e( "sssssssssssssssss" , "onTouchEvent A" + Common.getAction(event) );
     return false ;
}

    BLayout 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
     // TODO Auto-generated method stub
     Log.e( "sssssssssssssssss" , "InterceptTouchEvent  B " + Common.getAction(ev));
     return false ;
}
 
@Override
public boolean onTouchEvent(MotionEvent event) {
     // TODO Auto-generated method stub
     Log.e( "sssssssssssssssss" , "onTouchEvent B" + Common.getAction(event) );
     return false ;
}

  CLayout 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
     // TODO Auto-generated method stub
     Log.e( "sssssssssssssssss" , "InterceptTouchEvent  C " + Common.getAction(ev));
     return false ;
}
 
@Override
public boolean onTouchEvent(MotionEvent event) {
     // TODO Auto-generated method stub
     Log.e( "sssssssssssssssss" , "onTouchEvent C" + Common.getAction(event) );
     return false ;
}

下面的是布局文件:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
< 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"
     tools:context = ".MainActivity" >
 
     < com.example.testaaaa.ALayout
         android:id = "@+id/aa"
         android:layout_width = "200dip"
         android:layout_height = "200dip"
         android:layout_alignParentLeft = "true"
         android:layout_alignParentTop = "true"
         android:layout_marginLeft = "61dp"
         android:layout_marginTop = "68dp"
         android:background = "#ff0000ff" >
 
         < com.example.testaaaa.BLayout
             android:id = "@+id/bb"
             android:layout_width = "150dip"
             android:layout_height = "150dip"
             android:layout_centerHorizontal = "true"
             android:layout_centerVertical = "true"
             android:background = "#ffff0000" >
         </ com.example.testaaaa.BLayout >
     </ com.example.testaaaa.ALayout >
 
     < com.example.testaaaa.CLayout
         android:id = "@+id/cc"
         android:layout_width = "100dip"
         android:layout_height = "100dip"
         android:layout_centerHorizontal = "true"
         android:layout_centerVertical = "true"
         android:background = "#ff00ff00" >
     </ com.example.testaaaa.CLayout >
 
</ RelativeLayout >
具体就是这个样子:

结合xml中的布局可知,AB是层级或者父子关系,AC是平级或者兄弟关系。下面先点击“1”区域,打印出的日志如下:

整个流程是这样的:因为AB是层级关系,事件优先被下发到A的onInterceptTouchEvent中,因为其返回值为false,因此A的onTouchEvent不会被立即调用,然后触摸事件继续下发到B中,B的onInterceptTouchEvent被调用,因为其也返回false,触摸事件应该继续下发给B的子视图,但是B没有childView,于是B的onTouchEvent被调用,又因其返回false表明事件没有被消耗,于是A的onTouchEvent被调用。(PS:下面的所有的实验都是在ABC相关函数都是返回false的基础上修改,每次都并且只修改一个值)

实验1:同样点击“1”区域,A的onInterceptTouchEvent中返回true,此时B的onInterceptTouchEvent和onTouchEvent都不会被调用


实验2:同样点击“1”区域,A的onTouchEvent返回true


实验3:同样点击“1”区域,B的onTouchEvent返回true,此时A的onTouchEvent不会被调用。


    下面将ABC中onInterceptTouchEvent和onTouchEvent都返回false。这次点击“3”区域,日志如下:

这次的流程是这样的:AC为平级关系,因为C是盖在A上的,因此触摸事件先被下发到C中,C的onInterceptTouchEvent和onTouchEvent被调用,因为C中onTouchEvent返回false,因此触摸事件没有被消耗并继续被下发到A

实验4:同样点击“3”区域,但是C中onInterceptTouchEvent返回true,因为C没有childView因此不会有变化

实验6:同样点击“3”区域,但是C中onTouchEvent返回true,因为事件被C消耗,A中onInterceptTouchEvent和onTouchEvent不会被调用。

实验7:同样点击“3”区域,但是A中onTouchEvent返回true

下面将ABC中onInterceptTouchEvent和onTouchEvent都返回false。这次点击“2”区域,如果已经对touch事件的处理过程有了一定认识后,应该可以直接分析出吧。下图:


这个只是综合区域“1”和“3”的情形。

Touch事件的处理流程:

1.一旦某个ViewGroup获得了ACTION_DOWN的事件,会根据深度优先的算法遍历以ViewGroup为根节点的view树

2.如果点击的位置在被遍历到的childView区域中,childView是groupview的话其onInterceptTouchEvent将被调用,这个过程会一直进行下去

3.直到某个viewX的onInterceptTouchEvent返回了true,或者一直遍历到某个叶viewX,然后该viewX的onTouchEvent被调用

4.如果viewX的onTouchEvent返回false,则该viewX的父节点viewY的onTouchEvent被调用,这个过程会一直下去

5.如果直到根节点的onTouchEvent都返回false,那么后续的ACTION_MOVEACTION_UP将被这棵树里面的所有的view忽略

6.当某个viewX在onTouchEvent中返回true后,ACTION_MOVE和ACTION_UP会从根节点开始按照深度优先的算法依次调用onInterceptTouchEvent,但是和ACTION_DOWN不同,该过程只持续到viewX的父节点为止,然后viewX的onInterceptTouchEvent不被调用,而直接调用onTouchEvent(查看实验3的日志

7.如果在ACTION_MOVE或者ACTION_UP在被viewX的onTouchEvent处理之前,某个viewZ的onInterceptTouchEvent返回true,表明viewZ要处理接下来的触摸事件,那么在viewZ的onTouchEvent被调用之前,onInterceptTouchEvent调用还会继续下去,只不过之后的view的onInterceptTouchEvent中的ACTION变成了ACTION_CANCEL,直到viewX中的onTouchEvent处理了这个ACTION_CANCEL后,viewZ的onTouchEvent才被调用,后续事件将只被viewZ的onTouchEvent处理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值