前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >RecyclerView嵌套滑动置顶 项目篇

RecyclerView嵌套滑动置顶 项目篇

作者头像
Jingbin
发布于 2021-03-24 06:33:10
发布于 2021-03-24 06:33:10
1.3K00
代码可运行
举报
文章被收录于专栏:Android 技术栈Android 技术栈
运行总次数:0
代码可运行

都2021了,RecyclerView嵌套滑动置顶应该已经被说烂了吧,但是如果项目中真的需要一个这样的结构应用到首页,想找到一个成熟的方案并不容易。这篇文章给出的是已稳定运行大半年的嵌套滑动代码。

代码地址:ByRecyclerView / stickyrv

效果图

体验地址

fir.im下载

项目来源

半年前接到的任务需要将首页改为天猫或京东的一样,现在似乎滑动置顶都是标配了,之前在网上看到那么多这类似的文章,找找应该不难,结果我几乎找遍了所有的文章与项目基本都不能使用,有卡顿的bug,问作者有没有应用到项目中也没有回应。

本来想像以前的滑动置顶使用CoordinatorLayout+TabLayout+RecyclerView的形式处理,但是感觉欠妥,用uiautomatorviewer分析了天猫/京东/网易考拉所有App的首页都是使用的RecyclerView-ViewPager-RecyclerView的形式,然后继续寻找时发现了一个最接近的项目 xmuSistone/PersistentRecyclerView。这应该是我找过的最完善的代码,然后应用到实际项目中发现还是有问题:

  • 1.在华为设备上滑动子RecyclerView时会有跳动
  • 2.父RecyclerView下拉刷新使用的是SmartRefreshLayout有issues反应有卡顿
  • 3.子RecyclerView加载更多需要处理
  • 4.子RecyclerView嵌套横向的RecyclerView滑动冲突问题
  • 5.Android4.4惯性滑动崩溃问题

当然轮子不可能完美贴合项目的需求,于是在上面修改了部分代码。

完善

1.在华为设备上滑动子RecyclerView时会有跳动

这个问题我在好几个作者写的滑动置顶代码那里都发现了,其他手机都是没问题的,原因是华为设备灵敏度很高,在手指放在ChildRecyclerView时很容易触发parent.requestDisallowInterceptTouchEvent(false)将事件丢给ParentRecyclerView,然后导致卡顿。处理方式是在dispatchTouchEvent里如果垂直滑动的距离超过24f才丢给ParentRecyclerView。 具体代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
override fun dispatchTouchEvent(e: MotionEvent): Boolean {
    val x = e.rawX
    val y = e.rawY
    when (e.action) {
        MotionEvent.ACTION_DOWN -> {
            //将按下时的坐标存储
            downX = x
            downY = y
            // true 表示让ParentRecyclerView不要拦截
            parent.requestDisallowInterceptTouchEvent(true)
        }
        MotionEvent.ACTION_MOVE -> {
            //获取到距离差
            val dx: Float = x - downX
            val dy: Float = y - downY
            // 通过距离差判断方向
            val orientation = getOrientation(dx, dy)
            val location = intArrayOf(0, 0)
            getLocationOnScreen(location)
            when (orientation) {
                "d" -> if (canScrollVertically(-1)) {
                    // 可以向下滑动时让ParentRecyclerView不要拦截
                    parent.requestDisallowInterceptTouchEvent(true)
                } else { //内层RecyclerView下拉到最顶部时
                    if(dy < 24f){
                        // 如果滑动的距离小于这个值依然让Parent不拦截
                        parent.requestDisallowInterceptTouchEvent(true)
                    }else{
                        // 将滑动事件抛给Parent,这样可以随着Parent一起滑动
                        parent.requestDisallowInterceptTouchEvent(false)
                    }
                }
                "u" -> {
                    // 向上滑动时,始终由ChildRecyclerView处理
                    parent.requestDisallowInterceptTouchEvent(true)
                }
            }
        }
    }
    return super.dispatchTouchEvent(e)
}

private fun getOrientation(dx: Float, dy: Float): String {
    return if (Math.abs(dx) > Math.abs(dy)) {
        //X轴移动
        if (dx > 0) "r" else "l" //右,左
    } else {
        //Y轴移动
        if (dy > 0) "d" else "u" //下//上
    }
}

2.解决下拉刷新/上拉加载问题(问题2/3)

由于之前花了一番功夫写了 ByRecyclerView,支持下拉刷新和上拉加载,其本质上就是Adapter上加一个特殊的viewType来处理,所带来的兼容性也好很多,于是将BaseRecyclerView继承ByRecyclerView后就达到了要求,给ParentRecyclerViewChildRecyclerView分别加下拉刷新和上拉加载的监听就好了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
parentRecyclerView.setOnRefreshListener { }
childRecyclerView.setOnLoadMoreListener { }

