You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将带Fragment的ViewPager2迁移至HorizontalPager时滑动崩溃问题

问题:HorizontalPager滑动时触发"The specified child already has a parent"异常

我正逐步将基于Fragment的ViewPager2迁移为Compose的HorizontalPager,但滑动HorizontalPager时触发java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first异常。尝试过移除Fragment视图(如调用前一个Fragment的onDestroyView、从父容器移除视图等操作),但均未解决问题。

问题代码

@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun ViewPager(
    data: List<Data>,
    fragmentManager: FragmentManager,
    fragmentContainerView: FragmentContainerView,
    currentItem: Int
) {

    val pagerState = rememberPagerState(
        initialPage = currentItem,
        pageCount = {
            data.size
        })
    HorizontalPager(state = pagerState, userScrollEnabled = true) { page -> //if userScrollEnabled is set to false fragment does not crash but scroll not works
        val fragment = AboutAppFragment()
        AndroidView(
            modifier =  Modifier.fillMaxSize(),
            factory = {
                val currentFragment = fragmentManager.findFragmentByTag("TEST_TAG")

                val transaction = fragmentManager.beginTransaction()
                currentFragment?.onDestroyView()
                currentFragment?.let(transaction::remove)

                transaction.commit()
                val transaction2 = fragmentManager.beginTransaction()
                transaction2.replace(fragmentContainerView.id, fragment, "TEST_TAG")
                transaction2.commit()
                fragmentContainerView
            }
        )
    }
}


private fun getFragmentContainer() =
    FragmentContainerView(context = this@Activity).apply {
        id = View.generateViewId()
    }

完整堆栈信息

