处理Android碎片事务问题的正确/最佳方式



在我的开源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上有什么区别?用户是否看到一些故障转换?

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

那么,发生了什么:

  1. 当我们从MapFragment转到ViewMapFragments时,FragmentManager会记得删除MapFraction并添加ViewMapFraction操作
  2. 然后,我们使用Navigation Drawer转到任何其他片段,这会导致ViewMapFragment删除操作,然后添加从Drawer中选择的片段
  3. 最后,当我们按下Back按钮时,popBackStackImmediate被调用,FragmentManager执行相反的操作:ViewMapFragment被删除(实际上它已经被删除了),MapFragments被添加。问题出现在这里——选定的片段仍然存在,所以我们在屏幕上同时有两个片段

有几种选择可以处理这种情况:

  1. 只需将导航抽屉操作添加到后堆栈即可
  2. 每当导航抽屉启动碎片切换时,清除后堆栈

要清除后堆栈,您可以使用以下代码(相关问题):

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

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

还有你的问题:

  1. 您可以使用FragmentManager.findFragmentByTag方法来检查片段是否在后堆栈中。请参阅我答案末尾的示例
  2. 从技术上讲,后栈的一个条目可以包含多个片段,我认为没有办法从中删除单个片段。你可以清除后栈,而不是删除(请参阅这个问题)
  3. 主要的区别是popBackStack会在它真正开始弹出过程之前给您一个做某事的机会(当应用程序返回到它的事件循环时,它就会启动)

示例。例如,我们添加了一个标签为"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,那么我们可以假设这个片段在后堆栈中。

最新更新