天天看點

解決ViewPager與SwipeRefreshLayout滑動沖突

當SwipeRefreshLayout嵌套ViewPager的時候,滑動的時候很不靈敏,而且在左右滑動的往往都會觸發SwipeRefreshLayout的重新整理操作,是以我們需要自定義ViewPager和SwipeRefreshLayout,采用内部攔截法來解決滑動沖突,内部攔截法是指父容器不攔截任何事件,所有的事件都會傳遞給子元素,如果子元素需要此事件就直接消耗掉,否則就交給父容器進行處理。

首先需要重寫子元素的dispatchTouchEvent方法,在ACTION_DOWN事件的時候,請求SwipeRefreshLayout不要攔截,隻有在ACTION_MOVE事件的時候,并且判斷是垂直滑動的話,才請求SwipeRefreshLayout攔截。

class MyViewPager : ViewPager {

    private var startX = 0f
    private var startY = 0f
    private var moveX = 0f
    private var moveY = 0f
    private var deltaX = 0f
    private var deltaY = 0f

    constructor(context: Context) : super(context)

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)

    override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
        when (ev.action) {
            MotionEvent.ACTION_DOWN -> {
                startX = ev.x
                startY = ev.y
                ViewCompat.setNestedScrollingEnabled(this,true)
                parent.requestDisallowInterceptTouchEvent(true)
            }
            MotionEvent.ACTION_MOVE -> {
                moveX = ev.x
                moveY = ev.y
                deltaX = abs(moveX - startX);
                deltaY = abs(moveY - startY);
                if (deltaX < deltaY) {
                    parent.requestDisallowInterceptTouchEvent(false)
                }
            }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
            }
        }
        return super.dispatchTouchEvent(ev)
    }
}
           

其次,父容器不能攔截ACTION_DOWN事件,因為ACTION_DOWN事件并不受FLAG_DISALLOW_INTERCEPT這個标記位的控制,是以一旦父容器攔截了ACTION_DOWN事件,所有的事件都無法傳遞到子元素中去了,這樣内部攔截就無法起作用了。

class MyRefreshLayout : SwipeRefreshLayout {
    constructor(context: Context) : super(context)

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)

    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        if (ev.action == MotionEvent.ACTION_DOWN) {
            super.onInterceptTouchEvent(ev)
            return false
        }
        return true
    }
}