处理Android碎片交易问题的正确/最佳方式

时间:2014-09-12 19:37:53

标签: java android android-fragments fragment

在我的开源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();

ViewMapFragment' s onDestroyView()

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()之间的界面有何不同?用户是否看到了一些毛病的过渡?

1 个答案:

答案 0 :(得分:4)

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

那么,会发生什么:

  1. 当我们从MapFragment转到ViewMapFragment时,FragmentManager会记住删除MapFragment并添加ViewMapFragment操作。
  2. 然后我们使用导航抽屉转到任何其他片段,导致ViewMapFragment删除操作,然后添加从抽屉中选择的片段。
  3. 最后,当我们按下Back按钮时,调用popBackStackImmediate并且FragmentManager执行反向操作:ViewMapFragment被删除(实际上它已被删除)并添加了MapFragment。问题出现了 - 所选片段仍然存在,所以我们在屏幕上同时有两个片段。
  4. 有几种方法可以处理这种情况:

    1. 只需将导航抽屉操作添加到后台堆栈。
    2. 每次导航抽屉启动片段切换时清除堆栈。
    3. 要清除堆栈,您可以使用此代码(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)之前做一些事情。
      4. 实施例。例如,我们添加了一个标签为“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,那么我们可以假设片段在后面的堆栈中。