我们在开发中经常会碰到view滑动冲突的情况。
滑动冲突的解决办法就两种:
1.外部拦截法:是指在点击事件先经过父容器的拦截处理,如果父容器需要处理此事件就进行拦截,如果不需要此事件就不拦截,这样就可以解决滑动冲突的问题,外部拦截法需要重写父容器的onInterceptTouchEvent()方法,在内部做相应拦截处理。
2.内部拦截法:是指点击事件先经过子View处理,如果子view需要次事件就直接消耗掉,否则就交给父容器进行处理,这样就可以解决滑动冲突的问题。内部拦截需要配合requestDisallowInterceptTouchEvent()方法,来确定子view是否允许父容器拦截事件。
a.允许父容器进行事件拦截
getParent().requestDisallowInterceptTouchEvent(false)
b.禁止父容器进行事件拦截
getParent().requestDisallowInterceptTouchEvent(true)
以上是解决滑动冲突的方法,下面我们回到我们今天解决的正题ScrollView嵌套viewPager中嵌套listView滑动事件冲突
这里可以使用第一种解决方案:外部拦截法
只需要自定义一个ScrollView,然后重写它的onInterceptTouchEvent()即可,代码如下:
public class VerticalScrollView extends ScrollView { private float xDistance, yDistance, xLast, yLast; public VerticalScrollView(Context context) { super(context); } public VerticalScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public VerticalScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: xDistance = yDistance = 0f; xLast = ev.getX(); yLast = ev.getY(); break; case MotionEvent.ACTION_MOVE: final float curX = ev.getX(); final float curY = ev.getY(); xDistance += Math.abs(curX - xLast); yDistance += Math.abs(curY - yLast); xLast = curX; yLast = curY; if (xDistance > yDistance) { return false; } } return super.onInterceptTouchEvent(ev); } }
第二种解决方案如下:不需要自定义控件
1.第一步:允许父容器进行事件拦截
scrollView.setOnScrollToBottomLintener(new BottomScrollView.OnScrollToBottomListener() { @Override public void onScrollBottomListener(boolean isBottom) { if (isBottom) { scrollView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_MOVE: //解决滑动冲突 scrollView.getParent().requestDisallowInterceptTouchEvent(false); break; } return false; } }); } } });
2.第二步:父元素也要默认拦截除了ACTION_DOWN以外的其他事件,这样当子元素调用
scrollView.getParent().requestDisallowInterceptTouchEvent(false)
方法时,父元素才能继续拦截所需的事件。
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { int action = ev.getAction(); return action != MotionEvent.ACTION_DOWN; }
3.为什么父容器不能拦截ACTION_DOWN事件呢?
因为ACTION_DOWN事件并不受FLAG_DISALLOW_INTERCEPT这个标记位的控制,所以一旦父容器拦截ACTION_DOWN事件,那么所有的事件都无法传递到子元素中去,这样内部拦截就无法起作用了。