问题概述:
使用 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])
}
}
}
}