3.子RecyclerView里的item嵌套横向的RecyclerView滑动冲突问题

这部分相对于嵌套置顶的处理要简单多了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
override fun dispatchTouchEvent(e: MotionEvent): Boolean {
    val x = e.rawX
    val y = e.rawY
    when (e.action) {
        MotionEvent.ACTION_DOWN -> {
            downX = x
            downY = y
        }
        MotionEvent.ACTION_MOVE -> {
            // 获取到距离差
            val dx: Float = x - downX
            val dy: Float = y - downY
            // 通过距离差判断方向
            val orientation = getOrientation(dx, dy)
            val location = intArrayOf(0, 0)
            getLocationOnScreen(location)
            when (orientation) {
                // 上下滑动时抛给ChildRecyclerView处理
                "d" -> parent.requestDisallowInterceptTouchEvent(false)
                "u" -> parent.requestDisallowInterceptTouchEvent(false)
                "r" -> {
                    if (canScrollVertically(-1)) {
                        // 可以向右滑动时,自己处理,可以内部左右滑
                        parent.requestDisallowInterceptTouchEvent(true)
                    } else {
                        // 右滑动到顶时,交给parent处理,使其可以滑到ViewPager下一个的position
                        parent.requestDisallowInterceptTouchEvent(false)
                    }
                }
                "l" -> {
                    if (canScrollVertically(-1)) {
                        parent.requestDisallowInterceptTouchEvent(true)
                    } else {
                        parent.requestDisallowInterceptTouchEvent(false)
                    }
                }
            }
        }
    }
    return super.dispatchTouchEvent(e)
}

4.Android 4.4惯性滑动崩溃问题

上到线上后发现,使用Android 4.4手机惯性滑动停止后会必现崩溃,具体原因是 onNestedScrollAccepted() was added in API 21. You can't use it in lower API levels.,因为使用此版本的人数很少,所以粗略处理了一下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    override fun onNestedPreFling(target: View, velocityX: Float, velocityY: Float): Boolean {
        try {
            if (android.os.Build.VERSION.SDK_INT <= 19) {
                return true
            }
            return super.onNestedPreFling(target, velocityX, velocityY)
        } catch (e: Exception) {
            return true
        }
    }

总结

