You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

ViewPager2轮播过渡动画过快,如何调整动画速度?

解决ViewPager2自动轮播时过渡动画过快的问题

这问题我之前做轮播组件时也踩过坑!你调整Handler的延迟参数只是改了页面切换的间隔,但ViewPager2默认的页面滑动动画时长只有250ms左右,这么短的时间里你的缩放淡出效果根本来不及展现。咱们可以从两个方向来解决:

方案一:替换内部Scroller修改动画时长(推荐)

ViewPager2的页面滑动速度是由内部的Scroller控制的,我们可以通过反射替换这个Scroller,自定义动画时长。

步骤1:创建自定义SlowScroller

class SlowScroller(context: Context) : Scroller(context, DecelerateInterpolator()) {
    override fun startScroll(startX: Int, startY: Int, dx: Int, dy: Int, duration: Int) {
        // 把默认时长改成你需要的数值,比如1000ms(1秒)
        super.startScroll(startX, startY, dx, dy, 1000)
    }
}

步骤2:反射替换ViewPager2的Scroller

在你的Activity里添加这个方法,用来替换内部的Scroller:

private fun setupSlowViewPagerAnimation() {
    try {
        // 获取ViewPager2内部的RecyclerView
        val viewPager2Field = ViewPager2::class.java.getDeclaredField("mRecyclerView")
        viewPager2Field.isAccessible = true
        val recyclerView = viewPager2Field.get(viewPager) as RecyclerView

        // 获取RecyclerView的LayoutManager
        val layoutManagerField = RecyclerView::class.java.getDeclaredField("mLayout")
        layoutManagerField.isAccessible = true
        val layoutManager = layoutManagerField.get(recyclerView) as LinearLayoutManager

        // 替换LayoutManager里的Scroller为我们自定义的SlowScroller
        val scrollerField = LinearLayoutManager::class.java.getDeclaredField("mScroller")
        scrollerField.isAccessible = true
        scrollerField.set(layoutManager, SlowScroller(this))
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

步骤3:初始化时调用这个方法

在你设置ViewPager2的适配器和PageTransformer之后,调用这个方法:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    
    viewPager.adapter = yourPagerAdapter
    viewPager.setPageTransformer(ZoomOutPageTransformer())
    setupSlowViewPagerAnimation() // 设置慢动画
    slideViewPager() // 启动自动轮播
}

方案二:用ValueAnimator手动控制滚动(更灵活,无反射)

如果不想依赖反射,也可以放弃ViewPager2自带的setCurrentItem动画,用ValueAnimator手动控制页面滚动,完全自定义动画时长。

修改你的slideViewPager方法:

private fun slideViewPager() {
    var currentPage = 0
    val DELAY_MS: Long = 2000 // 首次延迟
    val PERIOD_MS: Long = 3000 // 切换间隔
    val ANIMATION_DURATION: Long = 1000 // 自定义动画时长

    val handler = Handler(Looper.getMainLooper())
    val update = Runnable {
        // 处理循环滚动
        currentPage = if (currentPage == NUM_PAGES - 1) 0 else currentPage + 1
        
        val targetPos = currentPage
        val currentPos = viewPager.currentItem
        if (targetPos != currentPos) {
            // 计算需要滚动的距离
            val scrollDistance = viewPager.width * (targetPos - currentPos)
            ValueAnimator.ofInt(0, scrollDistance).apply {
                duration = ANIMATION_DURATION
                interpolator = DecelerateInterpolator() // 和默认动画插值器一致
                addUpdateListener { anim ->
                    val scrollX = anim.animatedValue as Int
                    viewPager.scrollBy(scrollX - viewPager.scrollX, 0)
                }
                start()
            }
        }
    }

    Timer().schedule(object : TimerTask() {
        override fun run() {
            handler.post(update)
        }
    }, DELAY_MS, PERIOD_MS)
}

注意:这个方法适合水平方向的ViewPager2,如果你用的是垂直方向,需要把width改成heightscrollX改成scrollY。另外,从最后一页回到第一页时会有反向滚动的跳变,要是需要平滑循环,可以额外处理(比如在适配器里添加首尾页的复制项)。

额外提醒:避免内存泄漏

记得在Activity销毁时取消Timer和Handler的任务:

private var timer: Timer? = null
private val handler = Handler(Looper.getMainLooper())
private val updateRunnable = Runnable { /* ... */ }

override fun onDestroy() {
    super.onDestroy()
    timer?.cancel()
    handler.removeCallbacks(updateRunnable)
}

内容的提问来源于stack exchange,提问作者SNM

火山引擎 最新活动