当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
}
}