1. 程式人生 > >Android滑動衝突解決方法

Android滑動衝突解決方法

敘述

滑動衝突可以說是日常開發中比較常見的一類問題,也是比較讓人頭疼的一類問題,尤其是在使用第三方框架的時候,兩個原本完美的控制元件,組合在一起之後,忽然發現整個世界都不好了。

關於滑動衝突

滑動衝突分類###

滑動衝突,總的來說就是兩類。

  1. 同方向滑動衝突 比如ScrollView巢狀ListView,或者是ScrollView巢狀自己

  2. 不同方向滑動衝突 比如ScrollView巢狀ViewPager,或者是ViewPager巢狀ScrollView,這種情況其實很典型。現在大部分應用最外層都是ViewPager+Fragment 的底部切換(比如微信)結構,這種時候,就很容易出現滑動衝突。不過ViewPager裡面無論是巢狀ListView還是ScrollView,滑動衝突是沒有的,畢竟是官方的東西,可能已經考慮到了這些,所以比較完善。

複雜一點的滑動衝突,基本上就是這兩個衝突結合的結果。

滑動衝突解決思路###

滑動衝突,就其本質來說,兩個不同方向(或者是同方向)的View,其中有一個是占主導地位的,每次總是搶著去處理外界的滑動行為,這樣就導致一種很彆扭的使用者體驗,明明只是橫向的滑動了一下,縱向的列表卻在垂直方向發生了動作。就是說,這個占主導地位的View,每一次都身不由己的攔截了這個滑動的動作,因此,要解決滑動衝突,就是得明確告訴這個占主導地位的View,什麼時候你該攔截,什麼時候你不應該攔截,應該由下一層的View去處理這個滑動動作。

這裡不明白的同學,可以去了解一下Android Touch事件的分發機制

,這也是解決滑動衝突的核心知識。

第二種滑動衝突,解決起來是比較簡單的。這裡就結合例子說一下。

滑動衝突

這裡,說一下背景情況。之前做下拉重新整理、上拉載入更多時一直使用的是PullToRefreshView這個控制元件,因為很方便,不用匯入三方工程。在其內部可以放置ListView,GridView及ScrollView,非常方便,用起來可謂是屢試不爽。但是直到有一天,因專案需要,在ListView頂部加了一個輪播圖控制元件BannerView(這個可以參考之前寫的一篇學習筆記)。結果發現輪播圖滑動的時候,和縱向的下拉重新整理元件衝突了。

如之前所說,解決滑動衝突的關鍵,就是明確告知接收到Touch的View,是否需要攔截此次事件。

解決方法

這裡,相當於是PullToRefreshView嵌套了ViewPager,那麼每次優先接收到Touch事件的必然是PullToRefreshView。因為正常情況下,父控制元件會優先接收到touch事件。這樣就清楚了,看程式碼:

在PullToRefreshView的onInterceptTouchEvent方法中:

    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
        int y = (int) e.getRawY();
        int x = (int) e.getRawX();
        boolean resume = false;
        switch (e.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 發生down事件時,記錄y座標
                mLastMotionY = y;
                mLastMotionX = x;
                resume = false;
                break;
            case MotionEvent.ACTION_MOVE:
                // deltaY > 0 是向下運動,< 0是向上運動
                int deltaY = y - mLastMotionY;
                int deleaX = x - mLastMotionX;

                if (Math.abs(deleaX) > Math.abs(deltaY)) {
                    resume = false;
                } else {
                //當前正處於滑動
                    if (isRefreshViewScroll(deltaY)) {
                        resume = true;
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                break;
        }
        return resume;
    }

這裡最關鍵的程式碼就是這行

if (Math.abs(deleaX) > Math.abs(deltaY)) {
                    resume = false;
                }

橫向滑動距離大於縱向時,無須攔截這次滑動事件,滑動事件會傳遞到下一層的view,也就是這裡的輪播圖控制元件,這樣橫向滑動輪播圖的時候,PullToRefreshView就不會有下拉的動作了。其實,就是這麼簡單,但前提是你必須明確瞭解Android Touch事件的傳遞機制,期間各個方法執行的順序及意義。