View的事件分发机制

安卓的事件分发已经说了很多了,但是不少书籍、博客都是从源码的角度解释,实际源码是更多给研发自己、给机器看的,而不是给别人看的。所以希望从业务逻辑的角度,尽可能来解释事件分发机制。

1. 分发过程

首先明确事件分发主要是指触摸事件,以及点击事件等,这些MotionEvent。安卓整体是事件驱动的,所以从Looper中获取触碰等事件,首先分发到的应该是Activity中。我们要研究的是View中的分发机制,所以要弄清楚怎么分发到View的。从《安卓开发艺术探索》中可以知道,安卓的分发通过Activity到Window里,由于Window实际上只有PhoneWindow的实现,所以是到PhoneWindow里,再由PhoneWindow进行dispatchTouchEvent(),传到了DecorView里,这是安卓的顶层布局,是一个FrameLayout。

1. 1分发对象

1.1.1 缓存的顶部触摸目标:

触摸事件发生后,肯定是有承载对象的,用户触碰到的是什么。安卓由于是层级的布局,触碰到的可能不止一个控件,在ViewGroup的逻辑,其中最顶部的也就是第一个碰到的目标mFirstTouchTarget。安卓内在分发的过程中,不断获取新的触碰目标,直到发送到顶部的view,这部分没有做缓存,只有第一个碰到的目标进行了缓存。

如果没有第一个触碰的目标,即mFirstTouchTarget是null,那么就说明事件中途被拦截了,ViewGroup会默认拦截接下来的事件。在很多书中描述的是后者的表现,但是没有看到其中的业务逻辑。

1.1.2 View可以不允许ViewGroup拦截,disallowInterceptTouchEvent

通过标记位可以让ViewGroup不能拦截除了Action_Down以外的事件。Down事件ViewGroup可以感知,是因为要通过这个事件真正打通分发的流程。

1.1.3 事件的接收

接受触摸事件主要由两个点来衡量:子元素是否在播放动画(这是探索里说的,但是我没找到逻辑)、点击坐标是否在区域内

1.2屏蔽

1.2.1 触摸优先于点击,自定义优于默认逻辑

onTouchListener设置后,onTouch()会屏蔽掉onTouchEvent(),也就是事件不会继续传递了。而onTouchEvent()中,onClick()也会被onTouch()屏蔽掉。

1.3 三个重要的方法

(1) dispatchTouchEvent()

(2) onInterceptTouchEvent()

(3) onTouchEvent()

2. 滑动冲突

2.1 滑动冲突的场景

滑动事件是分发的,外部View和内部View都可以收到。滑动冲突从业务逻辑看并不是冲突,而只是内部、外部需要对滑动事件进行各自的响应,而不能直接外部消耗掉。

(1)外部、内部不一致

(2)外部、内部一致;

(3)嵌套

2.2 解决方式

(1)外部拦截法:父容器处理是否拦截

(2)内部拦截法:子View处理,处理不了交由父View处理。由于分发机制不一致,需要配合requestDisallowInterceptTouchEvent方法才能正常工作

内部拦截法的真正原理_momo_ibeike的博客-CSDN博客_内部拦截法

这里介绍了cancel事件是因为mFirstTouchTarget不为空,且要父容器拦截事件,需要给mFirstTouchTarget发送cancel事件,然后父容器去处理,这个细节非常多的博客都没有注意到。

另外,这个博客虽然啰嗦,但比较重点的就是介绍了mFirstTouchTarget的处理逻辑,在Down事件发生的时候会置为空且重置FLAG_DISALLOW_INTERCEPT。因此,打破了这个逻辑:一个事件被消费,此事件序列则一定有此容器处理。这个逻辑实际就是由mFirstTouchTarget等管理的。对于容器来说,不考虑拦截的话,如果mFirstTouchTarget不为空,交由其处理,否则,自己处理。因而内部拦截的时候,ACTION_MOVE由于mFirstTouchTarget是空,则会交给自己处理。这也是内部拦截法,一个默认的规则。在Down事件来到时候,父容器不拦截,子容器处理;后续事件本该全有子容器处理,但是Move的时候可以通过requestDisallowInterceptTouchEvent(false),让父容器拦截生效。此时由于Down事件在子容器返回true,父容器的mFirstTouchTarget不为空,所以父容器不拦截的时候会交给子容器处理,拦截的话,就发送cancel给子容器,然后自己处理,over,哈,我这应该说的比大多数博客都清楚了吧。

(3)较高版本的安卓有自带的内部recyclerview

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值