ScrollView、SwipeRefreshLayout、ListView、RecyclerView等控件解决滑动冲突

        ------因为种种缘由,本文废话较多,代码较少,可根据需求断定是否须要深刻阅读。html

(1)需求场景

ScrollView里面放了具有横向滑动的东西,进度条啊、横向的列表啊、banner等ide

ScrollView里面放好几层的LIstView、RecyclerView(其实很是不推荐这样作)布局

各类纵向横向的带滑动的东西交叉使用spa

(2)解决方案

滑动、拖动的权限原本是你们都有的,把它私有化就行了,具体的私有化过程就是不给ScrollView滑动,不给LIstView下滑,不给Seekbar拖动,差很少就是这个意思。code

再详细一点,就是吧具体的相关的某些控件的onTouchEven事件给屏蔽了,核心在这个方法:
requestDisallowInterceptTouchEvent()htm

onTouchEven这个方法还有两个小伙伴须要了解一下(dispatchTouchEvent、OnInterceptTouchEven),他们之间的故事仍是挺精彩的,也很少介绍,能够去看下别人的博客,这里有个链接:https://www.cnblogs.com/neil-zhao/p/3778094.htmlblog

其实大概意思就是,dispatchTouchEvent是有限执行,且用requestDisallowInterceptTouchEvent是不能屏蔽掉的,onTouchEven则是这些可拖动可下滑的控件滑动的关键,因此只要吧父容器的onTouchEven屏蔽掉的话,就能达到横向滑动的时候禁止纵向滑动的效果。事件

须要作的就是重写控件的onTouchEven方法,在按下的时候或者是滑动超出某个范围的时候禁止列表下滑,能够看下简单的代码:get

/**
     * 设置须要屏蔽触摸事件的控件(横向移动幅度大于60像素的时候会促发屏蔽效果)
     * @param view
     */
    public void stopTouchShield(final View view){
        view.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int action = event.getAction();
                if (action == MotionEvent.ACTION_DOWN) {
                    // 记录点击到ViewPager时候,手指的X坐标
                    lastX = event.getX();
                }
                if (action == MotionEvent.ACTION_MOVE) {
                    float x=event.getX()-lastX;
                    if(x<0){
                        x*=-1;
                    }
                    if(x>4f){
                        recyclerView.requestDisallowInterceptTouchEvent(true);
                    }
                }
                if (action == MotionEvent.ACTION_UP) {
                    // 用户抬起手指,恢复父布局状态
                    recyclerView.requestDisallowInterceptTouchEvent(false);
                }
                return false;
            }
        });
    }

(3)这事还有坑

我用了不少下拉刷新的组件,不少用的都不太舒服,滑动回弹的效果不是我喜欢的,因此本身手撸了一个下拉刷新的,还有一个ScrollView,写这两个东西的时候,若是是重写控件的onTouchEven方法的时候,老是会出现莫名其妙的拖不动或者是回弹出错的毛病,因此重写的时候基本上不懂onTouchEven方法,而是改成重写dispatchTouchEvent,而后就一路顺风了。源码

没有去深刻研究Android的源码,有个研究过的大神在一篇博客里面说过,若是requestDisallowInterceptTouchEvent不生效的话,就是跟dispatchTouchEvent有关系,dispatchTouchEvent在搞某种操做的时候会把requestDisallowInterceptTouchEvent设置的参数重置,大概是这个意思,具体描述我也找不到那篇博客了,反正能够肯定的是,若是requestDisallowInterceptTouchEvent不生效,那就是跟dispatchTouchEvent有关系。

经过我手撸下拉刷新的经验,能够重写ScrollView、RecyclerView等控件的dispatchTouchEvent方法,在须要屏蔽掉下滑操做的时候,屏蔽掉super.dispatchTouchEvent操做,而后再结合requestDisallowInterceptTouchEvent来使用,应该没问题。

我这里的写法就是,重写的时候提供屏蔽的方法,而后多加一个boolean变量用于标识是否须要屏蔽掉dispatchTouchEvent,而后在重写的dispatchTouchEvent的时候加上这个boolean变量断定是否须要执行后续的操做。

习惯性贴一下代码,不保证适用:

@Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        if(!isTouchShield){
            //不屏蔽滑动事件
            return super.dispatchTouchEvent(event);
        }
    }


    /**
     * 设置须要屏蔽触摸事件的控件(横向移动幅度大于60像素的时候会促发屏蔽效果)
     * @param view
     */
    public void stopTouchShield(final View view){
        view.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int action = event.getAction();
                if (action == MotionEvent.ACTION_DOWN) {
                    // 记录点击到ViewPager时候,手指的X坐标
                    lastX = event.getX();
                }
                if (action == MotionEvent.ACTION_MOVE) {
                    float x=event.getX()-lastX;
                    if(x<0){
                        x*=-1;
                    }
                    if(x>4f){
                        recyclerView.requestDisallowInterceptTouchEvent(true);
                        isTouchShield(true);
                    }
                }
                if (action == MotionEvent.ACTION_UP) {
                    // 用户抬起手指,恢复父布局状态
                    recyclerView.requestDisallowInterceptTouchEvent(false);
                    isTouchShield(false);
                }
                return false;
            }
        });
    }
相关文章
相关标签/搜索