kotlin 将分页3 alpha更新为稳定导致索引问题Android

qnyhuwrf  于 2023-03-30  发布在  Kotlin
关注(0)|答案(2)|浏览(159)

嘿,我用的是Paging 3库和ViewPager 2,里面加载的数据是无限的。

implementation "androidx.paging:paging-runtime-ktx:3.0.0-alpha07"

数据源.kt

package com.example.viewpagerexample

import java.util.*

class DataSource(
    private val size: Int = 5,
    private val currentDate: Date,
    private val limitDate: Date?
) {

    fun returnData(pageNumber: Int): List<Date> {

        val dateList = mutableListOf<Date>()
        val startDateForPage = startDate(pageNumber)
        val tempCalendar = Calendar.getInstance()

        tempCalendar.time = startDateForPage
        val lastDateForPage = endDate(startDateForPage)

        while (tempCalendar.time < lastDateForPage) {
            if (limitDate == null ||
                tempCalendar.time.before(limitDate) ||
                tempCalendar.time == limitDate
            ) {
                dateList.add(tempCalendar.time)
                tempCalendar.add(Calendar.DATE, 1)
            } else {
                break
            }
        }
        return dateList
    }

    private fun startDate(pageNumber: Int): Date {
        if (pageNumber == 0) {
            return currentDate
        } else {
            Calendar.getInstance().let {
                it.time = currentDate
                it.add(Calendar.DATE, pageNumber * size)
                return it.time
            }
        }
    }

    private fun endDate(firstDateForPage: Date): Date {
        Calendar.getInstance().let {
            it.time = firstDateForPage
            it.add(Calendar.DATE, size)
            return it.time
        }
    }
}

ViewPagerPagingSource.kt

class ViewPagerPagingSource(
    private val dataSource: DataSource
) : PagingSource<Int, Date>() {

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Date> {
        val position = params.key ?: 0

        return try {
            val data = dataSource.returnData(position)
            LoadResult.Page(
                data = data,
                prevKey = if (data.isEmpty()) null else position - 1,
                nextKey = if (data.isEmpty()) null else position + 1,
                itemsBefore = LoadResult.Page.COUNT_UNDEFINED,
                itemsAfter = LoadResult.Page.COUNT_UNDEFINED
            )
        } catch (exception: IOException) {
            LoadResult.Error(exception)
        }
    }

}

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

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

implementation "androidx.paging:paging-runtime-ktx:3.0.1"

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

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

override fun getRefreshKey(state: PagingState<Int, Date>): Int? {
        return state.anchorPosition
}

我不明白更新库后是什么原因导致了这个问题。我正在添加我的github仓库Example。请有人告诉我代码中出现了什么问题?

更新

我试着学习分页的概念并更新我的代码。另外,按照@dlam的建议做一些修改。
再次是跳转1页,如果我通过前几天的日期作为当前日期。Github

2021-11-21 21:20:40.820 5460-5460/com.example.viewpagerexample E/Page -1: [06/11/2021, 07/11/2021, 08/11/2021, 09/11/2021, 10/11/2021]
2021-11-21 21:20:40.821 5460-5460/com.example.viewpagerexample E/Page 0: [11/11/2021, 12/11/2021, 13/11/2021, 14/11/2021, 15/11/2021]
2021-11-21 21:20:40.821 5460-5460/com.example.viewpagerexample E/Page 1: [16/11/2021, 17/11/2021, 18/11/2021, 19/11/2021, 20/11/2021]

更新2
@MuhannadFakhouri回答后我尝试了这个逻辑

package com.example.viewpagerexample

import java.util.*

class DataSource(
    private val size: Int = 5,
    private val currentDate: Date,
    private val limitDate: Date? = null
) {

    fun returnData(pageNumber: Int): Result {
        val dateList = mutableListOf<Date>()

        val startDateForPage = startDate(pageNumber)
        val tempCalendar = Calendar.getInstance()

        tempCalendar.time = startDateForPage
        val lastDateForPage = endDate(startDateForPage)

        var index = size
        while (tempCalendar.time <= lastDateForPage && index-- > 0) {
            dateList.add(tempCalendar.time)
            tempCalendar.add(Calendar.DATE, 1)
        }
        val limitCalendar = Calendar.getInstance().apply { limitDate }
        return Result(
            result = dateList,
            pageNumber - 1,
            if (dateList.lastOrNull()?.let { Calendar.getInstance().apply { time = it } }
                    ?.let {
                        it.get(Calendar.YEAR) == limitCalendar.get(Calendar.YEAR) &&
                                it.get(Calendar.DAY_OF_YEAR) == limitCalendar.get(Calendar.DAY_OF_YEAR)
                    } == true)
                null
            else
                pageNumber + 1
        )
    }

    private fun startDate(pageNumber: Int): Date {
        Calendar.getInstance().let {
            it.time = currentDate
            it.add(Calendar.DATE, pageNumber * size)
            return it.time
        }
    }

    private fun endDate(firstDateForPage: Date): Date? {
        Calendar.getInstance().let {
            it.time = firstDateForPage
            it.add(Calendar.DATE, size)

            limitDate?.let { limit ->
                return if (it.time > limit) limit else it.time
            } ?: run {
                return it.time
            }
        }
    }

    data class Result(
        val result: MutableList<Date>,
        val prevKey: Int?,
        val nextKey: Int?
    )
}

