在撰写 TabRow 中的活动重新启动/配置更改时显示空白片段

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

我正在尝试将

TabRow
FragmentContainerView
中显示的现有片段结合起来实现。当子片段添加到活动/主机片段时,它们可以正常工作。但是在活动重新启动时,片段似乎不可见,但存在于片段管理器中,但由于某种原因没有显示。由于我们现有的实现/架构,我们只能添加片段而不能替换它们。下面是它的示例代码。 Fragment A、B、C 中没有单独的设置。它们仅包含一个描述其名称的 textView。

此处提供错误场景 gif https://i.stack.imgur.com/mxKiq.gif

活动.kt

@Composable
fun MainScreen() {
        var selectedTabIndex by remember { mutableStateOf(0) }
        Column(Modifier.fillMaxSize()) {
            TabRow(
                selectedTabIndex = selectedTabIndex,
                backgroundColor = Color.Gray,
            ) {
                tabs.forEachIndexed { index, title ->
                    Tab(
                        text = {
                            Text(
                                text = title,
                                maxLines = 1,
                                overflow = TextOverflow.Ellipsis
                            )
                        },
                        selected = selectedTabIndex == index,
                        onClick = {
                            selectedTabIndex = index
                        },
                        selectedContentColor = Color.Black,
                        unselectedContentColor = Color.White
                    )
                }
            }
            FragmentContainer(
                modifier = Modifier.fillMaxSize(),
                commit = getCommitFunction(
                    index = selectedTabIndex
                )
            )
        }
    }

    private fun getFragment(index: Int): Fragment {
        return when (index) {
            0 -> FragmentA.newInstance()
            1 -> FragmentB.newInstance()
            else -> FragmentC.newInstance()
        }
    }

    private fun getCommitFunction(
        index: Int,
    ): FragmentTransaction.(containerId: Int) -> Unit =
        { containerId ->
            showPage(this, index, containerId)
        }

    private fun showPage(transaction: FragmentTransaction, position: Int, containerId: Int) {

        // hide current showing fragment
        tabs.forEach { fragmentTag ->
            val fragment: Fragment? = supportFragmentManager.findFragmentByTag(fragmentTag)
            if (fragment != null && fragment.isVisible) {
                transaction.hide(fragment)
            }
        }

        val fragmentTag = tabs[position]
        var fragment: Fragment? = supportFragmentManager.findFragmentByTag(fragmentTag)

        if (fragment == null) {
            // we don't have the fragment yet, need to create it
            fragment = getFragment(position)
            println("containerId $containerId")
            transaction.add(containerId, fragment, fragmentTag)
        } else {
            println("containerId else $containerId")
            // we already created the fragment, just need to show it
            transaction.show(fragment)
        }

        transaction.setPrimaryNavigationFragment(fragment)
    }

FragmentContainer.kt

@Composable
fun FragmentContainer(
    modifier: Modifier = Modifier,
    commit: FragmentTransaction.(containerId: Int) -> Unit
) {
    val localView = LocalView.current
    // Find the parent fragment, if one exists. This will let us ensure that
    // fragments inflated via a FragmentContainerView are properly nested
    // (which, in turn, allows the fragments to properly save/restore their state)
    val parentFragment = remember(localView) {
        try {
            localView.findFragment<Fragment>()
        } catch (e: IllegalStateException) {
            // findFragment throws if no parent fragment is found
            null
        }
    }
    val containerId by rememberSaveable { mutableStateOf(View.generateViewId()) }
    val container = remember { mutableStateOf<FragmentContainerView?>(null) }
    val viewBlock: (Context) -> View = remember(localView) {
        { context ->
            FragmentContainerView(context).apply { id = containerId }
        }
    }
    AndroidView(
        modifier = modifier,
        factory = viewBlock,
        update = {view ->
            val fragmentManager = parentFragment?.childFragmentManager
                ?: (view.context as? FragmentActivity)?.supportFragmentManager
            fragmentManager?.commit { commit(view.id) }
            container.value = view as FragmentContainerView
        }
    )

    // Set up a DisposableEffect that will clean up fragments when the FragmentContainer is disposed
    val localContext = LocalContext.current
    DisposableEffect(localView, localContext, container) {
        onDispose {
            val fragmentManager = parentFragment?.childFragmentManager
                ?: (localContext as? FragmentActivity)?.supportFragmentManager
            // Now find the fragment inflated via the FragmentContainerView
            val existingFragment = fragmentManager?.findFragmentById(container.value?.id ?: 0)
            if (existingFragment != null && !fragmentManager.isStateSaved) {
                // If the state isn't saved, that means that some state change
                // has removed this Composable from the hierarchy
                fragmentManager.commit(true) {
                    remove(existingFragment)
                }
            }
        }
    }
}
android android-jetpack-compose fragment android-jetpack-compose-material3 android-jetpack-compose-ui
1个回答
0
投票

状态没有恢复的原因是没有任何东西存储它的状态。每次应用程序被终止和恢复时,它都会重新创建。

        FragmentContainer(
            modifier = Modifier.fillMaxSize(),
            commit = getCommitFunction(
                index = selectedTabIndex
            )
        )

我们需要一些东西来保持这些片段的状态。一种方法是使用 NavHost 并通过 Navigator 管理导航。

我在https://github.com/elye/demo_android_jetpack_compose_fragment_navigation中创建了一个简单的示例。请参阅选项卡上的最后一个设计示例。希望这有帮助。

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