多个View嵌套使用既可能出现滑动冲突问题,实际上属于事件拦截与分发问题; 补充: ViewGroup事件三个重要方法:onInterceptTouchEvent(),onTouchEvent(),dispatchTouchEvent(); View事件两个重要方法:onTouchEvent(),dispatchTouchEvent() 常见场景有3种: 场景1.内部View和外部View滑动方向不一致:通过滑动轨迹的方向就可以判断出当前应该分发给谁。 场景2.内部View和外部View滑动方向一致:要结合业务逻辑状态判断出当前应该分发给谁。 场景3.多层View嵌套,既有内部外部滑动方向不一致的情况,也有滑动方向一致的情况:将以上两种方法结合起来。 滑动冲突的处理规则:规则1:当用户上下滑动的时候,需要让外部的View拦截事件,当用户左右滑动的时候,需要让内部的View拦截事件。 规则2:通常需要内部View响应View的滑动。 解决方法: 方法1:外部拦截法指事件都经过父容器(父布局)的拦截处理,如果父容器(父布局)需要此事件就拦截,如果子View需要此事件就不拦截。外部拦截法需要重写父容器的onInterceptTouchEvent()方法. 注意: onInterceptTouchEvent()方法中, ACTION_DOWN事件父容器必须返回false,这样事件才会传递到子元素; ACTION_MOVE事件根据具体需求判断是否拦截,如果父容器需要拦截就返回true,否则返回false; 如果父容器在ACTION_UP时返回true,则子元素无法收到UP事件导致onClick事件失效。


父容器(父布局)

int lastInterceptX;
int lastInterceptY;
 
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    boolean intercepted = false;
    int x = (int) ev.getX();
    int y = (int) ev.getY();
    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            intercepted = false;
            break;
        case MotionEvent.ACTION_MOVE:
            if (/*父容器需要拦截此事件*/) {
                intercepted = true;
            } else {
                intercepted = false;
            }
            break;
        case MotionEvent.ACTION_UP:
            intercepted = false;
            break;
    }
 
    lastInterceptX = x;
    lastInterceptY = y;
    return intercepted;
}


方法2:内部拦截法指父容器(父布局)不拦截事件,所有的事件都传递到子元素(子布局),如果子元素(子布局)需要此事件就直接消耗,否则使用requestDisallowInterceptTouchEvent()通知父容器(父布局)处理。 注意: 子元素(子布局)dispatchTouchEvent()必须在ACTION_DOWN事件调用requestDisallowInterceptTouchEvent(true), 这样才能保证子元素(子布局)能收到ACTION_MOVE事件,在move事件做逻辑操作。 父容器的onInterceptTouchEvent()方法里的ACTION_DOWN事件不能拦截,这样子元素才能收到事件。


子元素(子布局)

int lastX;
int lastY;
@Override
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 dx = x - lastX;
            int dy = y - lastY;
            if (/*父容器需要此事件*/) {
                parent.requestDisallowInterceptTouchEvent(false);
            } else {
                parent.requestDisallowInterceptTouchEvent(true);
            }
            break;
        case MotionEvent.ACTION_UP:
            break;
    }
 
    lastX = x;
    lastY = y;
    return super.dispatchTouchEvent(event);
}