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;
            }
        });
    }
相關文章
相關標籤/搜索