Jetpack Compose:使用Paging 3的LazyColumn触发数组越界异常
问题:Compose LazyColumn结合Paging3搜索过滤时抛出IndexOutOfBoundsException
场景说明
使用LazyColumn展示来自Room的分页数据,参考官方文档实现了结合Paging3的自定义PaginatedLazyColumn。首次打开页面时数据展示正常,但通过搜索过滤列表时,抛出IndexOutOfBoundsException异常。
核心实现代码
1. 自定义PaginatedLazyColumn
@Composable fun <T : Any> PaginatedLazyColumn( items: LazyPagingItems<T>, pageLoadContent: @Composable (LazyItemScope.() -> Unit), pageErrorContent: @Composable (LazyItemScope.(throwable: Throwable) -> Unit), modifier: Modifier = Modifier, state: LazyListState = rememberLazyListState(), itemContent: @Composable (LazyItemScope.(item: T, index: Int) -> Unit), ) { LazyColumn( modifier = modifier, state = state, ) { when (val loadState = items.loadState.prepend) { is LoadState.NotLoading -> Unit is LoadState.Loading -> item { pageLoadContent() } is LoadState.Error -> item { pageErrorContent(loadState.error) } } items( count = items.itemCount, ) { index -> val item = items[index] item?.let { itemContent(it, index) } } when (val loadState = items.loadState.append) { is LoadState.NotLoading -> Unit is LoadState.Loading -> item { pageLoadContent() } is LoadState.Error -> item { pageErrorContent(loadState.error) } } } }
2. ViewModel中暴露分页数据流
val items: Flow<PagingData<SubscriptionItem>> = filterParams.flatMapLatest { interactor.getList( it.query.nullifyBlank(), it.filter.getVideoType(), PagingConfig(pageSize = PagingUtils.LIST_PAGE_SIZE) ) }.cachedIn(viewModelScope)
3. Interactor层创建Pager
override fun getList( query: String?, videoType: VideoType?, tmdbPosterPathFormat: String, languageId: Long, config: PagingConfig ): Flow<PagingData<SubscriptionItem>> { val pagingSourceFactory = { subscriptionDao.getList(query.like(), videoType, tmdbPosterPathFormat, languageId) } return Pager( config = config, pagingSourceFactory = pagingSourceFactory ).flow.map { pagingData -> pagingData.map { item -> when (item.type) { VideoType.Series -> SubscriptionItem.Series( item.id, tmdbId = item.tmdbId, name = item.name, imageUrl = item.image, year = item.releaseYear, status = SeriesStatus.parse(item.status) ) VideoType.Movie -> SubscriptionItem.Movie( item.id, tmdbId = item.tmdbId, title = item.name, imageUrl = item.image, year = item.releaseYear, status = MovieStatus.parse(item.status) ) } } } }
异常堆栈信息
java.lang.IndexOutOfBoundsException: Index 1, size 1 at androidx.compose.foundation.lazy.layout.MutableIntervalList.checkIndexBounds(IntervalList.kt:183) at androidx.compose.foundation.lazy.layout.MutableIntervalList.get(IntervalList.kt:166) at androidx.compose.foundation.lazy.layout.LazyLayoutIntervalContent.getKey(LazyLayoutIntervalContent.kt:86) at androidx.compose.foundation.lazy.LazyListItemProviderImpl.getKey(LazyListItemProvider.kt:85) at androidx.compose.foundation.lazy.LazyListMeasuredItemProvider.getAndMeasure(LazyListMeasuredItemProvider.kt:46) at androidx.compose.foundation.lazy.LazyListMeasureKt.createItemsAfterList(LazyListMeasure.kt:418) at androidx.compose.foundation.lazy.LazyListMeasureKt.measureLazyList-50ZNEM8(LazyListMeasure.kt:278) at androidx.compose.foundation.lazy.LazyListKt$rememberLazyListMeasurePolicy$1$1.invoke-0kLqBqw(LazyList.kt:328) at androidx.compose.foundation.lazy.LazyListKt$rememberLazyListMeasurePolicy$1$1.invoke(LazyList.kt:193) at androidx.compose.foundation.lazy.layout.LazyLayoutKt$LazyLayout$3$2$1.invoke-0kLqBqw(LazyLayout.kt:107) at androidx.compose.foundation.lazy.layout.LazyLayoutKt$LazyLayout$3$2$1.invoke(LazyLayout.kt:100) at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$createMeasurePolicy$1.measure-3p2s80s(SubcomposeLayout.kt:708) at androidx.compose.ui.node.InnerNodeCoordinator$LookaheadDelegateImpl.measure-BRTryo0(InnerNodeCoordinator.kt:81) at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier.measure-3p2s80s(GraphicsLayerModifier.kt:646) at androidx.compose.ui.node.LayoutModifierNodeCoordinator$LookaheadDelegateForLayoutModifierNode.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:64) at androidx.compose.foundation.layout.FillNode.measure-3p2s80s(Size.kt:699) at androidx.compose.ui.node.LayoutModifierNodeCoordinator$LookaheadDelegateForLayoutModifierNode.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:64) at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performLookaheadMeasure$1.invoke(LayoutNodeLayoutDelegate.kt:1622) at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performLookaheadMeasure$1.invoke(LayoutNodeLayoutDelegate.kt:1621) at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2303) at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:496) at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:256) at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:133) at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release(OwnerSnapshotObserver.kt:111) at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release$default(OwnerSnapshotObserver.kt:105) at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performLookaheadMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1621) at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performLookaheadMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:36) at androidx.compose.ui.node.LayoutNodeLayoutDelegate$LookaheadPassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1289) at androidx.compose.ui.node.LayoutNode.lookaheadRemeasure-_Sx5XlM$ui_release(LayoutNode.kt:1126) at androidx.compose.ui.node.LayoutNode.lookaheadRemeasure-_Sx5XlM$ui_release$default(LayoutNode.kt:1120) at androidx.compose.ui.node.MeasureAndLayoutDelegate.doLookaheadRemeasure-sdFAvZA(MeasureAndLayoutDelegate.kt:313) at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureAndRelayoutIfNeeded(MeasureAndLayoutDelegate.kt:478) at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureAndRelayoutIfNeeded$default(MeasureAndLayoutDelegate.kt:461) at androidx.compose.ui.node.MeasureAndLayoutDelegate.measureAndLayout(MeasureAndLayoutDelegate.kt:354) at androidx.compose.ui.platform.AndroidComposeView.measureAndLayout(AndroidComposeView.android.kt:969) at androidx.compose.ui.node.Owner.measureAndLayout$default(Owner.kt:230) at androidx.compose.ui.platform.AndroidComposeView.dispatchDraw(AndroidComposeView.android.kt:1222) at android.view.View.draw(View.java:22576) at android.view.View.updateDisplayListIfDirty(View.java:21434) at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4535) at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4508) at android.view.View.updateDisplayListIfDirty(View.java:21392) at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4535) at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4508) at android.view.View.updateDisplayListIfDirty(View.java:21392) at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4535) at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4508) at android.view.View.updateDisplayListIfDirty(View.java:21392) at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4535) at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4508) at android.view.View.updateDisplayListIfDirty(View.java:21392) at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:559) at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:567) at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:651) at android.view.ViewRootImpl.draw(ViewRootImpl.java:4316) at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:4017) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3278) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2078) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8553) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1142) at android.view.Choreographer.doCallbacks(Choreographer.java:962) at android.view.Choreographer.doFrame(Choreographer.java:887) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1127) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:237) at android.app.ActivityThread.main(ActivityThread.java:8090) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)
使用依赖版本
- Room 2.6.0
- Compose 2023.10.01
- Paging 3.2.1
内容的提问来源于stack exchange,提问作者unveloper
相关产品推荐
相关产品推荐

