5、事件冲突相关
1、常用事件~如图
WX20221011-235812.png
事件可以走的流程 down up。 down move up。 down cancle。down move cancle
2、可以进行 事件 分发和拦截的只有 viewGroup 事件的处理只能在 view 里面
a、在 ViewGroup 的 dispatchTouchEvent 中先走 onInterceptTouchEvent 判断是否需要拦截
1、不拦截的话 会通过 dispatchTransformedTouchEvent 调用 子 view的 dispatchTouchEvent方法, 然后看 b 的处理逻辑
2、拦截的话,会走 父类的 dispatchTouchEvent (也就是 他自己的 dispatchTouchEvent)
b、在view中的 dispatchTouchEvent
1、如果设置了OnTouchListener,会先走 OnTouchListener 的 onTouch 方法
如果 OnTouchListener 返回 true, 则 onTouchEvent 就不会执行,里面的 onClick 就不会执行了
如果返回false,则会走 view 本身的 onTouchEvent ,在UP 的时候会走performClickInternal 然后走 onClick 事件
3、上面主要阐述了 事件的 处理 逻辑 ,下面介绍一下分发的逻辑
a、如果 子 view 有 OnTouchListener 或者 onClick 事件,则 view 的 dispatchTouchEvent 一定返回 true
这里返回 true 表示 告诉 父类 控件 这个事件 我已经消费了
b、在 ViewGroup 的 dispatchTouchEvent 事件中 是在 down 事件 调用 onInterceptTouchEvent 并获取返回值的
1、 如果拦截了 ,mFirstTouchTarget 这个就是 null ,然后才会 执行 方法 dispatchTransformedTouchEvent
其中 dispatchTransformedTouchEvent 传入的 child 参数是 null ,所以在 该方法里面 会 调用 super.dispatchTouchEvent() ,
就走到了 view 的dispatchTouchEvent() , 然后的处理逻辑跟 2 一样 <ViewGroup 也是 extends View>
2、如果没有拦截, onInterceptTouchEvent 返回的是 false 并且 是 down 事件 我们走分发流
同上 分发 流程
1、将 ViewGroup 中的 所有子 view 做一个排序 根据 子view 的 z 值 。在方法 buildTouchDispatchChildList 中的 buildOrderedChildList 方法里面 如果没有 z 值,是根据 xml 中的顺序 排列的
2、对 排序 之后的 view 的集合 进行一个 for 的遍历, 源码中是 倒序 遍历的
对每一个 view 的操作 如下:
a、先对view 进行一个判断, view是否可见或者是否执行了animation 和 点击的point 是否在view的范围内
b、如果 a 中的 判断不成立, 则执行 continue 。获取 下一个 子 view
c、a 中的判断成立:执行下面的 三个流程
i、调用 dispatchTransformedTouchEvent 方法 第三个参数是 当前的 view。所以会 执行 子 view 的dispatchTouchEvent 方法,分发出去了
ii、如果 子 view 的dispatchTouchEvent 返回 false, 则 循环继续走,直到循环 走完之后,走
自己本身处理 和 拦截的流程 一样
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// Dispatch to touch targets
}
iii、如果 子 view 的dispatchTouchEvent 返回 true ,则说明 子view 把事件处理了这个时候
进入到 if 里面 执行 addTouchTarget 方法,给 newTouchTarget 赋值 此时:
- mFirstTouchTarget = newTouchTarget 并且 newTouchTarget.next = null 同时 alreadyDispatchedToNewTouchTarget = true;
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break; 跳出循环
iv、此时因为 mFirstTouchTarget 不是 null 了所以会走 else 逻辑
这里有一个 while 循环。来处理 单点触摸 和 多点触摸 这个时候 viewGroup 的 dispatchTouchEvent 返回true。
因为 MOVE 事件 是不会 走 循环的,并且move 事件来的时候 mFirstTouchTarget 不是 null
所以move 事件会直接走 上面的 else 代码块,然后又会 走到 dispatchTransformedTouchEvent 方法
这是 dispatchTouchEvent 中的 拦截的代码
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) { 可以设置 disallowIntercept 为 true ,是 拦截方法失效
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
通过 方法 requestDisallowInterceptTouchEvent() 可以设置 viewgroup 是否强制 不拦截