【问题标题】:Proper/Best Way to Handle Android Fragment Transaction Issue处理 Android 片段事务问题的正确/最佳方法
【发布时间】:2014-09-12 19:37:53
【问题描述】:

在我的开源 Android 应用中,发现了一个问题,即特定片段会出现在另一个片段之上或在特定情况下使应用崩溃。

如果您想查看更多信息和示例屏幕截图,请访问 GitHub 上的问题: https://github.com/rpi-mobile/RPIMobile-Android/issues/31

我已经确定了原因,但想知道使用 android.support.v4.app 包中的哪些方法来解决问题。

MainActivity.java中,是使用FragmentTransaction.replace()切换片段的导航抽屉的代码。

问题出现是因为在MapFragment,我使用:

ViewMapFragment vmf = new ViewMapFragment();
FragmentTransaction ft = getSherlockActivity().getSupportFragmentManager().beginTransaction();
ft.addToBackStack(null);
ft.replace(R.id.content_frame, vmf);
ft.commit();

ViewMapFragmentonDestroyView()

FragmentManager fm = getSherlockActivity().getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.remove(fm.findFragmentById(R.id.mapview));
ft.commit();

onDestroyView() 正确地从视图中删除了 ViewMapFragment,但如果您将它放在视图中并使用导航抽屉更改为不同的片段,MapFragment 仍然在后台堆栈中。

所以,对于我的问题:

1) 如何在尝试删除/替换之前检查特定片段是否在后堆栈上,或者如果您尝试在没有任何内容(即不检查)时应用程序不会崩溃?例如。当后台堆栈上没有任何内容时调用popBackStack()

2) 我应该使用FragmentManager 类方法来尝试从后台堆栈中删除MapFragment 还是应该使用FragmentTransaction 方法?利弊?

3) popBackStack()popBackStackImmediate() 在 UI 上的区别是什么?用户是否看到了一些有问题的过渡?

【问题讨论】:

    标签: java android android-fragments fragment


    【解决方案1】:

    根据FragmentTransaction 的文档,当您调用 addToBackStack 方法时,它只会记住您在该事务中执行的操作。当调用 popBackStack 方法时,它将反转这些操作并执行它们。

    那么,会发生什么:

    1. 当我们从 MapFragment 转到 ViewMapFragment 时,FragmentManager 会记住删除 MapFragment 并添加 ViewMapFragment 操作。
    2. 然后我们使用 Navigation Drawer 转到任何其他片段,这会导致 ViewMapFragment 删除操作,然后添加从 Drawer 中选择的片段。
    3. 最后,当我们按下返回按钮时,popBackStackImmediate 被调用,FragmentManager 执行相反的操作:ViewMapFragment 被移除(实际上它已经被移除)并添加 MapFragment。这就是问题所在 - 选定的片段仍然存在,因此我们同时在屏幕上显示了两个片段。

    有几种方法可以处理这种情况:

    1. 只需将导航抽屉操作添加到后台堆栈。
    2. 导航抽屉每次启动片段切换时清除回栈。

    要清除回栈,您可以使用此代码 (related question):

    getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    

    如果你把这一行放在你的 selectItem 方法的开头,那么这个错误就会被修复。

    还有你的问题:

    1. 您可以使用FragmentManager.findFragmentByTag 方法检查片段是否在回栈中。请参阅我的答案末尾的示例。
    2. 从技术上讲,一个后栈条目可以包含多个片段,我认为没有办法从中删除单个片段。您可以只清除返回堆栈,而不是删除(请参阅此question)。
    3. 主要区别在于 popBackStack 会在实际开始弹出进程之前让您有机会做某事 (it will be started when application returns to it's event loop)。

    示例。例如,我们添加了一个带有标签“fragment-1”的片段:

    getSupportFragmentManager()
            .beginTransaction()
            .replace(R.id.frame, new TestFragment(), "fragment-1")
            .commit();
    

    然后,我们将它放入一个后栈,并用另一个片段替换它:

    getSupportFragmentManager()
            .beginTransaction()
            .addToBackStack(null)
            .replace(R.id.frame, new TestFragment(), "another-fragment")
            .commit();
    

    此时 getSupportFragmentManager().findFragmentByTag("fragment-1") 返回我们的第一个片段(它从返回堆栈条目中获取)。现在我们可以通过isAdded方法检查这个片段是否被添加到它的活动中——如果它返回false,那么我们可以假设这个片段在一个后栈中。

    【讨论】:

    • 感谢您的回答!我将使用您的一条线解决方案。作为对您答案的反馈,您没有完全回答问题 1,除非您间接回答了问题 2,并且您对问题 2 的回答可能会稍微更具体地说明在链接答案中的查找位置,因为我必须梳理关于该问题的 cmets页面。
    • 不幸的是,对于这个问题没有一个好的解决方案(在 API 中有 clearBackstack 方法会很好,但没有这样的方法)。由于这个问题,我不得不在我的项目中实现自己的后台堆栈系统,并且在清除后台堆栈时出现过渡故障(我正在使用非常自定义的过渡动画)。 :(
    • 我添加了有关 1 和 2 个问题的更多信息。希望这会有所帮助。
    猜你喜欢
    • 2010-12-19
    • 2021-11-12
    • 1970-01-01
    • 2021-03-07
    • 1970-01-01
    • 1970-01-01
    • 2017-08-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多