事件分发

  • content
    {:toc}

    0x01 为什么需要学习事件分发

  1. 事件分发可以让我们解决一些自定义view和控制一些组件的冲突
  2. 用了一个设计模式:责任链设计模式

0x02 事件分发

1: 先上个图片

2: 根据图片进行分析

  1. 事件分发最开始是通过activity的 dispatchTouchEvent 方法来进行的,activity上一层是C/C++层,具体应该是跟屏幕驱动之类的了。

  2. 根据代码:

    父控件的dispatchTouchEvent返回true,父控件自己的拦截也走了,事件直接在dispatchTouchEvent的地方消耗了

dispatchTouchEvent(view中的方法被ViewGroup重写) onTouchEvent(View中的被ViewGroup重写,onClickListener在它的UP事件上面) onInterceptTouchEvent(只有ViewGroup中有)
父控件(ViewGroup) 返回true,就直接处理了,后面的onInterceptTouchEvent也不进行执行了,因为把父类当成DecorView的子类就好了,直接就从这里不往下执行了,这里已经返回true,事件在这个地方已经被消费了。,如果自定义viewGroup在这个地方返回true,上面的点击事件就不起作用了;
其实只要不调用父类的方法(super.dispatchTouchEvent(ev))都在dispatchTouchEvent这里就不往下面传递了,这里最好根据上滑动或者下滑动的方式,或者是MOVE,DOWN,UP事件来进行判断,如:自己需要上滑动的情况下来进行返回true不给子控件点击这种来进行,相当于单个来进行放回true,尽量不要直接返回true来进行全部自己消费。
这个是事件的最终消费的方法.这里分两种情况:1.就是onInterceptTouchEvent不返回true的情况下。只要用户的点击不命中自己的上面(就是点击没有在自己的区域上面,源码的dispatchTouchEvent里面判断完整的点击事件在UP事件上面,并进行了点击范围的处理),都不会调用这个方法,即使命中的控件是enable是false的
2.就是onInterceptTouchEvent返回true,表示拦截了子类的某种行为。这个方法就会被回调
3.当子类在某种情况下调用了getParent().requestDisallowInterceptTouchEvent(false)这个方法也不会调用这个方法了。
如果返回true的话就会直接不给自己的子View点击事件了
子控件(view,不是viewGroup) 如果直接返回true,自己的onTouchEvent不会执行。viewGroup也一样(onInterceptTouchEvent也不执行了,因为onInterceptTouchEvent来进行是否分发在它的dispatchTouchEvent中) 如果返回true就代表事件在这里就消耗了。 view中没有该方法

requestDisallowInterceptTouchEvent说明,一开始拦截了就不起效果了:

第一个事件是先调用 ViewGroup 的 onIntercept 的,所以如果一开始 ViewGroup 就打算拦截,子 View 将没有任何机会;

所有的事件以:ACTION_DOWN 开始,ACTION_UP 或者 ACTION_CANCEL 结束

3:解决冲突从父组件的onInterceptTouchEvent入手

1
2
3
4
5
6
7
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(!allowChildViewScroll){
return true;
}
return super.onInterceptTouchEvent(ev);
}

如果不允许子控件滑动,就直接禁止这种方式,防止父类不能进行滑动。
只不过,还有很多种情况,比如,子控件可以滑动,通过滑动到第一个item或者说不能滑动的时候,然后又把滚动的权利还给父控件,然后就连贯了

getFirstVisiblePosition / getLastVisiblePosition 或者canScrollVertically(-1) 这些方式来判断滑动是否到了极限值,然后给另一个需要滑动的控件来进行滑动

参考

事件传递及滑动冲突的处理
Android之ScrollView嵌套两个ListView及冲突解决