View 事件体系之滑动冲突

1,661 阅读2分钟

滑动冲突三种情况

  • 外部滑动方式与内部滑动方式不一样.
  • 外部滑动方式与内部滑动方式一致.
  • 上面两种情况的嵌套.

滑动冲突处理原则

对于上面的第一种情况

记录上次记录点减去当前点得到deltaX,deltaY

  • 可以利用滑动路径和水平方向所形成的夹角来确定是那种滑动,如果小于45°,那自然就是横向,大于就是纵向.
  • 可以对比横向滑动距离和纵向滑动距离,那个大就是那个方向滑动距离大.

对于第二,三种情况

可以 根据业务写出处理规则, 比如当内部View滑动到顶部或者底部时响应外部View,我们就可以根据这个规则判断内部View有没有滑动到底, 如果有的话就不消费事件,没有的话就消费事件.具体怎么消费事件有两种方法.

外部拦截法

所有的事件都要经由decorView分发,所以我们可以在decorView处做文章
如果父View需要事件,就拦截事件;否则就不拦截事件.具体实现在onInterceptTouchEvent()中处理.

public boolean onInterceptTouchEvent(MotionEvent event){
    boolean interceptd = false;
    int x = (int) event.getX();
    int y = (int) event.getY();
    switch(event.getAction()){
        case MotionEvent.ACTION_DOWN:
            interceptd = false;
            break;
        case MotionEvent.ACTION_MOVE:
            if(父容器需要当前点击事件){
                interceptd = true;
            }else{
                interceptd = false;
            break;
        case MotionEvent.ACTION_UP:
            interceptd = false;
            break;
        default:
            break;
    mLastXIntercept = x;
    mLastYIntercept = y;
    return interceptd;

内部拦截法

父容器默认不拦截任何事件,所有事件都交由子元素,子元素不需要再requestDisallowInterceptTouchEvent(boolean)操控父元素处理,和上面的方法正好相反.

Called when a child does not want this parent and its ancestors to intercept touch events with onInterceptTouchEvent(MotionEvent).

disallowIntercept(boolean)
boolean: True if the child does not want the parent to intercept touch events.

public boolean dispatchTouchEvent(MotionEvent event){
    int x = (int) event.getX();
    int y = (int) event.getY();
    switch(event.getAction()){
        case MotionEvent.ACTION_DOWN:
            操控父元素不拦截ACTION_DOWN,因为ACTION_DOWN不受 ACTION_DISALLOW_INTERCEPT 标记控制,所以一旦父元素拦截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;

默认拦截除了ACTION_DOWN以外的事件.这样子元素调用requestDisallowInterceptTouchEvent(false)父元素才能继续拦截所需事件???

拦截方法对比

拦截方法 内部拦截法 外部拦截法
定义 父元素不拦截任何事件,所有事件都传给子元素 父元素需要就直接拦截,不需要就不拦截
重写 decorView的onInterceptTouchEvent() 子View的dispatchTouchEvent
默认 decorView不拦截ACTION_DOWN,不拦截ACTION_UP(1) 子元素默认拦截ACTION_DOWN;decorView不拦截ACTION_DOWN,拦截其他的(ACTION_MOVE,ACTION_UP)