事件分发处理与滑动冲突

幻昼 2021年03月07日 244次浏览

事件分发知识点

事件分类

事件简介
ACTION_DOWN手指 初次接触到屏幕 时触发
ACTION_MOVE手指 在屏幕上滑动时触发,会多次触发
ACTION_UP手指 离开屏幕 时触发
ACTION_CANCEL事件 被上层拦截 时触发
ACTION_OUTSIDE表示用户触碰超出了正常的UI边界
ACTION_POINTER_DOWN有一个非主要的手指按下了
ACTION_POINTER_UP一个非主要的手指抬起来了

相关方法

类型相关方法ActivityViewGroupView
事件分发dispatchTouchEvent
事件拦截onInterceptTouchEventXX
事件消费onTouchEventX

事件分发流程

流程图

dispatchEvent

源码分析(时序图)

事件分发

事件冲突

冲突原因

根本原因就是事件分发到某个对象时,该对象处理了不该处理的事件或者没有处理到本该自己处理的事件。

  1. 例如提前拦截子view的事件,返回 true。
  2. 又或者子view消费了事件,仅完成了自己的操作,没有让父view 处理其负责的事件。

解决冲突

  1. 内部拦截法

    父 view 在 onInterceptTouchEvent 可以放行 含有子view该处理的事件 到子view,子view在 dispatchTouchEvent 发现是自己该处理的,请求父view不要拦截,发现是父view该处理事件,就让父view拦截该事件

  2. 外部拦截法

    父view在 onInterceptTouchEvent 判断是否为自己该处理的事情,是的话就拦截,返回true。不是的话就放行或默认处理,看情况

其他

                // 不允许父view调用onInterceptTouchEvent方法
				// ViewCompat.setNestedScrollingEnabled(this, true);
                getParent().requestDisallowInterceptTouchEvent(true);

事件分发流程再看

事件分发

四个重要方法:

  1. dispatchTouchEvent
  2. onInterceptTouchEvent 在分发流程如果调用这方法返回 true,则时间会被拦截
  3. onTouchEvent
  4. setOnTouchListener

requestDisallowInterceptTouchEvent(true) 子 view 禁止 parent 以及它的祖先拦截触摸事件

onTouchListener 中的onTouch方法每次都会先于view本身的onTouchEvent调用,且有优先消费权

事件的记忆机制

含义:窗口内的某个view消费了事件(onTouchEvent返回true),则后续事件会直接发给此view的onTouchEvent,不会再经过其他view的onTouchEvent。

原理:路径记忆是用分层记忆实现的。每个viewgroup都是分发的关键节点,其维护一个TouchTarget链表、记录哪个子view消费该事件。上层只关注其直接子节点。

事件的截获机制

如果截获ACTION_DOWN事件(返回true),则事件不会再分发给子view。

dispatchTouchEvent
	if(onInterceptTouchEvent)
		return this.onTouchEvent
	else
		return child.onTouchEvent

两种情况可截获:

  1. ACTION_DOWN事件一定可截获
  2. 非ACTION_DOWN事件,若子类消费了事件,且自身标志位允许截获,则可截获。

事件的分裂机制

含义:
当某个view消费一次事件(ACTION_DOWN)后,后续点击到其旁系view的多指触摸事件将被分裂为单独的事件(ACTION_DOWN),且其旁系view可消费、记忆此单独事件。

Android 3.0 以上默认开启此机制,可在主题中配置activity和window关闭此功能。