当两个滑动的容器,比如ScrollView和ListView嵌套的时候,就会出现冲突的情况。或者ScrollView嵌套地图控件的时候。也会产生滑动冲突,底层控件无法滑动。
解决的核心方法。来自ViewGroup中的一句核心的判断语句。可能不同版本的API这句话表述的方式不太一样,但基本原理是一致的。
if(disallowIntercept || !onInterceptTouchEvent(ev))
为了更好的理解这句话,简单说下事件分发的机制。当事件到达ViewGroup 的 dispatchTouchEvent的时候,这时事件会选择上面的判断,如果上面判断为true,则父容器不会拦截这个事件,事件会执行到子view,子view会在OnTouchEvent中选择消费还是不消费这个事件。也就是说当滑动冲突的时候,我们只需要告诉父容器的不要拦截这个事件。
写了个简单测试例子,ScrollView嵌套了一个200dp高度的ListView.这时候的ListView已经无法滑动,因为ActionMove的事件会被ScrollView给拦截掉,在父容器执行了,根本就无法到达子元素。
我们只需要在ListView添加如下事件。就可以正常滑动
lv_ceshi.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
//if(disallowIntercept || !onInterceptTouchEvent(ev))
//viewGroup有这么一个判断,判断为false父容器将会拦截事件,通常disallowIntercept = false,是否会真的拦截事件,取决于后者,
if(event.getAction() == MotionEvent.ACTION_UP){
sv_ceshi.requestDisallowInterceptTouchEvent(false);//允许父容器拦截事件功能
}else{
sv_ceshi.requestDisallowInterceptTouchEvent(true);//禁止父容器拦截事件的功能
}
return false;
}
});
一般来说当为ActionDown的时候,事件到达子元素ListView后,我们请求ScrollView不要在拦截这个事件。这时候父容器不再拦截事件。以后的ListView能够收到ActionMove的事件。正常将其消费掉就可以了。在ActionUp的时候,将其设置为允许父容器拦截事件。就一切正常。也不会干扰其它的事件。
这里如果将ScrollView的拦截方法onInterceptTouchEvent中的ActionDown事件就拦截掉。这样根本就不会执行到ListView的OnTouchEvent方法。子元素也就没有办法对父容器请求拦截事件。