22

我知道有办法改变ViewPager程序幻灯片的动画持续时间(这里)。

但它不工作ViewPager2

我试过这个:

try {
        final Field scrollerField = ViewPager2.class.getDeclaredField("mScroller");
        scrollerField.setAccessible(true);
        final ResizeViewPagerScroller scroller = new ResizeViewPagerScroller(getContext());
        scrollerField.set(mViewPager, scroller);
    } catch (Exception e) {
        e.printStackTrace();
    }

IDE 在“mScroller”上给我警告:

无法解析字段“mScroller”

如果我们运行此代码,那将不起作用,并在下面给我们错误:

No field mScroller in class Landroidx/viewpager2/widget/ViewPager2; (declaration of 'androidx.viewpager2.widget.ViewPager2' appears in /data/app/{packagename}-RWJhF9Gydojax8zFyFwFXg==/base.apk)

那么我们如何才能实现这个功能呢?

4

3 回答 3

33

基于这个 issue 票, Android 团队不打算为 ViewPager2 支持这种行为,票中的建议是使用ViewPager2.fakeDragBy(). 这种方法的缺点是您必须以像素为单位提供页面宽度,尽管如果您的页面宽度与 ViewPager 的宽度相同,那么您可以使用该值来代替。

这是示例实现

fun ViewPager2.setCurrentItem(
    item: Int,
    duration: Long,
    interpolator: TimeInterpolator = AccelerateDecelerateInterpolator(),
    pagePxWidth: Int = width // Default value taken from getWidth() from ViewPager2 view
) {
    val pxToDrag: Int = pagePxWidth * (item - currentItem)
    val animator = ValueAnimator.ofInt(0, pxToDrag)
    var previousValue = 0
    animator.addUpdateListener { valueAnimator ->
        val currentValue = valueAnimator.animatedValue as Int
        val currentPxToDrag = (currentValue - previousValue).toFloat()
        fakeDragBy(-currentPxToDrag)
        previousValue = currentValue
    }
    animator.addListener(object : Animator.AnimatorListener {
        override fun onAnimationStart(animation: Animator?) { beginFakeDrag() }
        override fun onAnimationEnd(animation: Animator?) { endFakeDrag() }
        override fun onAnimationCancel(animation: Animator?) { /* Ignored */ }
        override fun onAnimationRepeat(animation: Animator?) { /* Ignored */ }
    })
    animator.interpolator = interpolator
    animator.duration = duration
    animator.start()
}

为了支持RTL你必须翻转提供给的值ViewPager2.fakeDragBy(),所以从上面的例子中,而不是在fakeDragBy(-currentPxToDrag)使用fakeDragBy(currentPxToDrag)时使用RTL

根据官方文档,使用它时要记住的几件事:

  • 负值向前滚动,正向后滚动(用 RTL 翻转)

  • 在调用fakeDragBy()使用之前beginFakeDrag()和完成之后endFakeDrag()

  • 这个 API 可以很容易地与onPageScrollStateChangedfrom一起使用,你可以通过方法ViewPager2.OnPageChangeCallback区分编程拖动和用户拖动isFakeDragging()
  • 如果给定的项目是正确的,上面的示例实现没有安全检查。还可以考虑为 UI 的生命周期添加取消功能,这可以通过 RxJava 轻松实现。
于 2019-12-08T13:42:43.557 回答
5

ViewPager2 团队真的很难改变滚动速度。如果您查看 setCurrentItemInternal 方法,它们会实例化自己的私有 ScrollToPosition(..) 对象。连同状态管理代码,所以这将是您必须以某种方式覆盖的方法。

作为这里的解决方案:https ://issuetracker.google.com/issues/122656759 ,他们说使用(ViewPager2).fakeDragBy()它超级丑陋。

不是最好的,只需要等待他们给我们一个 API 来设置持续时间或复制他们的 ViewPager2 代码并直接修改他们的 LinearLayoutImpl 类。

于 2019-08-15T19:40:13.983 回答
0

当您希望 ViewPager2 以您的速度、方向和页数滚动时,在单击某个按钮时,请使用您的参数调用此函数。Direction 可以是 leftToRight = true 如果你希望它是那样,或者 false 如果你想从右到左,持续时间以毫秒为单位,numberOfPages 应该是 1,除非你想一直回去,当它应该是你的该 viewPager 的页数:

fun fakeDrag(viewPager: ViewPager2, leftToRight: Boolean, duration: Long, numberOfPages: Int) {
    val pxToDrag: Int = viewPager.width
    val animator = ValueAnimator.ofInt(0, pxToDrag)
    var previousValue = 0
    animator.addUpdateListener { valueAnimator ->
        val currentValue = valueAnimator.animatedValue as Int
        var currentPxToDrag: Float = (currentValue - previousValue).toFloat() * numberOfPages
        when {
            leftToRight -> {
                currentPxToDrag *= -1
            }
        }
        viewPager.fakeDragBy(currentPxToDrag)
        previousValue = currentValue
    }
    animator.addListener(object : Animator.AnimatorListener {
        override fun onAnimationStart(animation: Animator?) { viewPager.beginFakeDrag() }
        override fun onAnimationEnd(animation: Animator?) { viewPager.endFakeDrag() }
        override fun onAnimationCancel(animation: Animator?) { /* Ignored */ }
        override fun onAnimationRepeat(animation: Animator?) { /* Ignored */ }
    })
    animator.interpolator = AccelerateDecelerateInterpolator()
    animator.duration = duration
    animator.start()
}
于 2021-11-19T09:18:42.067 回答