17

我有一个Activity应用程序,其中多个应用程序Fragments正在使用导航组件进行切换。当我在两个片段之间切换时,它们的onCreate()onDestroy()方法似乎重叠。因此,当片段访问相同的全局对象时,我很难为片段编写初始化和清理代码。

Framgent_A从到导航Fragment_B具有以下方法顺序:

Fragment_B.onCreate()
Fragment_A.onDestroy()

Fragment_A.onDestroy()我反转我在Fragment_A.onCreate(). 在我希望事情被调用Fragment_B时处于中立状态。onCreate()然而,情况并非如此,因为Fragment_A.onDestroy()尚未调用。

Android上的重叠是正常的还是我在导航组件中配置了错误?有没有另一种方法可以实现我想要做的事情?我知道我可以将两者结合Fragments起来并使其工作,但我不希望任何一个 Fragment 彼此了解。对我来说,在创建时Framgnet_A仍然活着,应该替换时似乎很奇怪。Fragment_BFragment_BFragment_A

任何帮助是极大的赞赏!


编辑:

在调试时浏览源代码后,我发现在FragmentNavigator.navigate() FragmentTransaction.setReorderingAllowed()被调用,它允许重新排序操作,甚至允许在前onCreate()一个片段之前调用新片段onDestroy()。问题仍然存在,在下一个 Fragment 中初始化相同的全局状态之前,如何解决我在一个 Fragment 中正确清理全局状态的问题。

4

4 回答 4

8

Android 生命周期Fragment并不是真正适合您需求的回调主机。导航控制器将用动画替换这两个片段,因此两者都以某种方式同时可见,最终即使onPause()是退出的片段也会onResume()在进入的片段之后被调用。

解决方案 1:使用OnDestinationChangedListener

onDestinationChanged()任何生命周期事件之前调用回调。作为一种非常简化的方法(注意泄漏),您可以执行以下操作:

    findNavController().addOnDestinationChangedListener { _, destination, _ ->
        if(shouldCleanupFor(destination)) cleanup()
    }

解决方案 2:将全局更改抽象掉

与其让单个导航点更改全局状态,不如为其提供一个真实点。这可能是独立于导航层次结构的另一个片段。然后像以前一样观察导航:

findNavController(R.id.nav_graph).addOnDestinationChangedListener { _, destination, _ ->
    resetAll()
    when(distination.id) {
        R.id.fragment_a -> prepareForA()
        R.id.fragment_b -> prepareForB()
        else -> prepareDefault()
    }
}

作为一个额外的优势,您也可以幂等地实现状态更改。

于 2019-01-17T14:33:49.673 回答
2

由于您有一个控制片段膨胀的活动,因此您可以手动控制正在膨胀的片段的生命周期。通过调用以下方法,您可以控制哪个片段准备好使用全局数据。此时您将不得不,一些如何将数据传回 Mainactivity 以确定哪个片段是活动的,因为您询问如何同时膨胀 2 个片段,这将共享一个对象。更好的方法是让 MainActivity 使用特定的类实现 FragmentA 和 FragmentB-detail 来执行 Stuff,这样您必须像平板电脑一样对待您的应用程序并确定 2 窗格模式以及哪一点您可以从您控制的那些片段中使用适当的类活动。包含的链接与您尝试完成的内容相匹配

private void addCenterFragments(Fragment fragment) {
        try {
            removeActiveCenterFragments();
            fragmentTransaction = fragmentManager.beginTransaction();
            fragmentTransaction.add(R.id.content_fragment, fragment);
            fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
            activeCenterFragments.add(fragment);
            fragmentTransaction.commit();
        }catch (Exception e){
            Crashlytics.logException(e);
        }
    }

    private void removeActiveCenterFragments() {
        if (activeCenterFragments.size() > 0) {
            fragmentTransaction = fragmentManager.beginTransaction();
            for (Fragment activeFragment : activeCenterFragments) {
                fragmentTransaction.remove(activeFragment);
            }
            activeCenterFragments.clear();
            fragmentTransaction.commit();
        }
    }
于 2019-01-14T20:20:12.193 回答
2

也许您可以将一些与初始化相关的代码移动到该片段onStart()onCreateView()方法中,假设您处于中性状态。根据开发人员文档,这是应该进行初始化的地方。

另一个可用的选项是使用Observer / Observable模式,您可以onDestroy()在 Fragment A 完成后通知您的 Activity。然后 Activity 将通知 Fragment B 可以安全地假定已清理状态并开始初始化。

于 2019-01-17T16:07:17.747 回答
0

我的情况有点不同,我想分享一下,以防有人遇到同样的问题。

我想对onPause()当前片段执行一项操作,但在从一个片段导航到另一个片段时不执行该代码。我要做的是调用isRemoving()方法来检查当前片段是否被删除。调用方法时将其设置为 true NavController.navigate(...)

override fun onPause() {
    super.onPause()
    if (!isRemoving()) {
        // Write your code here
    }
}

根据谷歌的Fragment.isRemoving()文档:

如果当前正在从其活动中删除此片段,则返回 true。这 不是它的活动是否完成,而是它是否正在从其活动中移除。

于 2020-03-22T18:49:44.513 回答