解决ViewPager2中滑动列表时,容易误滑ViewPager2的问题

具体现象

水平的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
喜欢就支持一下吧
点赞0 分享