View的滑动冲突
产生:界面中存在内外两层同时可以滑动的View
滑动冲突的场景
- 场景一:外部滑动方向和内部滑动方向不一致
- 场景二:外部滑动方向和内部滑动方向一致
- 场景三:上面两种情况的嵌套
滑动冲突的处理规则
- 根据滑动是水平滑动还是竖直滑动来判断到底由谁来拦截事件(适用场景一)
- 当处于某种状态时需要外部View响应用户的滑动,而处于另外一种状态时则需要内部View来响应View的滑动(适用场景二和场景三)
滑动冲突的解决方式
外部拦截法和内部拦截法
外部拦截法:事件都先经过父容器的拦截处理,如果父容器需要此事件就拦截,如果不需要就不拦截。外部拦截法需要重写父容器的onInterceptTouchEvent方法
//分别记录上次滑动的坐标(onInterceptTouchEvent) private int mLastXIntercept = 0; private int mLastYIntercept = 0; public boolean onInterceptTouchEvent(MotionEvent event){ boolean intercepted = false; int x = (int)event.getX(); int y = (int)event.getY(); switch(event.getAction()){ case MotionEvent.ACTION_DOWN: intercepted = false; break; case MotionEvent.ACTION_MOVE: int deltaX = x - mLastXIntercept; int deltaY = y - mLastYIntercept; if(父容器需要当前点击事件){ intercepted = true; }else{ intercepted = false; } break; case MotionEvent.ACTION_UP: intercepted = false; break; default: break; } mLastXIntercept = x; mLastYIntercept = y; return intercepted; }
上述代码是外部拦截法的典型逻辑,针对不同的滑动冲突只需修改“父容器需要当前点击事件”这个条件即可
内部拦截法:指父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就消费,否则就交由父容器进行处理。这种方法需要配合requestDisallowInterceptTouchEvent方法才能正常工作
//分别记录上次滑动的坐标 private int mLastX = 0; private int mLastY = 0; public boolean dispatchTouchEvent(MotionEvent event){ int x = (int)event.getX(); int y = (int)event.getY(); switch(event.getAction()){ case MotionEvent.ACTION_DOWN: parent.requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_MOVE: int deltaX = x - mLastX; int deltaY = y - mLastY; if(父容器需要此类点击事件){ parent.requestDisallowInterceptTouchEvent(false); } break; case MotionEvent.ACTION_UP: break; default: break; } mLastX = x; mLastY = y; return super.dispatchTouchEvent(event); }
上述代码是内部拦截法的典型代码,面对不同的滑动策略时只需要更改“父容器需要此类点击事件”这个条件即可
父元素所做的修改如下
public boolean onInterceptTouchEvent(MotionEvent event){ int action = event.getAction(); if(action == MotionEvent.ACTION_DOWN){ return false; }else{ return true; } }