Сбой Android-приложения из-за исключения NullPointerException в сопровождении HorizontalPager for Jetpack Compose

Я использую Performpanist HorizontalPager в проекте Android Jetpack Compose, чтобы показать динамически изменяющийся список из Firebase Firestore. Он работает хорошо, если список изначально пуст или содержит элементы, но как только в нем есть элементы, а затем он становится пустым, приложение вылетает из-за исключения NullPointerException из HorizontalPager.

Ниже приведены соответствующие зависимости.

      compose_version = '1.0.1'
hilt_version = '2.38.1'
kotlin_version = '1.5.21'

// Accompanist
def accompanistVersion = "0.16.1"
implementation("com.google.accompanist:accompanist-pager:$accompanistVersion")
implementation("com.google.accompanist:accompanist-pager-indicators:$accompanistVersion")
implementation("com.google.accompanist:accompanist-systemuicontroller:$accompanistVersion")
implementation("com.google.accompanist:accompanist-insets:$accompanistVersion")
implementation("com.google.accompanist:accompanist-flowlayout:$accompanistVersion")
implementation("com.google.accompanist:accompanist-swiperefresh:$accompanistVersion")

def coil = "1.3.2"
implementation("io.coil-kt:coil:$coil")
implementation("io.coil-kt:coil-compose:$coil")

Ниже приведен фрагмент кода.

      val pagerList: List<PagerDomain> by viewModel.pagerList.collectAsState()
val pagerState: PagerState = rememberPagerState(pageCount = pagerList.size)

Column(
    modifier = Modifier
        .fillMaxSize(),
    horizontalAlignment = Alignment.CenterHorizontally,
    verticalArrangement = Arrangement.Bottom
) {

    HorizontalPager(
        modifier = Modifier
            .fillMaxWidth(),
        horizontalAlignment = Alignment.CenterHorizontally,
        state = pagerState,
    ) { page: Int ->
        ProfileCarouselItem(
            modifier = Modifier
                .graphicsLayer {
                    // Calculate the absolute offset for the current page from the
                    // scroll position. We use the absolute value which allows us to mirror
                    // any effects for both directions
                    val pageOffset =
                        calculateCurrentOffsetForPage(page).absoluteValue

                    // We animate the scaleX + scaleY, between 85% and 100%
                    lerp(
                        start = 0.85f,
                        stop = 1f,
                        fraction = 1f - pageOffset.coerceIn(0f, 1f)
                    ).also { scale ->
                        scaleX = scale
                        scaleY = scale
                    }

                    // We animate the alpha, between 50% and 100%
                    alpha = lerp(
                        start = 0.5f,
                        stop = 1f,
                        fraction = 1f - pageOffset.coerceIn(0f, 1f)
                    )
                }
                .fillMaxWidth(0.8f)
                .aspectRatio(0.5f),
            pagerDomain = pagerList[page],
            onConnectClick = onConnectClick,
            showConnectLoading = showConnectLoading
        )
    }
    HorizontalPagerIndicator(
        pagerState = pagerState,
        modifier = Modifier
            .padding(16.dp),
    )
}

Ниже представлена ​​трассировка стека.

      Fatal Exception: java.lang.NullPointerException
