关于Android事件分发的设计模式理解与思考

关于Android事件分发的设计模式理解与思考

在现在Android智能机上,触碰几乎成为了唯一的交互方式。那么触碰消息在Android系统当中怎么进行分发的呢?
在事件分发处理上,Android使用了责任链模式。将message放到消息链中让其自行匹配处理。

事件分发机制

事件分发机制大概可以分为三个部分:

  1. 事件生产: 由客户操作屏幕产生
  2. 事件分发: 用户和设备交互产生事件后,事件层层传递,最后交由目标view的这个过程就是事件分发
  3. 事件消耗: 目标view消耗了事件,并做出反应就是事件消耗

MotionEvent

MotionEvent 的事件有哪些,他们的定义是什么?

  1. ACTION_DOWN: 手指首次触摸到屏幕时触发
  2. ACTION_UP: 手指离开屏幕时触发
  3. ACTION_CANCEL: 事件被上层拦截时触发
  4. ACTION_MOVE: 手指在屏幕上滑动时触发,会多次触发

事件分发处理有3个关键方法,我们通过这3个方法对message进行拦截、下发、传递处理等;

  1. dispatchTouchEvent: (事件下发--view和viewGroup都有的方法)
  2. onInterceptTouchEvent: (拦截下发的事件,并交给自己的onTouchEvent处理--viewgroup才有的方法)
    1. 同一个事件组只会调用一次该方法
    2. 因为只有事件是ACTION_DOWN时才会判断该方法
  3. onTouchEvent: (事件上报--view和viewGroup都有的方法)

上面三个方法如果返回值为true,则代表终止传递

事件传递流程

在一个activity中有两个viewGroup、一个view。
Acitvity ->PhoneWindow-->DockerView>ViewGroup ->View

  1. 查看Acitvity源码可以看到,Acitvity实现了Window类的一个回调接口Window.Callback
    因此Acitvity中有dispatchTouchEventonTouchEvent两个方法,当一个事件被产生后,首先Activity的dispatchTouchEvent会被执行,这是如果在Acitvity重写了该方法,事件将交由Acitvity的onTouchEv![img](https://img2023.cnblogs.com/blog/1281505/202301/1281505-20230128140218158-291472459.png)ent处理
  2. 否则将继续传递到PhoneWindow。在Acitvity的dispatchTouchEvent这个方法中可以看到,它执行了getWindow().superDispatchTouchEvent,这个windows就是PhoneWindow
  3. 继续往下看,能发现事件传递到了DecorView,而DecorView的父类是FrameLayout
  4. 接下来事件将逐级传递,最终到view
  5. 如果这个过程dispatchTouchEvent始终返回true,事件一直没有被消耗。那么事件将在onTouchEvent这个方法回传

上图:
Android事件分发流程图

  1. 那么,假如activity的dispatchTouchEvent返回true呢,那么消息将终止传递。
  2. 当事件走到viewGroup1时,将调用 onInterceptTouchEvent查询是否拦截事件,默认为false不拦截,如果为true将停止向下传递,将自己处理该事件,后续事件将都交于该事件处理。在同一个事件组, onInterceptTouchEvent只调用一次。
  3. 当事件走到view时(注意,view没有 onInterceptTouchEvent方法,viewGroup才有 )将调用onTouchEvent,如果该方法返回true,事件终止传递,事件被消费,将逐层返回true结果。如果返回false,将事件传递给上层onTouchEvent处理事件。

事件冲突

什么是事件冲突?
当一个事件,对应有多个人想要去处理它?而如果处理的对象不是我们想要的对象,就叫发生了冲突。

例子1:

同时添加setOnClickListener()setOnTouchListener()两个监听事件,并在onClick()onTouch()中打印日志。
onTouch()==false的情况下onClick()onTouch()都会打印日志,当onTouch()==true时,onClick()不会打印日志。

这是什么原因,查看view的源码可以看到,在view的dispatchTouchEvent()这个方法中,有一个判断,当onTouch()==true时,dispatchTouchEvent()将返回true,而通过事件分发可以知道,dispatchTouchEvent()返回true表示这个事件已经被该view消耗了,代表事件终止传递。

例子2:

有一个垂直滑动的view嵌套了一个水平滑动的view。这种情况存在上下滑动和水平滑动,两者会发生冲突。

解决思路1:

  1. 在父view的onInterceptTouchEvent中拦截事件,当事件为ACTION_DOWN时不拦截。
  2. 在子view中的dispatchTouchEvent方法中调用requestDisallowInterceptTouchEvent(true)方法来禁止父view拦截事件。当在合适的情况再调用requestDisallowInterceptTouchEvent(false)允许父view拦截事件。

比如:
dispatchTouchEvent中,事件类型为ACTION_DOWN时,禁止父view拦截事件,然后判断滑动的方向是垂直还是水平,如果是垂直则调用requestDisallowInterceptTouchEvent(false)允许父view处理事件。

解决思路2:

  1. 在父view的onInterceptTouchEvent方法中处理拦截事件,判断滑动方向。如果是垂直则拦截事件,自己处理。如果是水平方向则不拦截,让子view处理。这样就使父view的垂直滑动和子view的水平滑动都能流程进行。
posted @   Ysun_top  阅读(118)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示