安卓 自定义View的滑动事件冲突

关于安卓的事件分发机制,网上参考资料非常多,只有真正理解了事件分发的机制,才能很好的去处理自定义view过程中的事件冲突,事件分发机制参考资料,我认为讲的比较好

我的项目中当然也遇见了事件冲突


我项目中遇到的冲突.png

如图,项目中有一个横向滑动的自定义View,外面是RV,所以不处理肯定是这两个view要冲突的,看下面的gif图


分时图的自定义view.gif
事件冲突点

当我在分时的view中长按之后需要出来十字线,且十字线是需要左右滑动的,根据滑动的点显示当前的信息
冲突点很明显,就是当我左右滑动的时候,事件被最后那个子view(此处暂且就认为就是我的分时图的view)分发,最终给到了外层的RV控件,所以RV控件认为子view都不需要当前的事件,它就自己处理了,所以造成在左右滑动十字线的时候,RV上下滚动,分时View也就失去焦点,十字线就消失了

解决思路
  • 第一种,外部拦截法解决
    即父View根据需要对事件进行拦截。逻辑处理放在父View的onInterceptTouchEvent方法中。我们只需要重写父View的onInterceptTouchEvent方法,并根据逻辑需要做相应的拦截即可。
    上一下伪代码
public boolean onInterceptTouchEvent(MotionEvent event) {
        boolean intercept = 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: {
              //在此做x,y值的判断,根据需求来选择是否拦截事件
                if (满足父容器的拦截要求) {
                    intercept = true;
                } else {
                    intercept  = false;
                }
                break;
            }
            case MotionEvent.ACTION_UP: {
                intercepted = false;
                break;
            }
            default:
                break;
        }
      
        return intercepted;
    }

常见的外部拦截法就是通过判断event.getX()和event.getY()等距离,满足我们的滑动需求时,去让intercepted改变,从而达到父view不再向下传递事件,它自己消费掉,最终来解决事件冲突
但是这种方法要注意以下两点

  1. ACTION_DOWN 一定返回false,不要拦截它,否则根据View事件分发机制,后续ACTION_MOVE 与 ACTION_UP事件都将默认交给父View去处理!
  2. 原则上ACTION_UP也需要返回false,如果返回true,并且滑动事件交给子View处理,那么子View将接收不到ACTION_UP事件,子View的onClick事件也无法触发。而父View不一样,如果父View在ACTION_MOVE中开始拦截事件,那么后续ACTION_UP也将默认交给父View处理!
  • 第二种,内部拦截法
    即父View不拦截任何事件,所有事件都传递给子View,子View根据需要决定是自己消费事件还是给父View处理。这需要子View使用requestDisallowInterceptTouchEvent方法才能正常工作。
public boolean dispatchTouchEvent(MotionEvent event){
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
              
                break;
            }
            case MotionEvent.ACTION_MOVE: {
       
                if (父容器需要此类点击事件) {
                    getParent().requestDisallowInterceptTouchEvent(false);
                }
                break;
            }
            case MotionEvent.ACTION_UP: {
                break;
            }
            default:
                break;
        }
     
        return super.dispatchTouchEvent(event);
    }

这种方法同样要注意

  1. 内部拦截法要求父View不能拦截ACTION_DOWN事件,由于ACTION_DOWN不受FLAG_DISALLOW_INTERCEPT标志位控制,一旦父容器拦截ACTION_DOWN那么所有的事件都不会传递给子View。
  2. 滑动策略的逻辑放在子View的dispatchTouchEvent方法的ACTION_MOVE中,如果父容器需要获取点击事件则调用 getParent().requestDisallowInterceptTouchEvent(false)方法,让父容器去拦截事件。
综上

很显然,我需要针对自定义的View使用第二种方法,因为我不可能去更改RV的代码,或者是自定义一个RV,当长按时,自定义的子view消费掉滑动事件,不再传递给父view
思路就是这么个思路,在分时图的View中做如下操作

 @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                if (isLongPress) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                break;
            }
            case MotionEvent.ACTION_UP: {
                break;
            }
            default:
                break;
        }
        return super.dispatchTouchEvent(event);
    }
最终结果.gif

你可能感兴趣的:(安卓 自定义View的滑动事件冲突)