RecyclerView结合LinearSnapHelper实现先快后慢的滚动时间选择器
实现RecyclerView类似三星闹钟的滚动减速效果
要实现滚动初期速度快、临近目标时减速的效果,核心是自定义SnapHelper的滚动插值器,而非单纯修改calculateScrollDistance这类方法。以下是具体实现步骤:
1. 自定义LinearSnapHelper,重写滚动逻辑
LinearSnapHelper默认的滚动是匀速的,我们需要替换它的平滑滚动器,用自定义插值器实现先快后慢的减速曲线:
class SmoothLinearSnapHelper : LinearSnapHelper() { private var recyclerView: RecyclerView? = null override fun attachToRecyclerView(recyclerView: RecyclerView?) { super.attachToRecyclerView(recyclerView) this.recyclerView = recyclerView } override fun createScroller(layoutManager: RecyclerView.LayoutManager?): RecyclerView.SmoothScroller? { return object : LinearSmoothScroller(recyclerView?.context) { override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics?): Float { // 数值越小,整体滚动速度越快,可根据需求调整 return 0.07f } override fun onTargetFound(targetView: View, state: RecyclerView.State, action: Action) { val dx = calculateDxToMakeVisible(targetView, horizontalSnapPreference) val dy = calculateDyToMakeVisible(targetView, verticalSnapPreference) val distance = sqrt((dx * dx + dy * dy).toDouble()).toFloat() // 用自定义插值器替换默认的匀速逻辑 action.update(dx, dy, (distance * 1.2).toInt(), CustomDecelerateInterpolator()) } } } // 自定义先快后慢的插值器 private class CustomDecelerateInterpolator : Interpolator { override fun getInterpolation(input: Float): Float { // 二次方曲线实现:前半段快速加速,后半段缓慢减速趋近目标 return if (input < 0.5) { 2 * input * input } else { 1 - Math.pow((1 - input) * 2.0, 2.0) / 2 }.toFloat() } } }
2. 替换原有SnapHelper
在RecyclerView初始化代码中,用自定义的SmoothLinearSnapHelper替代默认的LinearSnapHelper:
val snapHelper = SmoothLinearSnapHelper() snapHelper.attachToRecyclerView(yourTimePickerRecyclerView)
3. 优化快速滑动场景
如果用户快速拖动后松手,需要确保滚动收尾阶段仍遵循先快后慢逻辑,可通过滚动监听补全:
yourTimePickerRecyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) if (newState == RecyclerView.SCROLL_STATE_SETTLING) { val layoutManager = recyclerView.layoutManager as LinearLayoutManager val targetPosition = snapHelper.findTargetSnapPosition(layoutManager, 0, 0) if (targetPosition != RecyclerView.NO_POSITION) { recyclerView.smoothScrollToPosition(targetPosition) } } } })
为什么之前的方法无效?
attachToRecyclerView仅为生命周期回调方法,不直接控制滚动速度逻辑;calculateScrollDistance只是计算滚动距离,修改它只会让整体滚动距离变化,无法实现分段式的速度差异,因此会出现均匀减速的问题。
内容的提问来源于stack exchange,提问作者Praveen Anandu
相关产品推荐
相关产品推荐

