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

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

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

目录

1.流程解读:

从Activity--->DecorView:

从DecorView到子view:

2.场景分析:

场景一:

场景二:

场景三:


 

1.流程解读:

从Activity--->DecorView:

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

Android 事件分发原理和实际场景解析_第1张图片

基本流程:

  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

===事件流结束

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(android)