View的事件分发机制

事件分发机制,是指整个系统对于MotionEvent产生后,对MotionEvent由上自下地分发方式,说简单点,就是当一个点击事件发生后,视图层间要怎么来把它交给合适的视图去处理。

    主要由三个每个view都有的方法来分发:dispatchTouchEvent(MotionEvent)、onInerceptTouchEvent(MotionEvent)、onTouchEvent(MotionEvent),它们之间的关系用伪代码来表示的话如下:

    

    意思就是,每个view都可以拦截事件,或者选择不拦截。如果拦截事件,则调用自己的onTouchEvent来试图消耗这个事件,消耗成功则告诉上层视图(如果存在),好让上层视图也一步步往上汇报事件已经被消耗;如果消耗失败则把失败的消息上传上层视图,调用上层视图的方法(上层的onTouchEvent)进行处理。如果不拦截事件,则会试图调用自己的子视图的分配方法去让子视图分配事件,然后等待子视图的处理结果,在获得结果后再把结果返回给自己的上层视图。

    事件分发的整体过程是:Activity->Window->顶层View(一般是ViewGroup)->子View->...

    需要补充的是,如果一个View在处理事件时,设置了OnTouchListener,则onTouchEvent不会执行,会选择执行onTouch方法,2者选其一来执行;如果onTouchEvent里有设置OnClickListener,则onClick在clickable或者longClickable有一个为true时就会执行,也就是说,执行优先级是OnTouchListener>onTouchEvent>OnClickListener。

    关于传递机制的一些结论:

    1.一个事件序列由DOWN事件开始,以UP事件结束

    2.一般情况下,一个事件序列只能被一个View拦截,DOWN事件被拦截后,后续的MOVE和UP都会交由它来处理,并且不会再询问它是否拦截了

    3.一个View虽然拦截下了事件,但是如果它对于事件的首事件DOWN并不消耗掉,则剩下的事件就都转给上层视图的处理方法onTouchEvent方法去执行处理,如果没有一个视图层可以消耗掉DOWN事件,则最终会由Activity的onTouchEvent方法来处理

    4.一个View拦截下了事件,并且它对事件的首事件DOWN消耗,但是如果对剩下的事件不消耗,则无论它是哪一层的view都会把剩下的事件交由Activity的onTouchEvent处理,而不是给上一层非Activity视图

    5.默认情况:ViewGroup类默认是不拦截事件的;可点击View默认消耗事件,比如Button,它的clickable默认是true;相反,不可点击View默认不消耗事件,比如TextView,它的clickable默认是false;View的longClickable默认是false(可点击的意思就是longClickable和clickable都是true)

    6.View的enable属性不会影响视图是否消耗事件,它只是在视图的显示上做出改变

    7.可以通过requestDisallowInterceptTouchEvent方法阻止父视图对非DOWN事件的拦截

    对于拦截与消耗,可以这样总结:

    事件总是由顶到底分发下去的,当某个视图决定拦截事件时,意味的是事件的分发底线到了这个视图,这个视图以下的视图拦截都无效了,无效的意思是所有的事件,无论是点击DOWN还是MOVE和UP事件都会直接交付到这个决定拦截事件的view来处理,因为所有的拦截方法对于是否拦截事件都只判断一次;但是获得事件拦截的view是可以又将事件序列的处理返回给上一个视图来处理的,只要它对DOWN事件的处理我们手动返回false就行了,这样的机制是为了解决滑动冲突和无效滑动设计的,虽然大多数情况它默认是返回true(无效滑动是针对一些不可点击的视图,比如TextView,也就是说TextView会拦截事件,但是它又会把拦截的事件自动返回给上一个视图处理);如果只对DOWN返回true,其他事件返回false,则这些返回false的事件会由Activity来处理,不会给上一个视图,这样是保证一个事件序列不会让2个视图来处理。


    整个传递的具体过程:Activity在获得事件后,通过Window(通过实现类PhoneWindow)把事件传递给decor view,decor view是当前界面的父容器,也就是我们用setContentView设置的view的父容器,decor view又会把事件传递给顶层view,也就是用setContentView设置的view。

    子视图其实也可以拦截已经拦截DOWN事件的父视图除了DOWN以外的事件,通过requestDisallowInterceptTouchEvent来改变父视图的FLAG_DISALLOW_INTERCEPT便签,这和子视图在拦截到事件后,通过设置onTouchEvent返回false让父视图来处理事件一样,都是设置来解决滑动冲突的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值