You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 09:26:02