关于事件分发和滑动冲突
2021-01-26 本文已影响0人
浩仔_Boy
自己学习笔记,仅供自己参考,如有不对欢迎指正
事件分发,概括成一句话:
Activity-PhoneWindow-DecorView-ViewGroup-View,如果view不处理,则会将事件往上传递最终交给Activity处理掉。
事件由上往下分发流程:
事件由上往下传递主要是通过DispatchTouchEvent方法,如果当前节点是ViewGroup则会循环它的所有子View的DispachTouchEvent。如果View不消耗MotionEvent.ACTION_DOWN 事件(onTouchEvent 返回false),后续事件的分发过程中不会调用此 View 的 dispatchTouchEvent 方法。
事件传到到最下层,view的处理;
当事件传递到最下层view,如果其不消耗或者只消耗了 MotionEvent.ACTION_DOWN 事件,但是不消耗同一事件序列的后续事件,则后续事件中不会调用 View 父元素的 onTouchEvent 方法,而是调用 Activity 的 onTouchEvent 方法
滑动冲突的处理:
目前不管是ScrollView、ViewPager等看他源码都已经做了滑动冲突。
滑动冲突处理,两种方案
1.在外层拦截
主要是拦截onInterceptTouchEvent方法
public class OuterInterceptView extends View {
private float startY;
private float startX;
// --- 记录是否正在拖拽的标记
private boolean mHorScroDraging;
private final int mTouchSlop = ViewConfiguration.get(this.getContext()).getScaledTouchSlop();
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
{
// -- 不拦截横向滑动
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
// --- 记录手指按下的位置
startY = ev.getY();
startX = ev.getX();
// --- 初始化标记
mHorScroDraging = false;
break;
case MotionEvent.ACTION_MOVE:
// --- 如果正在横向中,那么不拦截它的事件,直接 return false;
if (mHorScroDraging) {
return false;
}
// --- 获取当前手指位置
float endY = ev.getY();
float endX = ev.getX();
float distanceX = Math.abs(endX - startX);
float distanceY = Math.abs(endY - startY);
// --- 如果 X 轴位移大于 Y 轴位移,那么将事件交给 viewPager 处理。
if (distanceX > mTouchSlop && distanceX > distanceY) {
mHorScroDraging = true;
return false;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// --- 初始化标记
mHorScroDraging = false;
break;
}
}
return super.onInterceptTouchEvent(ev);
}
}
2.在内部拦截
一般是拦截DispacthTouchEvent的方法,拦截掉down事件,如果父View还需要重新获得事件的处理权限,则可以调用requestDisallowInterceptTouchEvent方法。
public class InnerInterceptView extends View {
private float startY;
private float startX;
// --- 记录是否正在 Ver 拖拽的标记
private boolean mVerScroDraging;
private final int mTouchSlop = ViewConfiguration.get(this.getContext()).getScaledTouchSlop();
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
// --- 记录手指按下的位置
startY = ev.getY();
startX = ev.getX();
// --- 初始化标记
mVerScroDraging = false;
break;
case MotionEvent.ACTION_MOVE:
// --- 如果正在 Ver 拽中,那么让父容器拦截
if (mVerScroDraging) {
getParent().requestDisallowInterceptTouchEvent(false);
return false;
}
// --- 获取当前手指位置
float endY = ev.getY();
float endX = ev.getX();
float distanceX = Math.abs(endX - startX);
float distanceY = Math.abs(endY - startY);
// --- 如果 Y 轴位移大于 X 轴位移,那么将事件交给父容器处理。
if (distanceY > mTouchSlop && distanceY > distanceX) {
mVerScroDraging = true;
getParent().requestDisallowInterceptTouchEvent(false);
return false;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// --- 初始化标记
mVerScroDraging = false;
break;
}
return super.dispatchTouchEvent(ev);
}
}