日志

2022-01-08 23:39:12.601 7886-7886/com.example.viewpagerexample E/Page -1: [24/12/2021, 25/12/2021, 26/12/2021, 27/12/2021, 28/12/2021]
2022-01-08 23:39:12.603 7886-7886/com.example.viewpagerexample E/Page 0: [29/12/2021, 30/12/2021, 31/12/2021, 01/01/2022, 02/01/2022]
2022-01-08 23:39:12.604 7886-7886/com.example.viewpagerexample E/Page 1: [03/01/2022, 04/01/2022, 05/01/2022, 06/01/2022, 07/01/2022]

截图

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

新的详细说明

Youtube Link with detail explanination
首先,它打开屏幕,其中只有一个按钮。当我点击这个它会打开viewpager屏幕。每当我点击它打开不同的日期屏幕。我的主要观点是,如果我通过当前日期它将打开当前日期作为查看寻呼机主屏幕。如果我通过任何日期,例如29/12/2021,它将打开此日期。新的附加视频显示它的行为。它将显示代码,日志和模拟器。

第一次点击打开04/01/22索引**-1**可以查看视频

2022-01-09 00:01:03.108 9637-9637/com.example.viewpagerexample E/Page -1: [04/01/2022, 05/01/2022, 06/01/2022, 07/01/2022, 08/01/2022]
2022-01-09 00:01:03.109 9637-9637/com.example.viewpagerexample E/Page 0: [09/01/2022]
2022-01-09 00:01:03.109 9637-9637/com.example.viewpagerexample E/Page 1: []

第二次也是一样

2022-01-09 00:01:06.488 9637-9637/com.example.viewpagerexample E/Page -1: [04/01/2022, 05/01/2022, 06/01/2022, 07/01/2022, 08/01/2022]
2022-01-09 00:01:06.488 9637-9637/com.example.viewpagerexample E/Page 0: [09/01/2022]
2022-01-09 00:01:06.488 9637-9637/com.example.viewpagerexample E/Page 1: []

第三次它打开09/01/22它纠正。

2022-01-09 00:01:09.181 9637-9637/com.example.viewpagerexample E/Page -1: [04/01/2022, 05/01/2022, 06/01/2022, 07/01/2022, 08/01/2022]
2022-01-09 00:01:09.181 9637-9637/com.example.viewpagerexample E/Page 0: [09/01/2022]
2022-01-09 00:01:09.181 9637-9637/com.example.viewpagerexample E/Page 1: []

我的问题是,为什么这会导致这种问题,有时打开正确的索引,有时它会打开错误的索引
谢谢
日志

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

31moq8wy1#

我注意到你在这里提交了一个bug:https://issuetracker.google.com/204328119,但我也想更新这个答案,为其他未来的人阅读这一问题的SO。
核心问题是在ViewPager准备好开始绑定这些项目之前,您就开始在Paging上收集。您应该使用lifecycleScope.launchWhenCreated而不是lifecycleScope.launch来解决这个问题:

lifecycleScope.launchWhenCreated {
  viewModel.dataList.collectLatest {
    adapter.submitData(it)
  }
}

我注意到的另一个问题是(这也可以解决这个问题),就是你启用了占位符,但是没有在LoadResult.Page中传入itemsBeforeitemsAfter。启用占位符和静态计数也会给予你的视图一个要绑定的列表大小,但是因为你传入了COUNT_UNDEFINED,分页无法正确地用null占位符填充列表,因为它不知道要添加多少占位符。

v7pvogib

v7pvogib2#

我没有深入研究这一点以及这与库版本的关系,但我注意到您的数据源逻辑中存在一个小故障,您正在进行以下检查

if (lastDateForPage == limitDate)
                    null
                else
                    pageNumber + 1

在此循环之后

while (tempCalendar.time <= lastDateForPage && index-- > 0) {
            dateList.add(tempCalendar.time)
            tempCalendar.add(Calendar.DATE, 1)
        }

循环实际上可能会在没有达到限制的情况下中断,这意味着你的数据源不能保证总是达到limitDate。
更改条件lastDateForPage == limitDate以检查以下内容,应该可以解决此问题

val limitCalendar = Calendar.getInstance().apply { limitDate }

            if (dateList.lastOrNull()?.let { Calendar.getInstance().apply { time = it } }
                    ?.let {
                        it.get(Calendar.YEAR) == limitCalendar.get(Calendar.YEAR) &&
                                it.get(Calendar.DAY_OF_YEAR) == limitCalendar.get(Calendar.DAY_OF_YEAR)
                    } == true)
                null
            else
                pageNumber + 1

相关问题