【Android】Android滑动冲突解决方案

场景

1731560214120

外部滑动方向和内部不同

左右滑动让父容器的View拦截点击事件来处理,上下滑动让内部的View拦截点击事件来处理

可以根据滑动的距离差,滑动速度或者角度来进行判断

解决套路

外部拦截法

重写父容器的事件拦截方法

如果此时想要横向滑动,父容器拦截该事件,在onTouchEvent方法处理;想要竖向滑动,父容器就不拦截,交给子元素处理

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: {
   
            if (父容器需要此类点击事件) {
   
                intercepted = true;
            } else {
   
                intercepted = false;
            }
            break;
        }
        case MotionEvent.ACTION_UP: {
   
            intercepted = false;
            break;
        }
        default:
            break;
    }
    mLastXIntercept = x;
    mLastYIntercept = y;
    return intercepted;
}

注意:

  1. down不要拦截,一旦拦截后续都会交给父容器处理,那样如果不符合就无法交给子元素处理了

内部拦截法

重写子容器的事件分发方法和父容器的事件拦截方法

父容器不再拦截任何事件,都交给子元素处理。那么在子元素的dispatchTouchEvent方法中,如何当前想要竖向滑动就直接讲此事件消耗掉。如果是横向滑动,就调用requestDisallowInterceptTouchEvent方法,

下面是子元素的dispatchTouchEvent方法


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);
}

同时父View需要重写onInterceptTouchEvent方法:

public boolean onInterceptTouchEvent(MotionEvent event) {
   
    int action = event.getAction(); 
    if (action == MotionEvent.ACTION_DOWN) {
   
        return false;
    } else {
   
        return true;
    }
}

注意:为何ACTION_DOWN要返回false?

@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
   

    if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
   
        return;
    }

    // 传入true,设置该标志,否则清楚该标志
    if (disallowIntercept) {
   
        mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
    } else {
   
        mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
    }

    // 然后调用父视图的该方法
    if (mParent != null) {
   
        mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
    }
}

在ViewGroup的dispatchTouchEvent方法中

if (actionMasked == MotionEvent.ACTION_DOWN
    || mFirstTouchTarget != null) {
   
    // requestDisallowInterceptTouchEvent设置的true,导致这里的disallowIntercept为false
    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    if (!disallowIntercept) {
   
        // 本来设置的true,不让父容器拦截,但这里却进入了
        intercepted = onInterceptTouchEvent(ev);
        // 所以我们重写onInterceptTouchEvent方法让他返回false,其他为false
        ev.setAction
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值