某些场景或者框架限制的情况下,使用了 Scrollview 嵌套 Recyclerview 布局
此时会有问题
1、滑动时 Recyclerview 被禁用了滑动事件,无法监听滑动事件
2、Recyclerview 被重新测量,item充满全屏,导致无法监听生命周期
还有场景是没有任何嵌套列表,只有 ScrollView
如果这时候需要在滑动停止时处理一些操作,比如视频自动播放,这种时候只能自己去实现,无法跟Recyclerview一样监听到滑动停止操作
因为 ScrollView 滑动手指抬起时,会有惯性在滑动一段距离
需要借助系统监听 setOnScrollChangeListener ,只要列表处于滑动中,它就会触发回调,按照这个思路,基于嵌套 Recyclerview 为例子,有个简单的实现方案
1、在滑动开始时开启一个循环,循环内读取当前列表的滑动值 scrollY
2、记录下当前 scrollY,用于下次触发时对比
3、监听停止时再次触发循环,此时 scrollY 是一样的,这时候就表示 ScrollView 已经停止
private class WeakReferenceHandler(tag: ScrollStatusListenerUtils): Handler() { private val scrollTag = WeakReference(tag) override fun handleMessage(msg: Message) { super.handleMessage(msg) scrollTag.get()?.apply { val scrollView = msg.obj as ScrollView? if (lastY == (scrollView?.scrollY ?: 0)) { stopScroll?.invoke(lastY) lastY = 0 removeMessages(SCROLL_MSG_START) isScroll = false } else { lastY = scrollView?.scrollY ?: 0 sendMsg(scrollView) } } } }
这里会有个循环发送,使用 handler 循环,当开启滑动后,每300毫秒触发一次,等结束时循环内 scrollY 会跟 lastY 一致,此时滑动停止
并且你反复触发多次滑动时不影响监听
可以看到日志,我最后一次监听的滑动坐标是 1211,在列表中处于第3个位置,也就是可见 position
然后在滑动结束时 removeMessages 当前消息,在活动销毁时释放内存 removeCallbacksAndMessages
为了防止 Handler 内存泄漏,需要使用 WeakReference
在滑动停止时,通过 ScrollView 获取到嵌套的 Recyclerview
同时通过 layoutManager 拿到所有 Recyclerview itemRootView,通过停止时的滑动值 scrollY 跟 当前 itemRootView 的 top 值对比,大于当前 top 值就是当前的可见 item
得到当前可见 item 后,就可以执行播放视频操作了,然后记录当前播放下标,在下次滑动停止时判断是否一致,可以做停止播放操作
package com.frogsing.common.utils import android.os.Handler import android.os.Message import android.widget.ScrollView import java.lang.ref.WeakReference class ScrollStatusListenerUtils { private var isScroll = false private var lastY = 0 private var stopScroll: ((Int) -> Unit)? = null private object SingleInstance { val instance = ScrollStatusListenerUtils() } companion object { private const val SCROLL_MSG_START = 1 private class WeakReferenceHandler(tag: ScrollStatusListenerUtils): Handler() { private val scrollTag = WeakReference(tag) override fun handleMessage(msg: Message) { super.handleMessage(msg) scrollTag.get()?.apply { val scrollView = msg.obj as ScrollView? if (lastY == (scrollView?.scrollY ?: 0)) { stopScroll?.invoke(lastY) lastY = 0 removeMessages(SCROLL_MSG_START) isScroll = false } else { lastY = scrollView?.scrollY ?: 0 sendMsg(scrollView) } } } } fun instance(): ScrollStatusListenerUtils { return SingleInstance.instance } } private val handler = WeakReferenceHandler(this) fun start(scrollView: ScrollView?) { if (!isScroll) { isScroll = true sendMsg(scrollView) } } fun sendMsg(scrollView: ScrollView?) { val msg = handler.obtainMessage(SCROLL_MSG_START) msg.obj = scrollView handler.sendMessageDelayed(msg, 300L) } fun setStopScrollListener(stopScroll: (Int) -> Unit) { this.stopScroll = stopScroll } fun clear() { lastY = 0 isScroll = false if (handler.hasMessages(SCROLL_MSG_START)) { handler.removeMessages(SCROLL_MSG_START) } handler.removeCallbacksAndMessages(null) } }ScrollStatusListenerUtils
使用时需要注意版本判断,因为 setOnScrollChangeListener 是在6.0才出的
来源Scrollview实现滑动停止监听,Scrollview嵌套Recyclerview获取滑动停止时当前可见position - 翻滚的咸鱼 - 博客园 (cnblogs.com)
标签:ScrollView,Scrollview,Recyclerview,private,停止,scrollView,滑动,lastY From: https://www.cnblogs.com/LiuZhen/p/17057859.html