权利的游戏Episode 3 of Series 8,想起《起风了》中的一句话。
起风了,唯有努力生存
无论对手多么强大,纵然被一步一步逼入绝望,我们能做唯有拼尽全力。
伪代码 ViewGruop(简单版)
dispatchTouchEvent代码逻辑
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if(onInterceptTouchEvent(ev)) {
consume = onTouchEvent(ev);
} else {
consume = child.dispatchTouchEvent(ev);
}
}
ViewGroup会先调用onInterceptTouchEvent方法判断是否要拦截,如果拦截就交给自己的onTouchEvent处理,如果不拦截交给匹配到的子View处理。不是很细节。
view dispatchTouchEvent代码
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
可以看出TouchListener.onTouch优先级高于View的OnTouchEvent方法,如果TouchListener的onTouch返回true表示事件已经被消耗了,就不会交给OnTouchEvent方法处理了。
代码 ViewGruop (复杂版)
public boolean dispatchTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
final float xf = ev.getX();
final float yf = ev.getY();
final float scrolledXFloat = xf + mScrollX;
final float scrolledYFloat = yf + mScrollY;
final Rect frame = mTempRect;
boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
//根据Action_Down事件 找到目标View
if (action == MotionEvent.ACTION_DOWN) {
if (mMotionTarget != null) {
mMotionTarget = null;
}
// 是否禁用拦截 或者 是否没有拦截 Down事件
if (disallowIntercept || !onInterceptTouchEvent(ev)) {
ev.setAction(MotionEvent.ACTION_DOWN);
final int scrolledXInt = (int) scrolledXFloat;
final int scrolledYInt = (int) scrolledYFloat;
final View[] children = mChildren;
final int count = mChildrenCount;
for (int i = count - 1; i >= 0; i--) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null) {
child.getHitRect(frame);
if (frame.contains(scrolledXInt, scrolledYInt)) {
final float xc = scrolledXFloat - child.mLeft;
final float yc = scrolledYFloat - child.mTop;
ev.setLocation(xc, yc);
child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
if (child.dispatchTouchEvent(ev)) {
mMotionTarget = child;
return true;
}
}
}
}
}
}
boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
(action == MotionEvent.ACTION_CANCEL);
if (isUpOrCancel) {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
final View target = mMotionTarget;
//目标view为空 (1.未找到匹配的view 2.down事件被自身拦截了)直接调用 自身onTouch
if (target == null) {
ev.setLocation(xf, yf);
if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
}
return super.dispatchTouchEvent(ev);
}
//如果拦截子View会收到ACTION_CANCEL事件
if (!disallowIntercept && onInterceptTouchEvent(ev)) {
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
ev.setAction(MotionEvent.ACTION_CANCEL);
ev.setLocation(xc, yc);
if (!target.dispatchTouchEvent(ev)) {
}
mMotionTarget = null;
return true;
}
if (isUpOrCancel) {
mMotionTarget = null;
}
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
ev.setLocation(xc, yc);
if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
mMotionTarget = null;
}
return target.dispatchTouchEvent(ev);
}
归纳总结
1.消费了DOWN事件的View,后续事件不管触摸点是否在该View内部还是外部,都会分发给该View
2.消费了DOWN事件的View,如果后续事件在其上层父View分发过程中拦截了,那么该View会收到一个CANCEL事件
3.一个View只有可见性为VISIBLE才有可能消费触摸事件,对于那些满足条件但被设置为INVISIBLE或GONE的View,不会消费触摸事件
滑动冲突解决方式
外部拦截法(修改父View)
父View
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction) {
case Action_DOWN:
return faslse; //不能拦截 否则后续全部交由父View处理
case Action_MOVE:
if(拦截条件)return true;
else return false;
case Action_UP:
return false;
}
}
内部拦截法
父View不能拦截Action_DOWN事件 确保Action_DOWN 返回false
子View
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction) {
case Action_DOWN:
parent.requestDisallowInterceptTouchEvent(true);
break;
case Action_MOVE:
if(父容器需要处理改事件) {
parent.requestDisallowInterceptTouchEvent(false);
}
break;
case Action_UP:
break;
}
return super. dispatchTouchEvent(ev);
}
参考文献
https://www.kancloud.cn/digest/defineview/120018
https://blog.csdn.net/guolin_blog/article/details/9153747
https://blog.csdn.net/abcdef314159/article/details/51119238