将 Paging 3 alpha 更新到稳定会导致 Android 索引问题

Viv*_*odi 6 android kotlin android-paging android-paging-library android-paging-3

嘿,我正在使用Paging 3库和ViewPager 2。它加载无限的数据。

\n
implementation "androidx.paging:paging-runtime-ktx:3.0.0-alpha07"\n
Run Code Online (Sandbox Code Playgroud)\n

数据源.kt

\n
package com.example.viewpagerexample\n\nimport java.util.*\n\nclass DataSource(\n    private val size: Int = 5,\n    private val currentDate: Date,\n    private val limitDate: Date?\n) {\n\n    fun returnData(pageNumber: Int): List<Date> {\n\n        val dateList = mutableListOf<Date>()\n        val startDateForPage = startDate(pageNumber)\n        val tempCalendar = Calendar.getInstance()\n\n        tempCalendar.time = startDateForPage\n        val lastDateForPage = endDate(startDateForPage)\n\n        while (tempCalendar.time < lastDateForPage) {\n            if (limitDate == null ||\n                tempCalendar.time.before(limitDate) ||\n                tempCalendar.time == limitDate\n            ) {\n                dateList.add(tempCalendar.time)\n                tempCalendar.add(Calendar.DATE, 1)\n            } else {\n                break\n            }\n        }\n        return dateList\n    }\n\n    private fun startDate(pageNumber: Int): Date {\n        if (pageNumber == 0) {\n            return currentDate\n        } else {\n            Calendar.getInstance().let {\n                it.time = currentDate\n                it.add(Calendar.DATE, pageNumber * size)\n                return it.time\n            }\n        }\n    }\n\n    private fun endDate(firstDateForPage: Date): Date {\n        Calendar.getInstance().let {\n            it.time = firstDateForPage\n            it.add(Calendar.DATE, size)\n            return it.time\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

ViewPagerPagingSource.kt

\n
class ViewPagerPagingSource(\n    private val dataSource: DataSource\n) : PagingSource<Int, Date>() {\n\n    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Date> {\n        val position = params.key ?: 0\n\n        return try {\n            val data = dataSource.returnData(position)\n            LoadResult.Page(\n                data = data,\n                prevKey = if (data.isEmpty()) null else position - 1,\n                nextKey = if (data.isEmpty()) null else position + 1,\n                itemsBefore = LoadResult.Page.COUNT_UNDEFINED,\n                itemsAfter = LoadResult.Page.COUNT_UNDEFINED\n            )\n        } catch (exception: IOException) {\n            LoadResult.Error(exception)\n        }\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

所有代码工作正常。当应用程序启动时,它会加载当前日期页面。

\n

在此输入图像描述

\n

现在当我将库更新到这个版本时

\n
implementation "androidx.paging:paging-runtime-ktx:3.0.1"\n
Run Code Online (Sandbox Code Playgroud)\n

我猜它跳到-1页,看起来像这样

\n

在此输入图像描述

\n

我不明白为什么这会导致问题,而且我还编辑了ViewPagerPagingSource来实现另一种方法

\n
override fun getRefreshKey(state: PagingState<Int, Date>): Int? {\n        return state.anchorPosition\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我不明白更新库后导致问题的原因。我正在添加我的 github 存储库示例。请有人建议我代码出了什么问题?

\n

更新

\n

我尝试学习分页概念并更新我的代码。另外,按照@dlam 建议进行更改。

\n

如果我在日期前几天作为当前日期,则再次跳转一页。吉图布

\n
2021-11-21 21:20:40.820 5460-5460/com.example.viewpagerexample E/Page\xc2\xa0-1: [06/11/2021, 07/11/2021, 08/11/2021, 09/11/2021, 10/11/2021]\n2021-11-21 21:20:40.821 5460-5460/com.example.viewpagerexample E/Page\xc2\xa00: [11/11/2021, 12/11/2021, 13/11/2021, 14/11/2021, 15/11/2021]\n2021-11-21 21:20:40.821 5460-5460/com.example.viewpagerexample E/Page\xc2\xa01: [16/11/2021, 17/11/2021, 18/11/2021, 19/11/2021, 20/11/2021]\n
Run Code Online (Sandbox Code Playgroud)\n

更新2

\n

在 @MuhannadFakhouri 回答之后我尝试了这个逻辑

\n
package com.example.viewpagerexample\n\nimport java.util.*\n\nclass DataSource(\n    private val size: Int = 5,\n    private val currentDate: Date,\n    private val limitDate: Date? = null\n) {\n\n    fun returnData(pageNumber: Int): Result {\n        val dateList = mutableListOf<Date>()\n\n        val startDateForPage = startDate(pageNumber)\n        val tempCalendar = Calendar.getInstance()\n\n        tempCalendar.time = startDateForPage\n        val lastDateForPage = endDate(startDateForPage)\n\n        var index = size\n        while (tempCalendar.time <= lastDateForPage && index-- > 0) {\n            dateList.add(tempCalendar.time)\n            tempCalendar.add(Calendar.DATE, 1)\n        }\n        val limitCalendar = Calendar.getInstance().apply { limitDate }\n        return Result(\n            result = dateList,\n            pageNumber - 1,\n            if (dateList.lastOrNull()?.let { Calendar.getInstance().apply { time = it } }\n                    ?.let {\n                        it.get(Calendar.YEAR) == limitCalendar.get(Calendar.YEAR) &&\n                                it.get(Calendar.DAY_OF_YEAR) == limitCalendar.get(Calendar.DAY_OF_YEAR)\n                    } == true)\n                null\n            else\n                pageNumber + 1\n        )\n    }\n\n    private fun startDate(pageNumber: Int): Date {\n        Calendar.getInstance().let {\n            it.time = currentDate\n            it.add(Calendar.DATE, pageNumber * size)\n            return it.time\n        }\n    }\n\n    private fun endDate(firstDateForPage: Date): Date? {\n        Calendar.getInstance().let {\n            it.time = firstDateForPage\n            it.add(Calendar.DATE, size)\n\n            limitDate?.let { limit ->\n                return if (it.time > limit) limit else it.time\n            } ?: run {\n                return it.time\n            }\n        }\n    }\n\n    data class Result(\n        val result: MutableList<Date>,\n        val prevKey: Int?,\n        val nextKey: Int?\n    )\n}\n
Run Code Online (Sandbox Code Playgroud)\n

日志

\n
2022-01-08 23:39:12.601 7886-7886/com.example.viewpagerexample E/Page\xc2\xa0-1: [24/12/2021, 25/12/2021, 26/12/2021, 27/12/2021, 28/12/2021]\n2022-01-08 23:39:12.603 7886-7886/com.example.viewpagerexample E/Page\xc2\xa00: [29/12/2021, 30/12/2021, 31/12/2021, 01/01/2022, 02/01/2022]\n2022-01-08 23:39:12.604 7886-7886/com.example.viewpagerexample E/Page\xc2\xa01: [03/01/2022, 04/01/2022, 05/01/2022, 06/01/2022, 07/01/2022]\n
Run Code Online (Sandbox Code Playgroud)\n

截屏

\n

在此输入图像描述

\n

它需要显示29/12/2021日期,但它显示我03/01/2022

\n

新的详细解释

\n

Youtube 链接及详细说明

\n

首先它打开只有一个按钮的屏幕。当我点击它时,它将打开 viewpager 屏幕。每当我单击它时,都会打开不同的日期屏幕。我的要点是,如果我作为当前日期传递,它将以当前日期打开作为查看寻呼机主屏幕。如果我通过任何日期,例如29/12/2021,那么它将打开该日期。新附加的视频展示了它的行为方式。它将显示代码、日志和模拟器。

\n

当我第一次点击它打开04/01/22时,索引为-1,您可以查看视频

\n
2022-01-09 00:01:03.108 9637-9637/com.example.viewpagerexample E/Page\xc2\xa0-1: [04/01/2022, 05/01/2022, 06/01/2022, 07/01/2022, 08/01/2022]\n2022-01-09 00:01:03.109 9637-9637/com.example.viewpagerexample E/Page\xc2\xa00: [09/01/2022]\n2022-01-09 00:01:03.109 9637-9637/com.example.viewpagerexample E/Page\xc2\xa01: []\n
Run Code Online (Sandbox Code Playgroud)\n

第二次发生同样的情况

\n
2022-01-09 00:01:06.488 9637-9637/com.example.viewpagerexample E/Page\xc2\xa0-1: [04/01/2022, 05/01/2022, 06/01/2022, 07/01/2022, 08/01/2022]\n2022-01-09 00:01:06.488 9637-9637/com.example.viewpagerexample E/Page\xc2\xa00: [09/01/2022]\n2022-01-09 00:01:06.488 9637-9637/com.example.viewpagerexample E/Page\xc2\xa01: []\n
Run Code Online (Sandbox Code Playgroud)\n

第三次它于09/01/22打开,它是正确的。

\n
2022-01-09 00:01:09.181 9637-9637/com.example.viewpagerexample E/Page\xc2\xa0-1: [04/01/2022, 05/01/2022, 06/01/2022, 07/01/2022, 08/01/2022]\n2022-01-09 00:01:09.181 9637-9637/com.example.viewpagerexample E/Page\xc2\xa00: [09/01/2022]\n2022-01-09 00:01:09.181 9637-9637/com.example.viewpagerexample E/Page\xc2\xa01: []\n
Run Code Online (Sandbox Code Playgroud)\n

我的问题是,为什么这会导致此类问题有时会打开正确的索引,有时会打开错误的索引

\n

谢谢

\n

日志

\n
2022-01-09 14:15:25.501 21246-21246/com.example.viewpagerexample D/Debugging\xc2\xa0paging: load: null java.lang.Throwable\n        at com.example.viewpagerexample.ViewPagerPagingSource.load(ViewPagerPagingSource.kt:16)\n        at androidx.paging.PageFetcherSnapshot.doInitialLoad(PageFetcherSnapshot.kt:283)\n        at androidx.paging.PageFetcherSnapshot.access$doInitialLoad(PageFetcherSnapshot.kt:54)\n        at androidx.paging.PageFetcherSnapshot$pageEventFlow$1.invokeSuspend(PageFetcherSnapshot.kt:163)\n        at androidx.paging.PageFetcherSnapshot$pageEventFlow$1.invoke(Unknown Source:8)\n        at androidx.paging.PageFetcherSnapshot$pageEventFlow$1.invoke(Unknown Source:4)\n        at androidx.paging.CancelableChannelFlowKt$cancelableChannelFlow$1.invokeSuspend(CancelableChannelFlow.kt:30)\n        at androidx.paging.CancelableChannelFlowKt$cancelableChannelFlow$1.invoke(Unknown Source:8)\n        at androidx.paging.CancelableChannelFlowKt$cancelableChannelFlow$1.invoke(Unknown Source:4)\n        at androidx.paging.SimpleChannelFlowKt$simpleChannelFlow$1$1$producer$1$1.invokeSuspend(SimpleChannelFlow.kt:57)\n        at androidx.paging.SimpleChannelFlowKt$simpleChannelFlow$1$1$producer$1$1.invoke(Unknown Source:8)\n        at androidx.paging.SimpleChannelFlowKt$simpleChannelFlow$1$1$producer$1$1.invoke(Unknown Source:4)\n        at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:89)\n        at kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScope.kt:264)\n        at androidx.paging.SimpleChannelFlowKt$simpleChannelFlow$1$1$producer$1.invokeSuspend(SimpleChannelFlow.kt:52)\n        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)\n        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)\n        at kotlinx.coroutines.EventLoop.processUnconfinedEvent(EventLoop.common.kt:69)\n        at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:375)\n        at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith$default(DispatchedContinuation.kt:278)\n        at kotlinx.coroutines.DispatchedCoroutine.afterResume(Builders.common.kt:256)\n        at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:102)\n        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)\n        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)\n        at android.os.Handler.handleCallback(Handler.java:883)\n        at android.os.Handler.dispatchMessage(Handler.java:100)\n        at android.os.Looper.loop(Looper.java:214)\n        at android.app.ActivityThread.main(ActivityThread.java:7697)\n        at java.lang.reflect.Method.invoke(Native Method)\n        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516)\n        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)\n2022-01-09 14:15:25.512 21246-21246/com.example.viewpagerexample D/Debugging\xc2\xa0paging: load: -1 java.lang.Throwable\n        at com.example.viewpagerexample.ViewPagerPagingSource.load(ViewPagerPagingSource.kt:16)\n        at androidx.paging.PageFetcherSnapshot.doLoad(PageFetcherSnapshot.kt:406)\n        at androidx.paging.PageFetcherSnapshot.access$doLoad(PageFetcherSnapshot.kt:54)\n        at androidx.paging.PageFetcherSnapshot$collectAsGenerationalViewportHints$$inlined$collect$1.emit(Collect.kt:135)\n        at kotlinx.coroutines.flow.FlowKt__ChannelsKt.emitAllImpl$FlowKt__ChannelsKt(Channels.kt:62)\n        at kotlinx.coroutines.flow.FlowKt__ChannelsKt.access$emitAllImpl$FlowKt__ChannelsKt(Channels.kt:1)\n        at kotlinx.coroutines.flow.FlowKt__ChannelsKt$emitAllImpl$1.invokeSuspend(Unknown Source:14)\n        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)\n        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)\n        at kotlinx.coroutines.EventLoop.processUnconfinedEvent(EventLoop.common.kt:69)\n        at kotlinx.coroutines.DispatchedTaskKt.resumeUnconfined(DispatchedTask.kt:244)\n        at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:161)\n        at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:397)\n        at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:431)\n        at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:420)\n        at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:328)\n        at kotlinx.coroutines.flow.SharedFlowImpl.tryEmit(SharedFlow.kt:368)\n        at androidx.paging.HintHandler$HintFlow.setValue(HintHandler.kt:133)\n        at androidx.paging.HintHandler$processHint$1.invoke(HintHandler.kt:85)\n        at androidx.paging.HintHandler$processHint$1.invoke(HintHandler.kt:79)\n        at androidx.paging.HintHandler$State.modify(HintHandler.kt:119)\n        at androidx.paging.HintHandler.processHint(HintHandler.kt:79)\n        at androidx.paging.PageFetcherSnapshot.accessHint(PageFetcherSnapshot.kt:197)\n        at androidx.paging.PageFetcher$PagerUiReceiver.accessHint(PageFetcher.kt:215)\n        at androidx.paging.PagingDataDiffer$collectFrom$2$1$1.invokeSuspend(PagingDataDiffer.kt:175)\n        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)\n        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)\n        at android.os.Handler.handleCallback(Handler.java:883)\n        at android.os.Handler.dispatchMessage(Handler.java:100)\n        at android.os.Looper.loop(Looper.java:214)\n        at android.app.ActivityThread.main(ActivityThread.java:7697)\n        at java.lang.reflect.Method.invoke(Native Method)\n        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516)\n        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)\n2022-01-09 14:15:25.519 21246-21246/com.example.viewpagerexample D/Debugging\xc2\xa0paging: load: -2 java.lang.Throwable\n        at com.example.viewpagerexample.ViewPagerPagingSource.load(ViewPagerPagingSource.kt:16)\n        at androidx.paging.PageFetcherSnapshot.doLoad(PageFetcherSnapshot.kt:406)\n        at androidx.paging.PageFetcherSnapshot.access$doLoad(PageFetcherSnapshot.kt:54)\n        at androidx.paging.PageFetcherSnapshot$collectAsGenerationalViewportHints$$inlined$collect$1.emit(Collect.kt:135)\n        at kotlinx.coroutines.flow.FlowKt__ChannelsKt.emitAllImpl$FlowKt__ChannelsKt(Channels.kt:62)\n        at kotlinx.coroutines.flow.FlowKt__ChannelsKt.access$emitAllImpl$FlowKt__ChannelsKt(Channels.kt:1)\n        at kotlinx.coroutines.flow.FlowKt__ChannelsKt$emitAllImpl$1.invokeSuspend(Unknown Source:14)\n        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)\n        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)\n        at kotlinx.coroutines.EventLoop.processUnconfinedEvent(EventLoop.common.kt:69)\n        at kotlinx.coroutines.DispatchedTaskKt.resumeUnconfined(DispatchedTask.kt:244)\n        at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:161)\n        at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:397)\n        at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:431)\n        at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:420)\n        at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:328)\n        at kotlinx.coroutines.flow.SharedFlowImpl.tryEmit(SharedFlow.kt:368)\n        at androidx.paging.HintHandler$HintFlow.setValue(HintHandler.kt:133)\n        at androidx.paging.HintHandler$processHint$1.invoke(HintHandler.kt:85)\n        at androidx.paging.HintHandler$processHint$1.invoke(HintHandler.kt:79)\n        at androidx.paging.HintHandler$State.modify(HintHandler.kt:119)\n        at androidx.paging.HintHandler.processHint(HintHandler.kt:79)\n        at androidx.paging.PageFetcherSnapshot.accessHint(PageFetcherSnapshot.kt:197)\n        at androidx.paging.PageFetcher$PagerUiReceiver.accessHint(PageFetcher.kt:215)\n        at androidx.paging.PagingDataDiffer.get(PagingDataDiffer.kt:271)\n        at androidx.paging.AsyncPagingDataDiffer.getItem(AsyncPagingDataDiffer.kt:214)\n        at androidx.paging.PagingDataAdapter.getItem(PagingDataAdapter.kt:231)\n        at com.example.viewpagerexample.ViewPagerAdapter.onBindViewHolder(ViewPagerAdapter.kt:11)\n        at com.example.viewpagerexample.ViewPagerAdapter.onBindViewHolder(ViewPagerAdapter.kt:8)\n        at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:7254)\n        at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7337)\n        at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:6194)\n        at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6460)\n        at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6300)\n        at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6296)\n        at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2330)\n        at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1631)\n        at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1591)\n        at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:668)\n        at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4309)\n        at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:4012)\n        at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4578)\n        at android.view.View.layout(View.java:22085)\n        at android.view.ViewGroup.layout(ViewGroup.java:6290)\n        at androidx.viewpager2.widget.ViewPager2.onLayout(ViewPager2.java:527)\n        at android.view.View.layout(View.java:22085)\n        at android.view.ViewGroup.layout(ViewGroup.java:6290)\n        at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1873\n
Run Code Online (Sandbox Code Playgroud)\n

dla*_*lam 1

我注意到您在这里提交了一个错误: https: //issuetracker.google.com/204328119,但我也想为其他未来阅读此问题的人更新此答案。

核心问题是,在 ViewPager 准备好开始绑定此项目之前,您就开始在 Paging 上进行收集。您应该使用lifecycleScope.launchWhenCreated而不是lifecycleScope.launch解决此问题:

lifecycleScope.launchWhenCreated {
  viewModel.dataList.collectLatest {
    adapter.submitData(it)
  }
}
Run Code Online (Sandbox Code Playgroud)

我注意到的另一个问题(也可以解决这个问题)是您启用了占位符,但没有传入itemsBeforeitemsAfter传入LoadResult.Page. 启用占位符并具有静态计数也会为您的视图提供要绑定的列表大小,但由于您传递了COUNT_UNDEFINED, Paging 无法null正确用占位符填充列表,因为它不知道要添加多少个。