Android 事件分发原理和实际场景解析

事件分发是android里的解决事件冲突的一种机制。一般我们的布局都是一层叠着一层,那么当我们手指点击或者滑动的时候,屏幕怎么知道哪一个控件该响应,哪一个控件该滑动呢?这依靠的就是事件分发机制。

本次文章源码细节不扣,但是主要流程会列出来,感兴趣的可以去源码中找对应的逻辑,有疑问可以留言哦。

目录

1.流程解读:

从Activity--->DecorView:

从DecorView到子view:

2.场景分析:

场景一:

场景二:

场景三:


 

1.流程解读:

从Activity--->DecorView:

当我们在activity中触摸屏幕点击的时候,它的流程是这样的:

基本流程:

  1. 从activity的dispatchTouchEvent开始,调用到DecorView的dispatchTouchEvent()(DevorView是ViewGroup的子类,使用ViewGroup的事件分发机制)
  2. DecorView是所有view的父布局,它的dispatchTouchEvent() 会递归从外到内进行事件分发。

从DecorView到子view:

DecorView和它的ziview是父布局和子布局的关系,所以他们的流程就是父布局到子布局事件分发的过程,之后所有的过程都是这样递归的。记住一点,事件分发的流程顺序是从父布局到子布局。

 

事件分发涉及到的几个关键方法:

  • ①dispatchTouchEvent() // 事件分发的总调度方法
  • ②requestDisallowInterceptTouchEvent() // 子view限制父类不能拦截事件
  • ③onInterceptTouchEvent() // 决定当前的view是否拦截事件
  • ④onTouchEvent() // 当前view事件操作。返回值代表是否消费了事件

其中③④是在①中执行的,②是在外部执行的。

 

ok,下面从源码的角度分析,在这之前,先贴几条结论:

  1. 一般讲事件的开始和结束是指事件流的开始和结束。一个事件流指一系列操作,一般是以DOWN事件开始,UP或cancel事件结束。例如 DOWN-->MOVE-->MOVE-->....-->UP 这是一个事件流。
  2. 一个事件流中,如果一个View在DOWN事件的时候消费了事件,那么在父view不拦截的情况下,后续事件则都由它处理。
  3. 一个事件流中,父view可以在任何阶段拦截事件。当父view拦截了事件,那么后续的事件就由父view来接管了。
  4. 子view可以通过设置parent.requestDisallowInterceptTouchEvent(true)来禁止父view拦截事件。

 

下面进入源码了,主要代码都在ViewGroup#dispatchTouchEvent()中,代码就不贴了。可以去看一下这个方法的源码,大致流程如下:

  • ① 处理down事件:如果是DOWN事件,那么清除触摸对象mFirstTouchTarget,重置状态。
  • ② 检查事件拦截:通过状态和onInterceptTouchEvent() 的返回值来设置intercepted
  • ③ 如果不拦截&不取消(其他过滤条件省略)&当前事件是DOWN事件,按照从前往后的顺序遍历子view。遍历过程:
    • 如果找到子view消费了事件,那么触摸对象mFirstTouchTarget赋值,alreadyDispatchedToNewTouchTarget=true,跳出循环。
  • ④ 如果触摸对象mFirstTouchTarget为空,那么自己消费事件,即执行自己的onTouchEvent()
  • ⑤ 如果触摸对象不为空,则判断是否已经处理过(依据alreadyDispatchedToNewTouchTarget和target)
    • --如果未处理过:判断是否拦截(即intercepted)
    • --如果不拦截,继续交给target处理事件
    • --如果拦截,则向target传递一个cancel事件,然后清除mFristTouchTarget。 然后下一个后续事件中自己就消费事件了。

上面的流程在源码中都能找到,说的比较详细,下面我们来从实际场景分析以上流程。

 

2.场景分析:

举例三个常见的场景

场景一:

父view不拦截,子view消费事件。

事件流开始===

1.DOWN事件:

经过①②后:

mFirstTouchTarget=null

intercepted=false

经过③后:

mFirstTouchTarget=taget

alreadyDispatchedToNewTouchTarget=true

这时已经处理了事件

④过

⑤过

 

2.MOVE事件

经过①②,

mFirstTouchTarget=target

intercepted=false

③过

④过

⑤满足触摸对象不为空,未处理过,不拦截,继续交给target处理事件

 

之后的MOVE/UP事件流程同2

===事件流结束

 

场景二:

父view拦截事件,子view消费事件

 

事件流开始===

1.DOWN事件:

经过①②后:

mFirstTouchTarget=null

intercepted=true

③过

④满足mFirstTouchTarget=null, 自己消费事件

⑤过

 

之后的事件都同1

===事件流结束

 

场景三:

父view开始不拦截事件,当MOVE时开始拦截,子view消费事件。

 

事件流开始===

1.DOWN事件:

经过①②后:

mFirstTouchTarget=null

intercepted=false

经过③后:

mFirstTouchTarget=taget

alreadyDispatchedToNewTouchTarget=true

这时已经处理了事件

④过

⑤过

 

2.MOVE事件

经过①②后:

mFirstTouchTarget=target

intercepted=true

③过

④过

⑤满足 触摸对象不为空,未处理过,拦截,则向target传递一个cancel事件,然后清除mFristTouchTarget。

 

3.下一个MOVE事件

经过①②后:

mFirstTouchTarget=null

intercepted=true

③过

④满足mFirstTouchTarget=null, 自己消费事件

⑤过

 

之后的事件都同3

===事件流结束

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龍林1102

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值