1. 程式人生 > >Android---SwipeRefreshLayout巢狀ViewPager時的滑動衝突

Android---SwipeRefreshLayout巢狀ViewPager時的滑動衝突

SwipeRefreshLayout巢狀ViewPager

最近在專案中用到了SwipeRefreshLayout控制元件,以實現下拉重新整理,在我的SwipeRefreshLayout佈局中存在一個ViewPager。那麼問題就出現了,當我對ViewPager進行左右滑動時,只要你的滑動手勢有偏下,即往左下或者右下滑動時,會觸發SwipeRefreshLayout的下拉動作,導致不能正常對ViewPager進行滑動操作。

解決方法

對於Android的事件分發機制在這篇文章裡就不再贅述,之後有時間再寫篇文章針對事件分發進行闡述吧。那對於本文描述的這種情況,其實思路很簡單,即對使用者的滑動手勢進行判斷,下拉事件就交給SwipeRefreshLayout處理,左右滑動事件SwipeRefreshLayout就不進行攔截,直接下發到ViewPager進行處理。那怎麼區分下拉還是左右滑動呢,我用到的一個思路就是根據使用者移動手勢的X方向位移dx和Y方向位移dy進行判斷,如果dx>dy,那麼就認為是左右滑動,交給ViewPager,如果dy>dx,就認為是上下滑動,交給SwipeRefreshLayout並攔截(看原始碼你會發現SwipeRefreshLayout是繼承ViewGroup的,對事件進行攔截後就不會再下發到子View,具體流程在此不贅述)。

程式碼示例

下面的程式碼是我重寫的一個SwipeRefreshLayout,對使用者滑動手勢進行了判斷處理:

public class SunnySwipeRefreshLayout extends SwipeRefreshLayout {
    private float startX;
    private float startY;
    private boolean mIsXMove;// 是否橫向拖拽
    private final int mTouchSlop;// getScaledTouchSlop()得來的一個距離,表示滑動的時候,手勢移動要大於這個距離才開始移動控制元件,ViewPager就是用這個距離來判斷使用者是否翻頁

    public SunnySwipeRefreshLayout (Context context, AttributeSet attrs) {
        super(context, attrs);
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            mIsXMove = false;
            startX = ev.getX();
            startY = ev.getY();
            break;
        case MotionEvent.ACTION_MOVE:
            // 如果橫向移動則不攔截,直接return false;
            if (mIsXMove) {
                return false;
            }
            float endX = ev.getX();
            float endY = ev.getY();
            float distanceX = Math.abs(endX - startX);
            float distanceY = Math.abs(endY - startY);
            // 如果dx>xy,則認定為左右滑動,將事件交給viewPager處理,return false
            if (distanceX > mTouchSlop && distanceX > distanceY) {
                mIsXMove= true;
                return false;
            }
            break;
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            mIsXMove= false;
            break;
        }
        // 如果dy>dx,則認定為下拉事件,交給swipeRefreshLayout處理並攔截
        return super.onInterceptTouchEvent(ev);
    }
}

這麼處理之後,在這個下拉重新整理佈局中再巢狀ViewPager或是其他存在左後滑動操作的控制元件時,就不會再產生這種滑動衝突啦。 大家如果有更好的方案歡迎留言討論~