ViewPager2 自动滚动直到适配器 itemCount 结束

ViewPager2 autoscroll until end of adapter itemCount

我已经尝试了几种不同的方法,但无法使此 viewpager 正常运行。我正在设置一个带有适配器的 viewpager2,但部分要求是 viewpager 可以手动滑动以及基于按钮以增加寻呼机视图。我滑动并单击按钮按预期移动了视图寻呼机,但是自动滚动是有问题的。

我正在 viewPager 上设置一个 postDelayed runnable。当这是 运行 时,刚刚完成的日志是有意义的,您可以在下面看到输出的样子。

companion object {
    private const val TAG = "LauncherActivity"
    private const val timerDelay: Long = 10 * 1000 // 1 minute in milliseconds
    var position: Int = 0
}
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    ...
    pager.autoScroll(timerDelay)
    ...
    pager.registerOnPageChangeCallback(object : OnPageChangeCallback() {
        override fun onPageSelected(position: Int) {
            Log.d(TAG, "-> OnPageChangeCallback() -> position: ${position}")
            super.onPageSelected(position)
            if (position != LauncherActivity.position) LauncherActivity.position = position
        }
    })
}

fun ViewPager2.autoScroll(interval: Long) {

    val count = adapter?.itemCount ?: 0

    val handler = Handler()
    val runnable = object: Runnable {
        override fun run() {
            if (position < count) {
                Log.d(TAG, "Autoscroll: current position = ${position} ending position = ${position + 1}")
                setCurrentItem((position++ % count), true)
                Log.d(TAG, "New position: ${position}")
                handler.postDelayed(this, interval)
            }
        }
    }
    handler.post(runnable)
}

更好地解释发生了什么的日志

2020-05-22 11:42:51.485 D/LauncherActivity: Autoscroll: current position = 0 ending position = 1
2020-05-22 11:42:51.485 D/LauncherActivity: New position: 1
2020-05-22 11:42:51.621 D/LauncherActivity: -> OnPageChangeCallback() -> position: 0
This is initial load. The view is still showing position 0 in viewpager. Seems like the postDelayed ran but didnt run?

2020-05-22 11:43:01.492 D/LauncherActivity: Autoscroll: current position = 0 ending position = 1
2020-05-22 11:43:01.492 D/LauncherActivity: New position: 1
10 seconds later the post delayed runs again but doesnt actually change the viewpager. BUT it starts
everything off as it should. lack of OnPageChangeCallback() indicated it didnt change the view.

2020-05-22 11:43:11.497 D/LauncherActivity: Autoscroll: current position = 1 ending position = 2
2020-05-22 11:43:11.503 D/LauncherActivity: -> OnPageChangeCallback() -> position: 1
2020-05-22 11:43:11.506 D/LauncherActivity: New position: 1
The view finally changed to the next position

2020-05-22 11:43:21.518 D/LauncherActivity: Autoscroll: current position = 1 ending position = 2
2020-05-22 11:43:21.519 D/LauncherActivity: New position: 2
10 seconds later the post delayed runs again but doesnt actually change the viewpager.

2020-05-22 11:43:31.532 D/LauncherActivity: Autoscroll: current position = 2 ending position = 3
2020-05-22 11:43:31.534 D/LauncherActivity: -> OnPageChangeCallback() -> position: 2
2020-05-22 11:43:31.535 D/LauncherActivity: New position: 2
Finally it has changed to the final item.

2020-05-22 11:43:41.548 D/LauncherActivity: Autoscroll: current position = 2 ending position = 3
2020-05-22 11:43:41.550 D/LauncherActivity: New position: 3
Not a clue why it ran again...

我会删除 autoScroll(Long) 方法,然后这样做:

  1. 在 registerOnPageChangeCallback 的 onPageSelected() 末尾添加:

handler.postDelayed(pager.setCurrentItem(position + 1), timerDelay)

这样您将开始滚动项目的循环。

pager.registerOnPageChangeCallback(object : OnPageChangeCallback() {
    override fun onPageSelected(position: Int) {
        Log.d(TAG, "-> OnPageChangeCallback() -> position: ${position}")
        super.onPageSelected(position)
        if (position != LauncherActivity.position) LauncherActivity.position = position
        if (position < count) {
            handler.postDelayed(pager.setCurrentItem(position + 1), timerDelay) // repeat
        }
    }
})

我也不知道你为什么在

中使用 剩余

setCurrentItem((position++ % count)

感觉不应该出现

有了上面的@Mwasz 回答,这让我重新开始解决用户向前或向后滑动或单击按钮向前移动时的延迟后池问题。这是一个完整的解决方案,因为 onPageScrollStateSchanged 指示用户物理滑动并且按钮 onClick 单独处理失效。

编辑:不需要 onPageScrollStateChanged() 回调。可以在 onPageSelected() 的开头清除处理程序回调,结果相同。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val handler = Handler()
    var origPosition: Int = 0
    ...
//    pager.autoScroll(timerDelay)
    ...
    pager.registerOnPageChangeCallback(object : OnPageChangeCallback() {
        override fun onPageSelected(position: Int) {
            super.onPageSelected(position)

            handler.removeMessages(0)

            val runnable = Runnable { pager.currentItem = ++pager.currentItem) }
            if (position < pager.adapter?.itemCount ?: 0) {
                handler.postDelayed(runnable, timerDelay)
            }
        }
    ...
    btnContinue.setOnClickListener {
        ...
        pager.currentItem = ++pager.currentItem
        ...
    }
}

只回答:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val handler = Handler()
    var origPosition: Int = 0
    ...
//    pager.autoScroll(timerDelay)
    ...
    pager.registerOnPageChangeCallback(object : OnPageChangeCallback() {
        override fun onPageSelected(position: Int) {
            super.onPageSelected(position)

            val runnable = Runnable { pager.setCurrentItem(position + 1) }
            if (position < pager.adapter?.itemCount ?: 0) {
                handler.postDelayed(runnable, timerDelay)
            }
        }

        override fun onPageScrollStateChanged(state: Int) {
            super.onPageScrollStateChanged(state)

            /**
            * The user swiped forward or back and we need to
            * invalidate the previous handler.
            */
            if (state == SCROLL_STATE_DRAGGING) handler.removeMessages(0)
        }
    })
    ...
    btnContinue.setOnClickListener {
        ...
        // We dont want the last delayed runnable jumping us back up the stack.
        handler.removeMessages(0)

        pager.setCurrentItem(++pager.currentItem)
        ...
    }
}