以编程方式滑动时更改 ViewPager2 滚动速度



我知道有办法更改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)

那么我们如何实现此功能呢?

基于此问题,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 可以轻松地与ViewPager2.OnPageChangeCallback中的onPageScrollStateChanged一起使用,借助isFakeDragging()方法,您可以在其中区分编程拖动和用户拖动
  • 上面的示例实现没有安全检查,如果给定项正确。还可以考虑为 UI 的生命周期添加取消功能,这可以通过 RxJava 轻松实现。

ViewPager2团队很难改变滚动速度。如果你看一下方法setCurrentItemInternal,它们会实例化自己的私有ScrollToPosition(..(对象。以及状态管理代码,因此这将是您必须以某种方式重写的方法。

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

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

M Tomczynski(参考:https://stackoverflow.com/a/59235979/7608625(给出的解决方案很好。但是当与内置的setCurrentItem((一起使用时,由于fakeDrag,即使我确保在使用setCurrentItem((之前调用endDrag((,我也面临着应用程序随机崩溃的问题。所以我修改了解决方案以使用 viewpager 的嵌入式回收器视图的 scrollBy(( 函数,该函数运行良好,没有任何问题。

fun ViewPager2.setCurrentItem(
index: Int,
duration: Long,
interpolator: TimeInterpolator = PathInterpolator(0.8f, 0f, 0.35f, 1f),
pageWidth: Int = width - paddingLeft - paddingRight,
) {
val pxToDrag: Int = pageWidth * (index - currentItem)
val animator = ValueAnimator.ofInt(0, pxToDrag)
var previousValue = 0
val scrollView = (getChildAt(0) as? RecyclerView)
animator.addUpdateListener { valueAnimator ->
val currentValue = valueAnimator.animatedValue as Int
val currentPxToDrag = (currentValue - previousValue).toFloat()
scrollView?.scrollBy(currentPxToDrag.toInt(), 0)
previousValue = currentValue
}
animator.addListener(object : Animator.AnimatorListener {
override fun onAnimationStart(animation: Animator) { }
override fun onAnimationEnd(animation: Animator) {
// Fallback to fix minor offset inconsistency while scrolling
setCurrentItem(index, true)
post { requestTransform() } // To make sure custom transforms are applied
}
override fun onAnimationCancel(animation: Animator) { /* Ignored */ }
override fun onAnimationRepeat(animation: Animator) { /* Ignored */ }
})
animator.interpolator = interpolator
animator.duration = duration
animator.start()
}

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

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()
}

最新更新