java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
    at android.view.ViewGroup.addViewInner(ViewGroup.java:5256)
    at android.view.ViewGroup.addView(ViewGroup.java:5085)
    at android.view.ViewGroup.addView(ViewGroup.java:5025)
    at android.view.ViewGroup.addView(ViewGroup.java:4997)
    at androidx.compose.ui.viewinterop.AndroidViewHolder.<init>(AndroidViewHolder.android.kt:98)
    at androidx.compose.ui.viewinterop.ViewFactoryHolder.<init>(AndroidView.android.kt:331)
    at androidx.compose.ui.viewinterop.ViewFactoryHolder.<init>(AndroidView.android.kt:322)
    at androidx.compose.ui.viewinterop.ViewFactoryHolder.<init>(AndroidView.android.kt:341)
    at androidx.compose.ui.viewinterop.AndroidView_androidKt$createAndroidViewNodeFactory$1.invoke(AndroidView.android.kt:274)
    at androidx.compose.ui.viewinterop.AndroidView_androidKt$createAndroidViewNodeFactory$1.invoke(AndroidView.android.kt:273)
    at androidx.compose.ui.viewinterop.AndroidView_androidKt$AndroidView$$inlined$ComposeNode$1.invoke(Composables.kt:254)
    at androidx.compose.runtime.changelist.Operation$InsertNodeFixup.execute(Operation.kt:476)
    at androidx.compose.runtime.changelist.Operations.executeAndFlushAllPendingOperations(Operations.kt:307)
    at androidx.compose.runtime.changelist.FixupList.executeAndFlushAllPendingFixups(FixupList.kt:50)
    at androidx.compose.runtime.changelist.Operation$InsertSlotsWithFixups.execute(Operation.kt:443)
    at androidx.compose.runtime.changelist.Operations.executeAndFlushAllPendingOperations(Operations.kt:307)
    at androidx.compose.runtime.changelist.ChangeList.executeAndFlushAllPendingChanges(ChangeList.kt:78)
    at androidx.compose.runtime.CompositionImpl.applyChangesInLocked(Composition.kt:976)
    at androidx.compose.runtime.CompositionImpl.applyChanges(Composition.kt:1005)
    at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:1099)
    at androidx.compose.runtime.ComposerImpl$CompositionContextImpl.composeInitial$runtime_release(Composer.kt:3599)
    at androidx.compose.runtime.CompositionImpl.composeInitial(Composition.kt:633)
    at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:619)
    at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcomposeInto(SubcomposeLayout.kt:500)
    at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose(SubcomposeLayout.kt:472)
    at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose(SubcomposeLayout.kt:463)
    at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose(SubcomposeLayout.kt:447)
    at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$Scope.subcompose(SubcomposeLayout.kt:872)
    at androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScopeImpl.measure-0kLqBqw(LazyLayoutMeasureScope.kt:125)
    at androidx.compose.foundation.pager.PagerMeasureKt.getAndMeasure-SGf7dI0(PagerMeasure.kt:522)
    at androidx.compose.foundation.pager.PagerMeasureKt.measurePager-_JDW0YA(PagerMeasure.kt:203)
    at androidx.compose.foundation.pager.PagerMeasurePolicyKt$rememberPagerMeasurePolicy$1$1.invoke-0kLqBqw(PagerMeasurePolicy.kt:159)
    at androidx.compose.foundation.pager.PagerMeasurePolicyKt$rememberPagerMeasurePolicy$1$1.invoke(PagerMeasurePolicy.kt:69)
    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:709)
    at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0(InnerNodeCoordinator.kt:126)
    at androidx.compose.foundation.AndroidOverscroll_androidKt$StretchOverscrollNonClippingLayer$2.invoke-3p2s80s(AndroidOverscroll.android.kt:584)
    at androidx.compose.foundation.AndroidOverscroll_androidKt$StretchOverscrollNonClippingLayer$2.invoke(AndroidOverscroll.android.kt:583)
    at androidx.compose.ui.layout.LayoutModifierImpl.measure-3p2s80s(LayoutModifier.kt:294)
    at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:116)
    at androidx.compose.foundation.AndroidOverscroll_androidKt$StretchOverscrollNonClippingLayer$1.invoke-3p2s80s(AndroidOverscroll.android.kt:568)
    at androidx.compose.foundation.AndroidOverscroll_androidKt$StretchOverscrollNonClippingLayer$1.invoke(AndroidOverscroll.android.kt:567)
    at androidx.compose.ui.layout.LayoutModifierImpl.measure-3p2s80s(LayoutModifier.kt:294)
    at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:116)
    at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier.measure-3p2s80s(GraphicsLayerModifier.kt:646)
    at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:116)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:252)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:251)
    at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2303)
    at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:500)
    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:113)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1617)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:620)
    at androidx.compose.ui.node.LayoutNode.remeasure-_Sx5XlM$ui_release(LayoutNode.kt:1145)
    at androidx.compose.ui.node.MeasureAndLayoutDelegate.doRemeasure-sdFAvZA(MeasureAndLayoutDelegate.kt:354)
    at androidx.compose.ui.node.MeasureAndLayoutDelegate.measureAndLayout-0kLqBqw(MeasureAndLayoutDelegate.kt:439)
    at androidx.compose.ui.platform.AndroidComposeView.measureAndLayout-0kLqBqw(AndroidComposeView.android.kt:982)
    at androidx.compose.ui.node.LayoutNode.forceRemeasure(LayoutNode.kt:1219)
    at androidx.compose.foundation.pager.PagerState.performScroll(PagerState.kt:222)
    at androidx.compose.foundation.pager.PagerState.access$performScroll(PagerState.kt:131)
    at androidx.compose.foundation.pager.PagerState$scrollableState$1.invoke(PagerState.kt:184)
    at androidx.compose.foundation.pager.PagerState$scrollableState$1.invoke(PagerState.kt:184)
    at androidx.compose.foundation.gestures.DefaultScrollableState$scrollScope$1.scrollBy(ScrollableState.kt:166)
    at androidx.compose.foundation.gestures.ScrollingLogic$dispatchScroll$performScroll$1.invoke-MK-Hz9U(Scrollable.kt:762)
    at androidx.compose.foundation.gestures.ScrollingLogic$dispatchScroll$performScroll$1.invoke(Scrollable.kt:752)
    at androidx.compose.foundation.AndroidEdgeEffectOverscrollEffect.applyToScroll-Rhakbz0(AndroidOverscroll.android.kt:184)
    at androidx.compose.foundation.gestures.ScrollingLogic.dispatchScroll-3eAAhYA(Scrollable.kt:778)
    at androidx.compose.foundation.gestures.ScrollDraggableState.dragBy(Scrollable.kt:886)
    at androidx.compose.foundation.gestures.DraggableNode$abstractDragScope$1.dragBy-k-4lQ0M(Draggable.kt:315)
    at androidx.compose.foundation.gestures.DraggableNode.draggingBy(Draggable.kt:327)
    at androidx.compose.foundation.gestures.AbstractDraggableNode$startListeningForEvents$1$1.invoke(Unknown Source:8)
    at androidx.compose.foundation.gestures.AbstractDraggableNode$startListeningForEvents$1$1.invoke(Unknown Source:4)
    at androidx.compose.foundation.gestures.DraggableNode$drag$2.invokeSuspend(Draggable.kt:322)
    at androidx.compose.foundation.gestures.DraggableNode$drag$2.invoke(Unknown Source:8)
    at androidx.compose.foundation.gestures.DraggableNode$drag$2.invoke(Unknown Source:4)
    at androidx.compose.foundation.gestures.ScrollDraggableState$drag$2.invokeSuspend(Scrollable.kt:894)
    at androidx.compose.foundation.gestures.ScrollDraggableState$drag$2.invoke(Unknown Source:8)
    at androidx.compose.foundation.gestures.ScrollDraggableState$drag$2.invoke(Unknown Source:4)
    at androidx.compose.foundation.gestures.DefaultScrollableState$scroll$2$1.invokeSuspend(ScrollableState.kt:181)
    at androidx.compose.foundation.gestures.DefaultScrollableState$scroll$2$1.invoke(Unknown Source:8)
    at androidx.compose.foundation.gestures.DefaultScrollableState$scroll$2$1.invoke(Unknown Source:4)
    at androidx.compose.foundation.MutatorMutex$mutateWith$2.invokeSuspend(MutatorMutex.kt:173)
    at androidx.compose.foundation.MutatorMutex$mutateWith$2.invoke(Unknown Source:8)
    at androidx.compose.foundation.MutatorMutex$mutateWith$2.invoke(Unknown Source:4)
    at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:61)
    at kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScope.kt:261)
    at androidx.compose.foundation.MutatorMutex.mutateWith(MutatorMutex.kt:166)
    at androidx.compose.foundation.gestures.DefaultScrollableState$scroll$2.invokeSuspend(ScrollableState.kt:178)
    at androidx.compose.foundation.gestures.DefaultScrollableState$scroll$2.invoke(Unknown Source:8)
    at androidx.compose.foundation.gestures.DefaultScrollableState$scroll$2.invoke(Unknown Source:4)
    at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:61)
    at kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScope.kt:261)
    at androidx.compose.foundation.gestures.DefaultScrollableState.scroll(ScrollableState.kt:177)
    at androidx.compose.foundation.pager.PagerState.scroll$suspendImpl(PagerState.kt:575)
    at androidx.compose.foundation.pager.PagerState.scroll(Unknown Source:0)
    at androidx.compose.foundation.gestures.ScrollDraggableState.drag(Scrollable.kt:892)
    at androidx.compose.foundation.gestures.DraggableNode.drag(Draggable.kt:320)
    at androidx.compose.foundation.gestures.AbstractDraggableNode$startListeningForEvents$1.invokeSuspend(Draggable.kt:435)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
    at androidx.compose.ui.platform.AndroidUiDispatcher.performTrampolineDispatch(AndroidUiDispatcher.android.kt:81)
    at androidx.compose.ui.platform.AndroidUiDispatcher.access$performTrampolineDispatch(AndroidUiDispatcher.android.kt:41)
    at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:68)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1404)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1415)
    at android.view.Choreographer.doCallbacks(Choreographer.java:1015)
    at android.view.Choreographer.doFrame(Choreographer.java:941)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1389)
    at android.os.Handler.handleCallback(Handler.java:959)
    at android.os.Handler.dispatchMessage(Handler.java:100)
    at android.os.Looper.loopOnce(Looper.java:232)
    at android.os.Looper.loop(Looper.java:317)
    at android.app.ActivityThread.main(ActivityThread.java:8705)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:886)

解决方案

问题核心

  • 复用了同一个FragmentContainerView实例在HorizontalPager的多个页面中,滑动时Compose尝试将已有父布局的View重复添加,触发异常。
  • Fragment事务commit()是异步执行的,连续调用会导致状态不一致;手动调用onDestroyView()不符合Fragment生命周期规范。

修复步骤

  1. 为每个Pager页面创建独立的FragmentContainerView,避免同一个View被多次添加到不同父布局。
  2. 使用commitNow()替代commit(),确保Fragment事务立即执行,保证状态同步。
  3. 让FragmentManager管理生命周期,不要手动调用onDestroyView()。

修改后的代码

@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun ViewPager(
    data: List<Data>,
    fragmentManager: FragmentManager,
    currentItem: Int
) {
    val pagerState = rememberPagerState(
        initialPage = currentItem,
        pageCount = { data.size }
    )

    HorizontalPager(state = pagerState, userScrollEnabled = true) { page ->
        AndroidView(
            modifier = Modifier.fillMaxSize(),
            factory = { context ->
                // 为每个页面创建独立的容器视图
                val containerView
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 23:53:08