如图所示,父布局是一个ScrollView,内部一个子View-SelectView是一个自定义的水平滑动的View,当手指放在SelectView上左右滑动时,会与ScrollView的上下滑动事件产生冲突。平时我们也会碰到类似的这种ScrollView与内部控件的滑动冲突问题,比如嵌套ListView等等,先来说说如何解决吧
SelectView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
//允许ScrollView拦截点击事件,ScrollView可滑动
ScrollView.requestDisallowInterceptTouchEvent(false);
//SelectView.getParent().requestDisallowInterceptTouchEvent(false)//这种写法亦可
} else {
//不允许ScrollView拦截点击事件,点击事件由子View处理
ScrollView.requestDisallowInterceptTouchEvent(true);
//SelectView.getParent().requestDisallowInterceptTouchEvent(true)//这种写法亦可
}
return false;
}
});
注:其中SelectView是子View的控件id,ScrollView是父控件View的控件id
归根到底发生冲突的原因是View的事件分发机制,本来应该由子View-SelectView处理的事件被父控件ScrollView处理了,知道原因我们就可以着手解决了。在ScrollView或者说ViewGroup中提供了这样一个方法requestDisallowInterceptTouchEvent(boolean disallowIntercept),翻译成中文就是请求不允许拦截点击事件,我们来看下源码:
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
if (disallowIntercept) {
recycleVelocityTracker();
}
super.requestDisallowInterceptTouchEvent(disallowIntercept);
}
内部调用了recycleVelocityTracker()方法,我们继续来看源码
private void recycleVelocityTracker() {
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
recycleVelocityTracker()内部调用了recycle()方法,我们继续来看源码
/**
* Return a VelocityTracker object back to be re-used by others. You must
* not touch the object after calling this function.
*/
public void recycle() {
if (mStrategy == null) {
clear();
sPool.release(this);
}
}
看到这一层的官方注释相信大家应该明白了,翻译过来的意思就是:返回一个VelocityTracker对象,让其他人重新使用它,在调用这个函数之后,你不能再碰这个对象。通俗点说就是:VelocityTracker对象回收后代表你不需要使用了,系统将此对象重新分配到其他请求者。反过来我们再看文章开头的解决方法大家应该都能明白了。