Android滑动冲突解决方案

 在App中,可以滑动的控件随处可见(viewPager,listView),毕竟在移动设备上,屏幕就那么大,要想给用户呈现更多的内容,我们的view就得支持滑动了,多个可以滑动的View嵌套在一起,就会引发一个问题,那就是一个事件(MotionEvent)到来时,到底哪一个view响应呢?这时应用的选择困难症犯病了,它会变得抓狂,我们的应用就会失常,这时我们要做的就是帮助它做出选择。废话不多说,下面我们就分析一下不同场景下滑动冲突的解决方案(全是套路啊)。
滑动冲突是View体系中一个深入的话题,理解滑动冲突的解决方法,需要对view的事件分发体制有一定的了解。
  • 滑动冲突场景

    滑动嘛,不是左右滑动,就是上下滑动,冲突场景无非就是这两种情况的组合,最常见的冲突场景主要有以下三种:
    
    (1)内外两层view滑动方向不同
    

    这种冲突场景也是最简单的一种了,在这里,外层view为水平方向的scrollView,内层view为listview。下面就用“外部拦截法”和“内部拦截法”分别对这种情景进行分析。
    
    ① 外部拦截法:
    
    可想而知,这种方法是对外部view做处理,内部view不需做处理。由外部view来决定事件是否传递到内部view(当然,事件得能传递到外部view),如果外部view决定消耗此事件,则外部view进行事件拦截,反之,外部view不拦截事件,将事件传递到内部view进行处理。
    需要说一下,处理滑动冲突的策略并不是唯一的,只要可以解决问题即可,对于此种冲突场景,制定如下处理策略:
    我们可以计算滑动在x,y方向上的偏移量(当然也可以通过滑动角度),如果abs(dx)>abs(dy),说明用户左右滑动的趋势较为明显,外层view拦截并消耗事件,若abs(dx)<abs(dy),说明用户上下滑动的趋势较为明显,外层view不拦截事件,由内层view处理事件。
    我们需要实现外层view的InterceptTouchEvent(MotionEvent ev),来决定是否拦截事件。
    
    float latestX;
    float latestY;

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean isIntercept = false;

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                isIntercept = false;
                break;
            case MotionEvent.ACTION_MOVE:
                float dx = Math.abs(ev.getRawX() - latestX);
                float dy = Math.abs(ev.getRawY() - latestY);
                if (dx > dy) {
                    isIntercept = true;
                } else {
                    isIntercept = false;
                }
                break;
            case MotionEvent.ACTION_UP:
                isIntercept = false;
                break;
        }
        latestX = ev.getRawX();
        latestY = ev.getRawY();
        return isIntercept;
    }
       ② 内部拦截法:

   外层view默认不拦截,由内层view决定事件是由自己消耗掉还是推给外层view处理,这种方法外层view和内层view都需要做处理。外层view需要实现onInterceptTouchEvent(MotionEvent ev),内层view需要实现dispatchTouchEvent(MotionEvent ev)。

   外层view:
@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean isIntercept = false;
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            isIntercept = false;
        } else {
            isIntercept = true;
        }
        return isIntercept;
    }
   内层view:
float latestX;
    float latestY;

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_MOVE:
                float dx = Math.abs(ev.getRawX() - latestX);
                float dy = Math.abs(ev.getRawY() - latestY);
                if (dx > dy) {
                    getParent().requestDisallowInterceptTouchEvent(false);
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
            latestX = ev.getRawX();
            latestY = ev.getRawY();
            return super.dispatchTouchEvent(ev);
        }
      (2)内外两层view滑动方向相同

处理这种冲突,就需要视业务需求而看了,我们的业务需求决定什么条件下让外层view消耗事件,什么条件下让内层view消耗事件,核心思想与上面的情景很类似,就是决定条件发生变化而已,这里就不重复了。

     (3)上面两种的组合

这种多重嵌套的情景就更复杂了,但是,归根结底,其处理思想与第一种相似,就需要看具体业务需求了。

猜你喜欢

转载自blog.csdn.net/qq_15692039/article/details/61426349