「这是我参与2022首次更文挑战的第23天,活动详情查看:2022首次更文挑战」
相关文章:
Android View的事件分发(一)-事件分发的过程
Android View的事件分发(二)-事件传递顺序
Android View的事件分发(三)-事件的分发(dispatchTouchEvent)
Android View的事件分发(四)-事件处理(onTouchEvent)
Android View的事件分发(五)-事件拦截(onInterceptTouchEvent)
前面几篇文章,对View的事件分发做了简单的介绍,从View的事件分发的定义,到事件的分发流程,传递过程,分发事件(dispatchTouchEvent),事件处理(onTouchEvent),最后到事件的拦截(onInterceptorEvent),这整个过程中的重要部分源码,那么下面来简单总结下,View事件分发的几个重点:
1)如果我们需要处理事件的拦截,不要把处理拦截的操作放到分发过程中,也就是dispatchTouchEvent中,如果 viewGroup 需要拦截事件,在 onInterceptTouchEvent 中在相应的事件返回 true 即可。拦截的操作写在 onTouchEvent 中。
2)如果我们在处理MotionEvent的时候,拦截了ViewGroup的ACTION_DOWN,那么所有的事件都会被拦截,从而响应子 view 的点击事件。
3)如果想让子View不被拦截,则可以在子 view 在 dispatchTouchEvent 中的 ACTION_DOWN 中调用 getParent().requestDisallowInterceptTouchEvent(true)。前提是父 view 不拦截 ACTION_DOWN 事件。
4)如果我们的父级View没有拦截ACTION_DOWN 事件,但拦截其他事件,那么我们可以处理拦截后需要做的事,这个时候可以在 onTouchEvent 中处理。
5)如果我们的父级View做事件的拦截,那么对于父View的事件处理就和View是一致的,如果设置了 OnTouchListener 还会响应 onTouch 事件, 注意返回 false, 返回 true 将不会执行 onTouchEvent。
那么介绍了View的事件处理之后,我们在项目中遇到的问题有哪些,首先最常见的就是滑动冲突,那么我们就滑动冲突做一些比较常用的解决方法
滑动冲突解决方法
首先,为什么我们在嵌套的UI里面会存在滑动冲突呢?滑动冲突产生的原因:只要在界面中存在内外两层可以同时滑动,就会产生滑动冲突。如下所示:图1是左右滑动和上下滑动冲突,图二是两个view之间的上下滑动冲突;
那么怎么去解决了,这个需要根据实际的需求,判断需要谁去处理滑动事件,谁需要禁用
提供2种方法:1)外部拦截;2)内部拦截
外部拦截
字面意思就很好理解,就是通过外部的拦截来处理,那么外部相对应的就是父View,也就是ViewGroup,通过父 view 根据自己需要来判断是否需要拦截事件。对于 ViewGroup,有个 onInterceptTouchEvent 方法,z在需要拦截的时候,返回 true 即可。相关代码如下:
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercepted=false;
int x= (int) event.getX();
int y= (int) event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
intercepted=false;//必须不能拦截,否则后续的ACTION_MOME和ACTION_UP事件都会拦截。
break;
case MotionEvent.ACTION_MOVE:
if (父容器需要当前点击事件){
intercepted=true;
}else {
intercepted=false;
}
break;
case MotionEvent.ACTION_UP:
intercepted=false;
break;
default:
break;
}
mLastXIntercept=x;
mLastXIntercept=y;
return intercepted;
}
内部拦截
内部拦截,自然是通过处理自身的事件来解决滑动冲突的问题,那么view应该如何处理呢?首先view 在 dispatchTouchEvent 方法内部调用 requestDisallowInterceptTouchEvent 不让父 view 拦截事件,然后再 onTouchEvent 方法中处理需要拦截的情况。不拦截的时候返回 false,将事件交还给父 view 处理。相关代码如下:
// view
public boolean dispatchTouchEvent(MotionEvent ev) {
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
getParent().requestDisallowInterceptTouchEvent(true);
break;
}
case MotionEvent.ACTION_MOVE: {
int deltaX = x - mLastXIntercept;
int deltaY = y - mLastYIntercept;
//如果是左右滑动
if (Math.abs(deltaX) > Math.abs(deltaY)) {
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
}
case MotionEvent.ACTION_UP: {
getParent().requestDisallowInterceptTouchEvent(false);
break;
}
}
mLastXIntercept = x;
mLastYIntercept = y;
return super.dispatchTouchEvent(ev);
}
这样还不行,因为这个时候可能父View会消费事件,所以还需要在我们的Action_down的事件保证父类不会拦截,否则事件就不会传递到子View了。
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
if (action == MotionEvent.ACTION_DOWN) {
return false;
} else {
return true;
}
}
如果这个时候子View也不处理的话,就会返回给父View处理。
好啦,到这里,对View事件的处理,就介绍的差不多了,如有不对的地方还请指正,欢迎留言评论