此项目是在PersistentRecyclerView中发展修改而来,其中解决了部分卡顿兼容问题和加了下拉刷新上拉加载功能,且经过一段时间的大范围使用,基本趋于稳定,使用起来更加方便了。项目代码放在了ByRecyclerView / stickyrv,因为不是必要的功能所以没放在library里,如果需要使用自行拷贝代码即可。有问题可以提issues,再次感谢作者的开源精神。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
View的滑动冲突的分析和处理实践
文中有用到 Scroller 来实现弹性滑动,不了解的可以先看下 View的滑动实现方式。
103style
2022/12/19
5200
View的滑动冲突的分析和处理实践
RecyclerView的NestedScroll实现
RecyclerView中的NestedScroll流程 手指触碰之后,onInterceptTouchEvent触发ACTION_DOWN: 调用startNestedScroll,沿着View树往上寻找可以接受嵌套滑动的父View,如果找到了,则会回调父View的onStartNestedScroll以及onNestedScrollAccepted 当手指滑动的时候,触发onTouchEvent中的ACTION_MOVE: 调用dispatchNestedPreScroll将嵌套滑动事件给
None_Ling
2018/10/24
1.1K0
NestedScrolling 机制深入解析
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/details/71553411
程序员徐公
2018/09/17
8820
NestedScrolling 机制深入解析
优雅地实现RecyclerView的上拉加载
这篇博客是承接上一篇博客--探索Android架构的DataLayer层(DataManager方式)具体实现,其实是上篇博客的一个使用比较普遍的例子,当然如果把上一篇博客设计的数据加载回调接口提炼出来也是可以做一篇单独的文章。
1025645
2018/08/23
9570
3分钟搞定,学会Android滑动冲突解决技巧
Android滑动冲突是Android开发中常见的问题。在一个界面中,可能存在多个View可以响应滑动事件。如果这些View滑动方向一致,则会导致滑动冲突。本文将从原理、使用与优化三个方面,详细介绍Android滑动冲突的解决方式。
Rouse
2024/01/10
8150
3分钟搞定,学会Android滑动冲突解决技巧
嵌套滑动机制详解
嵌套滑动机制主要涉及两个接口 public interface NestedScrollingChild { void setNestedScrollingEnabled(boolean var1); boolean isNestedScrollingEnabled(); boolean startNestedScroll(int var1);//开启嵌套滚动流程 //循环遍历parent,先判断是否是NestedScrollingParent的实例,如果是,那就调用par
提莫队长
2018/05/18
7670
1、创建刷新控件
 一些控件我定义成全局的是因为下面会用到。setPadding可以设置显示位置,左上右下,上为负数就是隐藏在顶部。我们需要手指下拉去控制他缓慢显示,就用到了OnTouchListener,我们实现OnTouch方法做一些事件的分发处理。
cMusketeer
2022/10/27
5610
1、创建刷新控件
Android--RecyclerView嵌套RecyclerView优化
我们在使用RecyclerView嵌套RecyclerView时,会觉得卡顿,操作不是很顺畅 RecyclerView嵌套RecyclerView滑动.gif 在这个现象中,我们想要水平滑动时,却竖直
aruba
2020/07/02
1.8K0
从 Android 开发到读懂源码 第02期:NestScroll 机制源码解析
首先我们来看一看嵌套滑动的效果,上面是 TopView (一般是banner类),下面是 RecyclerView,当 recyclerView 向上滑动时,topView 跟随往上滑动至隐藏后吸顶固定,recyclerView 下拉到顶时继续下拉,则把 topView 拉回初始位置:
数据库交流
2022/04/25
8120
从 Android 开发到读懂源码 第02期:NestScroll 机制源码解析
利用Android嵌套滑动机制轻松实现顶部布局置顶
传统的Android事件分发是子控件消费了事件,那么父控件就不能再处理这个事件了。也就是说一旦内部的滑动控件消费了滑动操作,外部的滑动控件就不能获取到这个滑动动作也就无法做处理了。在我们上一个情景里,滑动内容列表控件要求头布局和导航布局作出响应就是要求他们的共同父布局作出响应,显然用传统的事件分发处理是很困难的。
1025645
2018/08/23
1.6K0
Android - ViewDragHelper实现京东、淘宝拖拽详情
前言 这个内容是很早就好的,不知道后来怎么忘记发出来了,这不过年这几天没事给翻出来,就给放出来了。 好了,老规矩先上效果图。 要实现这个效果有三种方式: ① 手势 ② 动画 ③ ViewDrag
code_horse
2018/07/02
8660
SmartRefreshLayout dispatchTouchEvent 解读
SmartRefreshLayout 以打造一个强大,稳定,成熟的下拉刷新框架为目标,并集成各种的炫酷、多样、实用、美观的Header和Footer。
Erossssssss
2021/04/10
1.8K0
SmartRefreshLayout dispatchTouchEvent 解读
高级 UI 成长之路 (二) 深入理解 Android 8.0 View 触摸事件分发机制
在上一篇文章中我们介绍了 View 的基础知识以及 View 滑动的实现,本篇将为大家带来 View 的一个核心知识点 事件分发机制。事件分发机制不仅仅是核心知识点也是 Android 中的一个难点,下面我们就从源码的角度来分析事件的传递还有最后是如何解决滑动冲突的。
做个快乐的码农
2021/11/12
7290
高级 UI 成长之路 (二) 深入理解 Android 8.0 View 触摸事件分发机制
Android自定义组合控件---教你如何自定义下拉刷新和左滑删除
版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/lyhhj/article/details/80122918
Hankkin
2018/09/06
1.4K0
Android自定义组合控件---教你如何自定义下拉刷新和左滑删除
SwipeRefreshLayout实现上滑加载更多[通俗易懂]
在我们的项目中,需要用到许多下拉刷新和上滑加载的操作,不说什么没用的,直接来介绍SwipeRefreshLayout的扩展用法。
全栈程序员站长
2022/09/12
3.5K0
SwipeRefreshLayout实现上滑加载更多[通俗易懂]
踩坑记 | Flutter升级影响了NestedScrollView?
嗨,我是哈利迪~最近有个bug排查了好几天,就是有个老页面因业务复杂度,使用了NestedScrollView+tab+多Fragment的结构(各Fragment里有RecyclerView,即存在嵌套滑动),在新的班车中,出现了偶现的滑不动问题。在业务相关组件里排查了很久都没思路,哈迪便开始了万能的组件排除法,即在几十个变更组件里用二分法分批排查(没错就是这么骚),最后定位到一个Flutter组件,只要把它回退就没问题了。。
Holiday
2020/08/07
9800
踩坑记 | Flutter升级影响了NestedScrollView?
相关推荐
View的滑动冲突的分析和处理实践
更多 >
LV.2
这个人很懒,什么都没有留下~
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验