具体现象
水平的ViewPager2中嵌套使用垂直列表,当上下滑动列表到底部时,手指稍微斜一点就会触发ViewPager2的左右滑动,而列表未滑动到底部时,滑动正常。
问题分析
根据事件分发规则,内部的列表应该采用的是内部拦截法,即内部列表接收到滑动事件后,调用requestDisallowInterceptTouchEvent(true)
请求父控件不要拦截。这里使用的是RecyclerView,查看事件处理onTouchEvent
中源码,也确实是这样:
@Override
public boolean onTouchEvent(MotionEvent e) {
...
switch (action) {
case MotionEvent.ACTION_MOVE: {
...
if (scrollByInternal(
canScrollHorizontally ? dx : 0,
canScrollVertically ? dy : 0,
e, TYPE_TOUCH)) {
getParent().requestDisallowInterceptTouchEvent(true);
}
...
}
}
}
复制代码
反过来推,既然ViewPager2会误滑,也就意味着此时requestDisallowInterceptTouchEvent(true)
未执行,仔细看,该方法的触发是有条件的,即scrollByInternal
方法要返回true。
那么继续看scrollByInternal
源码,注释上写着
@return Whether any scroll was consumed in either direction.
当在该滑动方向上消费了距离,才会返回true。回过头来看现象,也很好解释了为什么滑动到底部容易误触ViewPager2滑动,此时RecyclerView没有滑动距离可消费滑动事件了。
解决方法
很简单,只要想办法消费滑动距离就行。这里自定义一个布局,包裹住RecyclerView,利用嵌套滑动机制消费掉剩余滚动距离。
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.FrameLayout
import androidx.core.view.NestedScrollingParent3
import androidx.core.view.ViewCompat
class TouchConsumerLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr), NestedScrollingParent3 {
override fun onNestedScrollAccepted(child: View, target: View, axes: Int, type: Int) {
}
override fun onStartNestedScroll(child: View, target: View, axes: Int, type: Int): Boolean {
return axes == ViewCompat.SCROLL_AXIS_VERTICAL
}
override fun onNestedPreScroll(target: View, dx: Int, dy: Int, consumed: IntArray, type: Int) {
}
override fun onNestedScroll(
target: View,
dxConsumed: Int,
dyConsumed: Int,
dxUnconsumed: Int,
dyUnconsumed: Int,
type: Int
) {
onNestedScroll(
target,
dxConsumed,
dyConsumed,
dxUnconsumed,
dyUnconsumed,
type,
intArrayOf(0, 0)
)
}
override fun onNestedScroll(
target: View,
dxConsumed: Int,
dyConsumed: Int,
dxUnconsumed: Int,
dyUnconsumed: Int,
type: Int,
consumed: IntArray
) {
//关键点,就这一行代码,消费所有剩余滚动
consumed[1] = dyUnconsumed
}
override fun onStopNestedScroll(target: View, type: Int) {
}
}
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END