1

我在 Android Jetpack Compose 项目中使用 Accompanist Horizo​​ntalPager 来显示来自 Firebase Firestore 的动态变化列表。如果列表最初为空或有项目,它会很好地工作,但是一旦它有一些项目然后变为空,应用程序会因来自 Horizo​​ntalPager 的 NullPointerException 而崩溃。

以下是使用的相关依赖项。

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)
4

1 回答 1

3

代码中的第一次崩溃是伴奏错误的结果。HorizontalPagerIndicator虽然它没有固定,但您只需要包装if

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

修复此崩溃后,您将面临另一次崩溃。发生这种情况是因为您正在调用calculateCurrentOffsetForPageinside graphicsLayerpagerList更改后调用

您可以通过将此计算移出修改器来轻松解决此问题:

// 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)
            )
        }
        ...
)
于 2021-08-14T10:42:43.787 回答