-
事件分发机制
1.1. 事件分发的顺序:
Activity
->ViewGroup
->View
1.2. 事件分发涉及到的方法
public boolean dispatchTouchEvent(MotionEvent ev)
事件过来的时候首先拜访这个方法,由这个方法决定,是不是需要向子类下发,如果有父类可以向父类分发(需配合requestDisallowInterceptTouchEvent
使用)。
public boolean onInterceptTouchEvent(MotionEvent ev)
分发过来的事件是不是要终止,如果终止不向子View 传递,否则反之。
public boolean onTouchEvent(MotionEvent ev)
用来处理事件。
三者的区分伪代码如下:public boolean dispatchTouchEvent(MotionEvent ev){ boolean consume = false; if(onInterceptTouchEvent(ev)){ consume = onTouchEvent(ev); }else{ consume = child.dispatchTouchEvent(ev) } return consume; }
所以点击事件优先级
onTouch
—>onTouchEvent
—>onClick
-
事件拦截机制
有了上面的基础,下面一张图就可以明白了:
如果父类在
onInterceptTouchEvent
方法中down
事件return true
,那么子View
根本收不到任何的事件。因为在这里已经拦截消费掉了。
如果在parent View 不拦截事件情况下 ,子View
如果设置了getParent().requestDisallowInterceptTouchEvent(true)
,
那么首次父 事件 的down
不会拦截,在子View接收到down
事件后,设置了父 的 事件拦截(getParent().requestDisallowInterceptTouchEvent(true)
),子ViewonTouchEvent
返回true
消费事件,那么以后的事件消费都在子View 进行了。
具体这块有个链接不错 Android事件传递之onInterceptTouchEvent()和requestDisallowInterceptTouchEvent()方法的使用类比吃苹果,
event
是一个苹果,爷爷拿着苹果如果不吃,会给爸爸,如果爸爸不吃会给儿子,如果儿子吃掉,就消费掉了苹果,如果儿子不吃,就返回给爸爸,爸爸不吃就返回给爷爷,如果爷爷不吃,那么Activity
的onTouchEvent
方法就结束了。 -
滑动冲突解决方式
3.1. 外部拦截法
外部拦截法需要重写父容器的onInterceptTouchEvent方法,在内部做相应的拦截即可,如下面伪代码所示:@Override 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; break; case MotionEvent.ACTION_MOVE: if(父容器需要点击事件){ intercepted=true; }else{ intercepted=false; } break; case MotionEvent.ACTION_UP: intercepted=false; break; default: break; } return intercepted; }
针对不同的滑动冲突只需要修改
父容器需要点击事件
这个条件即可,其他均不需要且不能修改。
在父容器的onInterceptTouchEvent
方法中,首先是ACTION_DOWN
事件,父容器必须返回false
,即不拦截ACTION_DOWN
事件,这是因为一旦父容器拦截者这个事件,那么后续的ACTION_MOVE
和ACTION_UP
事件都会直接交由父容器处理,这个时候就没法传递给子元素了;其次是ACTION_MOVE
事件,这个事件可以根据需要来决定是否拦截;最后是ACTION_UP,
这里必须返回false
,如果父容器返回true
,就会导致子元素无法接收到ACTION_UP
事件,这个时候onClick
事件就无法触发。3.2 内部拦截法
内部拦截法是指父容器不拦截任何事件,所有事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交由父容器处理。我们需要重写子元素的dispatchTouchEvent方法,一下是伪代码:@Override public boolean dispatchTouchEvent(MotionEvent event) { int x = (int) event.getX(); int y = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: parent.requestDisallowInterceptTouchEvent(true);// 表示不拦截 break; case MotionEvent.ACTION_MOVE: if (父容器需要点击事件) { parent.requestDisallowInterceptTouchEvent(false);// 表示拦截 } break; case MotionEvent.ACTION_UP: break; default: break; } return super.dispatchTouchEvent(event); }
针对不同的滑动冲突只需要修改
父容器需要点击事件
这个条件即可,其他均不需要且不能修改。
除了子元素需要处理以外,父元素也需要处理除ACTION_DOWN
以外的其他事件,这样子当子元素调用parent.requestDisallowInterceptTouchEvent(false)
时,父元素才能继续拦截所需事件。
参考 常见的滑动冲突场景及解决方案
View 的事件分发
最新推荐文章于 2021-01-13 17:48:20 发布