com.google.accompanist.pager.PagerState.getCurrentPageOffset (PagerState.kt:745)
com.google.accompanist.pager.PagerIndicatorKt$HorizontalPagerIndicator$1$2$1.invoke-Bjo55l4 (PagerIndicator.kt:95)
com.google.accompanist.pager.PagerIndicatorKt$HorizontalPagerIndicator$1$2$1.invoke (PagerIndicator.kt:94)
androidx.compose.foundation.layout.OffsetPxModifier$measure$1.invoke (Offset.kt:202)
androidx.compose.foundation.layout.OffsetPxModifier$measure$1.invoke (Offset.kt:201)
androidx.compose.ui.layout.MeasureScope$layout$1.placeChildren (MeasureScope.kt:68)
androidx.compose.ui.node.DelegatingLayoutNodeWrapper.placeAt-f8xVGno (DelegatingLayoutNodeWrapper.kt:111)
androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno (Placeable.kt:31)
androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50 (Placeable.kt:370)
androidx.compose.ui.node.OuterMeasurablePlaceable.placeAt-f8xVGno (OuterMeasurablePlaceable.kt:149)
androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno (Placeable.kt:31)
androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50 (Placeable.kt:370)
androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50$default (Placeable.kt:203)
androidx.compose.foundation.layout.BoxKt.placeInBox (Box.kt:186)
androidx.compose.foundation.layout.BoxKt.access$placeInBox (Box.kt:1)
androidx.compose.foundation.layout.BoxKt$boxMeasurePolicy$1$measure$5.invoke (Box.kt:167)
androidx.compose.foundation.layout.BoxKt$boxMeasurePolicy$1$measure$5.invoke (Box.kt:163)
androidx.compose.ui.layout.MeasureScope$layout$1.placeChildren (MeasureScope.kt:68)
androidx.compose.ui.node.LayoutNode$layoutChildren$1.invoke (LayoutNode.kt:925)
androidx.compose.ui.node.LayoutNode$layoutChildren$1.invoke (LayoutNode.kt:915)
androidx.compose.runtime.snapshots.Snapshot$Companion.observe (Snapshot.kt:1776)
androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads (SnapshotStateObserver.kt:123)
androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release (OwnerSnapshotObserver.kt:75)
androidx.compose.ui.node.OwnerSnapshotObserver.observeLayoutSnapshotReads$ui_release (OwnerSnapshotObserver.kt:56)
androidx.compose.ui.node.LayoutNode.layoutChildren$ui_release (LayoutNode.kt:915)
androidx.compose.ui.node.LayoutNode.onNodePlaced$ui_release (LayoutNode.kt:901)
androidx.compose.ui.node.InnerPlaceable.placeAt-f8xVGno (InnerPlaceable.kt:94)
androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno (Placeable.kt:31)
androidx.compose.ui.layout.Placeable$PlacementScope.placeRelative (Placeable.kt:359)
androidx.compose.ui.layout.Placeable$PlacementScope.placeRelative$default (Placeable.kt:179)
androidx.compose.foundation.layout.PaddingModifier$measure$1.invoke (Padding.kt:370)
androidx.compose.foundation.layout.PaddingModifier$measure$1.invoke (Padding.kt:368)
androidx.compose.ui.layout.MeasureScope$layout$1.placeChildren (MeasureScope.kt:68)
androidx.compose.ui.node.DelegatingLayoutNodeWrapper.placeAt-f8xVGno (DelegatingLayoutNodeWrapper.kt:111)
androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno (Placeable.kt:31)
androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50 (Placeable.kt:370)
androidx.compose.ui.node.OuterMeasurablePlaceable.placeAt-f8xVGno (OuterMeasurablePlaceable.kt:149)
androidx.compose.ui.node.OuterMeasurablePlaceable.replace (OuterMeasurablePlaceable.kt:161)
androidx.compose.ui.node.LayoutNode.replace$ui_release (LayoutNode.kt:811)
androidx.compose.ui.node.MeasureAndLayoutDelegate.measureAndLayout (MeasureAndLayoutDelegate.kt:215)
androidx.compose.ui.platform.AndroidComposeView.measureAndLayout (AndroidComposeView.android.kt:510)
androidx.compose.ui.platform.AndroidComposeView.dispatchDraw (AndroidComposeView.android.kt:666)
android.view.View.draw (View.java:23904)
android.view.View.updateDisplayListIfDirty (View.java:22776)
android.view.ViewGroup.recreateChildDisplayList (ViewGroup.java:5320)
android.view.ViewGroup.dispatchGetDisplayList (ViewGroup.java:5292)
android.view.View.updateDisplayListIfDirty (View.java:22731)
android.view.ViewGroup.recreateChildDisplayList (ViewGroup.java:5320)
android.view.ViewGroup.dispatchGetDisplayList (ViewGroup.java:5292)
android.view.View.updateDisplayListIfDirty (View.java:22731)
android.view.ViewGroup.recreateChildDisplayList (ViewGroup.java:5320)
android.view.ViewGroup.dispatchGetDisplayList (ViewGroup.java:5292)
android.view.View.updateDisplayListIfDirty (View.java:22731)
android.view.ViewGroup.recreateChildDisplayList (ViewGroup.java:5320)
android.view.ViewGroup.dispatchGetDisplayList (ViewGroup.java:5292)
android.view.View.updateDisplayListIfDirty (View.java:22731)
android.view.ThreadedRenderer.updateViewTreeDisplayList (ThreadedRenderer.java:579)
android.view.ThreadedRenderer.updateRootDisplayList (ThreadedRenderer.java:585)
android.view.ThreadedRenderer.draw (ThreadedRenderer.java:662)
android.view.ViewRootImpl.draw (ViewRootImpl.java:5042)
android.view.ViewRootImpl.performDraw (ViewRootImpl.java:4749)
android.view.ViewRootImpl.performTraversals (ViewRootImpl.java:3866)
android.view.ViewRootImpl.doTraversal (ViewRootImpl.java:2618)
android.view.ViewRootImpl$TraversalRunnable.run (ViewRootImpl.java:9965)
android.view.Choreographer$CallbackRecord.run (Choreographer.java:1010)
android.view.Choreographer.doCallbacks (Choreographer.java:809)
android.view.Choreographer.doFrame (Choreographer.java:744)
android.view.Choreographer$FrameDisplayEventReceiver.run (Choreographer.java:995)
android.os.Handler.handleCallback (Handler.java:938)
android.os.Handler.dispatchMessage (Handler.java:99)
android.os.Looper.loop (Looper.java:246)
android.app.ActivityThread.main (ActivityThread.java:8506)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:602)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1130)

1 ответ

Первый сбой в вашем коде - это результат ошибки аккомпаниатора . Пока это не исправлено, вам просто нужно обернуть HorizontalPagerIndicator с if:

      if (pagerState.pageCount != 0) {
    HorizontalPagerIndicator(
        pagerState = pagerState,
        modifier = Modifier
            .padding(16.dp),
    )
}

После того, как вы исправите этот сбой, вы столкнетесь с другим. Это происходит потому что ты звонишь calculateCurrentOffsetForPage внутри graphicsLayer, который вызывается после pagerList изменять

Вы можете легко решить эту проблему, убрав этот расчет из модификатора:

      // Calculate the absolute offset for the current page from the
// scroll position. We use the absolute value which allows us to mirror
// any effects for both directions
val pageOffset = calculateCurrentOffsetForPage(page).absoluteValue

ProfileCarouselItem(
    modifier = Modifier
        .graphicsLayer {
            // We animate the scaleX + scaleY, between 85% and 100%
            lerp(
                start = 0.85f,
                stop = 1f,
                fraction = 1f - pageOffset.coerceIn(0f, 1f)
            ).also { scale ->
                scaleX = scale
                scaleY = scale
            }

            // We animate the alpha, between 50% and 100%
            alpha = lerp(
                start = 0.5f,
                stop = 1f,
                fraction = 1f - pageOffset.coerceIn(0f, 1f)
            )
        }
        ...
)
Другие вопросы по тегам