触摸事件分发机制详解

1.触摸事件的相关方法:

ViewGroup:

•dispatchTouchEvent(MotionEvent) 用于分发touch事件

•onInterceptTouchEvent(MotionEvent) 用于是否中断touch事件

•onTouchEvent(MotionEvent) 用于处理touch事件

View、Activity(没有事件中断的方法)

•dispatchTouchEvent(MotionEvent)

•onTouchEvent(MotionEvent)

2.过程详解:
为了模拟事件分发机制,先模拟布局:
见下图:
这里写图片描述
其中黑色和粉色界面都是ViewGroup.

众所周知:
触摸事件最先是由Activity获得,然后是ViewGroupA、ViewGroupB、ViewC传递。具体的传递过程如下:

(1)当触摸View并滑动的时候(继承父类方法,不拦截,不消费)
这里写图片描述

图解分析说明:
这里写图片描述
a. 子类的dispatch只能由父类的dispatch调用。

b. ViewGroup在调用到dispatch之后,先会调用onIntercept方法,返回值为false,才会调用下一级的dispatch。

当down 的时候,activity的dispatch—–>ViewGroupA的dispatch—–>A的Intercept(内部询问是否拦截,默认false不拦截)—–>返回到A的dispatch—–>B的dispatch—–>B的Intercept(内部询问是否拦截,false)—->返回到B的dispatch—–>View C 的dispatch—->C的touch事件(询问是否消费,默认false不消费)—->C的dispatch—->B的dispatch—>B的touch(询问是否消费,默认false不消费)—–>B的dispatch—–>A的dispatch—->A的touch(询问是否消费,默认false不消费)—–>A的dispatch—–>activity的dispatch—->activity的touch(不消费)—->activity的dispatch

当move的时候:
这里写图片描述
事件不再往下传递:而是由activity直接接收和处理:
activity的dispatch—–>activity的touch(询问是否消费,默认false不消费)—–>activity的dispatch.
原因是: 在action down的时候,touch事件返回的都是false.所以在以后的事件中,ViewGroup和View都没有资格再去消费了。所以只由activity处理。

同理:当UP的时候,事件传递也是和move的一样:
这里写图片描述

总结1:消费了Down事件的View称为目标View,目标View可接收后续事件(非目标View不接收后续事件)

总结2:Down事件如果没有任何View消费,则后续事件不再传递,直接由Activity的onTouchEvent方法处理,所以,想要处理其它事件首先要消费Down事件,也就是在接收到Down事件的时候返回true。

(2)当有拦截事件的时候:

a.假定ViewGroupA在down的时候拦截,但是却不消费事件,其余条件不改变
这里写图片描述

down的时候:activity的dispatch—->ViewGroupA的dispatch—–>A的Intercept(true, 拦截)——>A的touch(false)—–>A的dispatch—->activity的dispatch—–>activity的touch(false)—>activity的dispatch

move或是UP的时候:activity的dispatch—>activity的touch—>activity的dispatch

原因是:down事件没有被消费,所以viewgroup和veiw都没有消费后续事件的资格了。事件被丢失了,后续事件由Activity的onTouchEvent方法处理:

b.假定ViewGroupA在move的时候拦截,但是却不消费事件. 其余条件不改变 :

这里写图片描述

原因是:由于down事件没有被做任何改变,所以down事件还是依次向下传递。down事件没有被消费,所以viewgroup和veiw都没有消费后续事件的资格了,后续的事件传递给activity了。

c. 假定ViewGroupA在move的时候拦截,但是却不消费事件.View C在down的时候消费事件:则:
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

原因:在down事件的时候,没有被拦截,并且View c 在此处消费了事件,则View c 就是目标View了。而在move的时候,事件被拦截,所以传递给目标View的是Cancel事件,后续的事件就会交给自己的onTouchEvent方法处理。此时拦截了事件的View变成了目标View,能接收后续事件,虽然它没有消费Down事件。
这里的ViewGroupA为什么会被转化为目标View呢?可以这样理解:因为有down事件被消费过,所以外界再次传递事件的时候,会去找这个目标View,但是实际上事件被拦截,不能传递到真正的目标View,这时做了拦截的那个View就被充当目标View了。

d. 如果都没有出现拦截事件,而View C 在action down的时候消费了事件,则View c就会被当作目标View, 事件分发返回后,不会把事件交给非目标View的onTouchEvent,即使目标View在处理(Move、Up)事件时返回false。
这里写图片描述
这里写图片描述
这里写图片描述

总结3:只要子类没有消费touch事件,在分发调用返回时,activity都会去调用自己的touch事件,不管它自己有没有能力消费。

(3)其它情况:
a.如果调用getParent().requestDisallowInterceptTouchEvent(true)方法请求父View禁止拦截事件,这个方法会递归的请求所有的父View禁止拦截事件。如果在Down事件的时候只是请求父View禁止拦截并不消费Down事件,虽然父View不再拦截了,但后续事件也接收不到了(这个拦不拦截没有多大的意义,因为没有消费down事件,所以并不是目标view了,事件也就传递不到它了)。

总结4:有两个View并没有包含关系,但是有重叠,则上面的ViewA先拿到事件,如果消费了,则事件不会传给另一个ViewB。而当A也不消费的时候,事件返回到调用处,接着再传递给ViewB

总结5:
a.父类的onTouchEvent方法要想执行,要么是等所有的子View都不消费Down事件,要么是父View把事件拦截。
b.如果子类消费了Down事件,而父View又想处理这个事件,则父View可以在dispatchTouchEvent方法处理touch事件
c.如果子View请求了禁止父View拦截,且父View还想要拦截的话,可在父View的dispatchTouchEvent方法中不调用super.dispatchTouchEvent则把事件拦截了

3.应用举例:
当ViewPager嵌套ViewPager的时候:比如说整个app架构是由viewpager+fragment组成的,某单一fragment 页面也用到了viewpager。 在低版本的时候,滑动单一页面的viewpager会引起整个页面也跟着滑动,这是不合理的。此时,在单一页面的viewpager中调用requestDisallowInterceptTouchEvent方法. 可以解决此问题。不过,在高版本中已经解决了此类问题。所以ViewPager嵌套ViewPage不会出现不合理情况发生了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值