RecyclerView + SnapHelper 滚动差异问题

问题概述:

使用 scrollToPosition(position) 跳转到 倒数第二个 item ,配合自定义的 CustomSnapHelper,无法成功吸附到左边 。 使用 smoothScrollToPosition(position) 则一切正常。

kotlin 复制代码
class CustomSnapHelper : PagerSnapHelper() {
    private var mHorizontalHelper: OrientationHelper? = null

    override fun calculateDistanceToFinalSnap(layoutManager: RecyclerView.LayoutManager, targetView: View): IntArray {
        val out = IntArray(2)
        // 判断支持水平滚动,修改水平方向的位置,是修改的out[0]的值
        if (layoutManager.canScrollHorizontally()) {
            out[0] = distanceToStart(targetView, getHorizontalHelper(layoutManager))
        } else {
            out[0] = 0
        }
        return out
    }

    private fun distanceToStart(targetView: View, helper: OrientationHelper): Int {
        return helper.getDecoratedStart(targetView) - helper.startAfterPadding
    }

    override fun findSnapView(layoutManager: RecyclerView.LayoutManager): View? {
        return findStartView(layoutManager, getHorizontalHelper(layoutManager))
    }

    private fun findStartView(layoutManager: RecyclerView.LayoutManager, helper: OrientationHelper): View? {
        return super.findSnapView(layoutManager)
    }

    private fun getHorizontalHelper(layoutManager: RecyclerView.LayoutManager): OrientationHelper {
        if (mHorizontalHelper == null) {
            mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager)
        }
        return mHorizontalHelper!!
    }
}

关键原因:是否触发 findSnapView

方法 会触发滚动动画 会触发 LayoutManager layout SnapHelper 可介入定位?
scrollToPosition(position) 否(瞬间跳转) 只 layout 当前页及之后几项 不会立即触发 SnapHelper 定位
smoothScrollToPosition(position) 是 连续 layout 滑动中的 item 会触发 SnapHelper 计算并吸附

解决方案: 在滚动之后,手动触发它吸附目标 view 到左侧:

kotlin 复制代码
/**
 * 强制触发 SnapHelper 吸附,确保目标 Item 吸附到左边
 *
 * 用于 scrollToPosition() 之后,确保 item 能平滑对齐到左边(通常结合 CustomSnapHelper 使用)
 *
 * @param recyclerView 绑定了自定义 CustomSnapHelper 的 RecyclerView
 */
fun forceSnapToStartAfterScroll(recyclerView: RecyclerView) {
    // 尝试获取当前 RecyclerView 使用的 SnapHelper(这里是自定义的 CustomSnapHelper)
    val snapHelper = recyclerView.onFlingListener as? CustomSnapHelper
    Logger.e("触发 SnapHelper 对齐 snapHelper = $snapHelper")

    // 如果有设置 SnapHelper,则手动触发一次吸附逻辑
    if (snapHelper != null) {
        recyclerView.post {
            val layoutManager = recyclerView.layoutManager ?: return@post

            // 找到当前需要吸附的目标 View
            val snapView = snapHelper.findSnapView(layoutManager) ?: return@post

            // 计算从当前位置到最终吸附位置所需的平移距离(x/y)
            val distance = snapHelper.calculateDistanceToFinalSnap(layoutManager, snapView)

            // 如果需要移动(距离非0),则平滑滚动使其吸附
            if (distance != null && (distance[0] != 0 || distance[1] != 0)) {
                recyclerView.smoothScrollBy(distance[0], distance[1])
            }
        }
    }
}
相关推荐
三少爷的鞋12 小时前
Java 线程自上而下,Kotlin 协程自下而上
android
好的佩奇14 小时前
Dart 之增强枚举
android·flutter·dart
梦影_14 小时前
MySQL进阶教程(五)—— SQL调优(下)
android·大数据·数据库·后端·sql·mysql·database
好的佩奇16 小时前
蓝牙之无线电与通信距离
android·flutter·蓝牙
vilya17 小时前
我把一套完整的 Python Agent 运行时塞进手机 APK:依赖闭包、Rust 轮子和三个真机坑
android
呆呆敲代码的小Y19 小时前
【实战精选面试题】C# 面试题详细解答,按难易程度排序
android·java·游戏·c#·游戏开发·unity面试题·c#面试题
思想觉悟20 小时前
SEAndroid实战
android·linux·c++
思想觉悟21 小时前
Aosp开发磨刀石之ADB速查
android·c++
应用市场1 天前
用本机跑的开源音乐模型,给安卓小游戏做 20 秒无缝循环背景音乐
android·python·游戏开发·soundpool·音频处理
企业数字化笔记1 天前
表单发布前怎么验收?版本引用、字段兼容和历史数据回归
android·数据挖掘·回归