在AndroidView中使用compose使用fragment时崩溃

问题描述 投票:0回答:4

我正在尝试撰写并尝试在撰写 AndroidView 中包含一个片段。

所以在我的情况下,我们有一个带有 ComposeView 的 AFragment,并且在 ComposeView 内部有一个 AndroidView,它创建一个 FragmentContainerView 并添加一个 PIFragment。

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
    ): View {
        return ComposeView(requireContext()).apply {
            setContent {
                AppTheme {
                    GalleryScreen(
                        factory = viewModelFactory,
                        remoteConfig = remoteConfig,
                        id = id,
                        currentPosition = currentPositionState,
                        onBack = { router.back(requireActivity()) },
                    ) {
                        AndroidView(
                            modifier = Modifier
                                .fillMaxWidth()
                                .wrapContentHeight(),
                            factory = {
                                FragmentContainerView(context).apply {
                                    id = R.id.pFIC
                                }
                            },
                            update = {
                                childFragmentManager.beginTransaction().replace(
                                    R.id.pFIC,
                                    PIFragment::class.java,
                                    buildArguments(
                                        id = id,
                                        origin = origin,
                                    ), null
                                ).commitAllowingStateLoss()
                            },
                        )
                    }
                }
            }
        }
    }

一切工作正常,但是当我们在生产中发布此代码时,我们在 firebase 中看到崩溃:

    java.lang.IllegalArgumentException: No view found for id 0x7f0b072b (...:id/pFIC) for fragment PIFragment{ef1f89b} (bdbe15f0-679d-41bb-8a27-367655f73545 id=0x7f0b072b)
        at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:513)
        at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282)
        at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:112)
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1647)
        at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3128)
        at androidx.fragment.app.FragmentManager.dispatchViewCreated(FragmentManager.java:3065)
        at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:2988)
        at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:546)
        at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282)
        at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2180)
        at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2100)
        at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:2002)
        at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:524)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
android android-fragments android-jetpack-compose
4个回答
8
投票

事实上,崩溃是在重新创建父片段之后发生的。

就我而言,因为导航:

  • AFragment(与 PIFragment)
  • 导航至 BFragment
  • 返回AFragment

崩溃,因为片段管理器正在尝试重新创建 AFragment 和 PIFragment,但 compose pass 尚未完成,因此 pFIC 尚不存在。

解决方案是在父片段视图被销毁时删除 PIFragment。

    override fun onDestroyView() {
        childFragmentManager.findFragmentById(R.id.pFIC)?.let { fragment ->
            childFragmentManager.beginTransaction().remove(fragment).commitAllowingStateLoss()
        }
        super.onDestroyView()
    }

4
投票

您还应该使用

AndroidViewBinding
而不是
AndroidView
在 Compose 中添加片段。
AndroidViewBinding
具有特定于片段的处理,可确保在处置包含的可组合项时删除片段。请注意,这需要启用视图绑定。

在 XML 中定义 FragmentContainerView,如下所示:

<androidx.fragment.app.FragmentContainerView
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/fragment_container_view"
  android:layout_height="match_parent"
  android:layout_width="match_parent"
  android:name="com.example.MyFragment" />

...然后在您的可组合函数中:

@Composable
fun FragmentInComposeExample() {
    AndroidViewBinding(MyFragmentLayoutBinding::inflate) {
        val myFragment = fragmentContainerView.getFragment<MyFragment>()
        // ...
    }
}

您可以阅读Compose 中的片段了解更多信息。


1
投票

我也遇到过这个异常。我将 sagix 的答案修改为对我来说更方便的方式。看看下面。不要忘记使用 childFragmentManager

@Composable
fun ReusableFragmentComponent(
    screen: SupportAppScreen,
    fragmentManager: FragmentManager,
    modifier: Modifier = Modifier
) {
    val containerId by rememberSaveable { mutableStateOf(View.generateViewId()) }

    AndroidView(
        modifier = modifier,
        factory = { context ->
            FragmentContainerView(ContextThemeWrapper(context, context.currentStyle())).apply {
                id = containerId
            }
        },
        update = {
            val fragmentAlreadyAdded = fragmentManager.findFragmentByTag(screen.screenKey) != null

            if (!fragmentAlreadyAdded) {
                fragmentManager
                    .beginTransaction()
                    .replace(containerId, screen.getFragment(), screen.screenKey)
                    .commit()
            }
        })

    DisposableEffect(LocalLifecycleOwner.current) {
        onDispose {
            fragmentManager.findFragmentById(containerId)?.let { fragment ->
                fragmentManager
                    .beginTransaction()
                    .remove(fragment)
                    .commitAllowingStateLoss()
            }
        }
    }
}

0
投票

从版本 1.8.1 开始,有一个

AndroidFragment
可组合项类似于
AndroidView
可组合项,可以执行此类操作。

https://developer.android.com/jetpack/androidx/releases/fragment#1.8.0

示例:

AndroidFragment(clazz = PIFragment::class.java)

就像上面一样简单,并且您还有其他参数可以传递您需要的任何捆绑参数,或者如果您需要在通过 FragmentManager 更新时执行某些操作。

这里还有几个例子:

https://github.com/search?q=AndroidFragment++language%3AKotlin&type=code

© www.soinside.com 2019 - 2024. All rights reserved.