随笔 - 129,  文章 - 3,  评论 - 50,  阅读 - 15万

在项目中出现多重嵌套情况时,会出现无法滑动的场景,比如经常碰到的场景

ViewPager -> Fragment -> RecyclerView -> RecyclerView | ViewPager

最外层是一个可滑动的 tabLayout+ViewPager,ViewPager 中是多个 Fragment,Fragment 中是 RecyclerView,RecycleView 列表是一个多类型,内嵌多个横向滑动列表

此时内嵌的 ViewPager 或者 RecyclerView,只要是横向滑动,就会跟上层的 ViewPager 滑动起冲突,导致嵌套的列表无法滑动

常规做法就是拦截事件,或者直接在滑动监听中消费事件,但是这种做法只会让滑动失效情况有下面几种:

要么只能横向滑动,无法在这块区域上下滑动;

要么直接滑动失效,只有一个列表有滑动事件;

要么上下滑动或者左右滑动不连贯,内外列表滑动不丝滑;

如果直接拦截或者消费事件处理,这种处理过于粗暴,容易影响其它 View,并且就跟上面前两种情况一样,无法得兼。

 

要做到嵌套列表区域可以正常横向滑动,外层列表可以正常滑动,上下列表可以正常滑动,就需要额外处理了;

这里不能直接拦截,因为嵌套层次多深时,直接拦截会影响其它事件的处理;

这里主要使用 requestDisallowInterceptTouchEvent(在拦截事件前面执行) 方法在 onTouchEvent 中处理;

系统 View 以及一些好的第三方控件都会根据情况处理滑动监听,在 onTouchEvent 事件中做一些处理,所以在不拦截消费的前提下,又满足了自身需求,又不影响其它 View;

而子 View 如果需要告知父容器放开拦截,就可以使用 requestDisallowInterceptTouchEvent 方法,让父容器的拦截事件无法得到执行。

 

上层滑动列表存在各种滑动监听处理,会在触摸事件 onTouchEvent  中处理各种滑动逻辑,所以导致嵌套列表无法起作用,要处理滑动问题,需要在 onTouchEvent   事件中根据情况告知父容器,不做滑动处理

复制代码
class NestedRecyclerView(context: Context, attrs: AttributeSet) : RecyclerView(context, attrs) {

    private var x1 = 0f
    private var y1 = 0f
    private var isScroll = false

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(evt: MotionEvent?): Boolean {
        evt?.let {
            var str = ""
            when (evt.action) {
                MotionEvent.ACTION_DOWN -> {
                    x1 = evt.x
                    y1 = evt.y
                }
                MotionEvent.ACTION_MOVE -> if (this.childCount > 1) {
                    // 通知其父控件不拦截
                    val x2 = evt.x
                    val y2 = evt.y
                    if (x1 - x2 > 1) {
                        str = "向右滑"
                        parent.requestDisallowInterceptTouchEvent(true)
                        isScroll = false
                    } else if (x2 - x1 > 1) {
                        str = "向左滑"
                        parent.requestDisallowInterceptTouchEvent(true)
                        isScroll = false
                    } else if (y1 - y2 > 100) {
                        parent.requestDisallowInterceptTouchEvent(false)
                        str = "向上滑"
                        isScroll = true
                    } else if (y2 - y1 > 100) {
                        parent.requestDisallowInterceptTouchEvent(false)
                        str = "向下滑"
                        isScroll = true
                    }
                    XLog.e("--== $x1 $x2 $str")
                    return if (isScroll){
                        true
                    }else{
                        super.onTouchEvent(evt)
                    }
                }
                MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                    if (isScroll) {
                        parent.requestDisallowInterceptTouchEvent(false)
                    } else {
                        parent.requestDisallowInterceptTouchEvent(true)
                    }
                    return if (isScroll){
                        true
                    }else{
                        super.onTouchEvent(evt)
                    }
                }
            }
        }
        return super.onTouchEvent(evt)
    }

}
复制代码

主要集中在触摸事件,通过滑动的距离判断滑动方向,如果是左右滑动列表,则告诉父容器不做处理,交给本身,如果是上下滑动列表,交给父容器处理。

 

要想滑动比较丝滑,内外列表互补影响,还需要在up事件中处理一下。

同理,ViewPager 也是一样的处理,只需要继承 ViewPager 就行,触摸逻辑一样,处理后,就如丝般柔顺了。

 

posted on   翻滚的咸鱼  阅读(3191)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 【.NET】调用本地 Deepseek 模型
< 2025年2月 >
26 27 28 29 30 31 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 1
2 3 4 5 6 7 8

点击右上角即可分享
微信分享提示