在一些特殊界面的设计里面,使用ScrollView嵌套ListView的情况还是比较常见的。由于ScrollView和ListView都有对滑动事件的监听和处理,所以两者嵌套肯定会存在滑动冲突的问题。我们知道Android事件是根据“冒泡”机制层层传递的,既从父View到子View层层处理,因此我们思考从ListView的“View父”ScrollView进行冲突处理。
从网上查阅资料和对ScrollView的源码分析,找到了ViewGroup里面有个方法requestDisallowInterceptTouchEvent,通过此方法可以阻止父层的View截获touch事件。也就是当我们设置ScrollView.requestDisallowInterceptTouchEvent(true),ScrollView不会拦截touch事件,并将其交由ListView处理,这正是我们想要的。
关键代码在下面......
public class ScrollViewInnerListView extends ListView{
……
private float mPrevMotionY;
private ScrollView scrollView;
public void setScrollView(ScrollView scrollView){
this.scrollView = scrollView;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
//交由子ListView处理,父scrollview不能滑动
setScrollAble(true);
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
//由父ScrollView处理onTouch
setScrollAble(false);
break;
default:
break;
}
return super.onInterceptTouchEvent(ev);
}
/**
* 处理父scrollview的requestDisallowInterceptTouchEvent()
*
* @param flag
*/
private void setScrollAble(boolean flag) {
if(scrollView != null){
scrollView.requestDisallowInterceptTouchEvent(flag);
}
}
/**
* 当ListView滑动到最顶部时,交回由ScrollView滑动;
* 当ListView滑动到最底部时,交回由ScrollView滑动。
*
* @param ev
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
final float y = ev.getY();
if (action == MotionEvent.ACTION_DOWN) {
mPrevMotionY = y;
} else if (action == MotionEvent.ACTION_MOVE) {
float dy = y - mPrevMotionY;
mPrevMotionY = y;
if(dy > 0){
View firstChild = this.getChildAt(0);
if(firstChild == null || (firstChild != null && (this.getFirstVisiblePosition() == 0 && firstChild.getTop() == 0))){
setScrollAble(true);
}
}else if(dy < 0){
View lastChild = this.getChildAt(this.getChildCount() - 1);
if(lastChild == null || (lastChild != null && (lastChild.getBottom() <= this.getHeight()))){
setScrollAble(true);
}
}
}
return super.dispatchTouchEvent(ev);
}
……
}