在我的开源 Android 应用程序中,发现了一个问题,即在特定情况下特定 fragment 会出现在另一个 fragment 之上或使应用程序崩溃。
GitHub上的问题如果你想看到更多的信息和示例截图: https://github.com/rpi-mobile/RPIMobile-Android/issues/31
我已经确定了原因,但想知道使用 android.support.v4.app 包中的哪些方法来解决问题。
在MainActivity.java
中,是使用FragmentTransaction.replace()
切换fragments的抽屉导航代码。
出现这个问题是因为在 MapFragment
中,我使用:
ViewMapFragment vmf = new ViewMapFragment();
FragmentTransaction ft = getSherlockActivity().getSupportFragmentManager().beginTransaction();
ft.addToBackStack(null);
ft.replace(R.id.content_frame, vmf);
ft.commit();
在 ViewMapFragment
的 onDestroyView()
中:
FragmentManager fm = getSherlockActivity().getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.remove(fm.findFragmentById(R.id.mapview));
ft.commit();
onDestroyView()
正确地从 View 中删除了 ViewMapFragment
,但是如果您在 View 中有它并使用抽屉导航更改为不同的 fragment , MapFragment
仍在返回堆栈中。
所以,对于我的问题:
1) 在尝试删除/替换特定 fragment 之前,我如何检查它是否在返回堆栈中,或者如果您在什么都没有时尝试(即不检查),应用程序不会崩溃吗?例如。当返回堆栈上没有任何内容时调用 popBackStack()
。
2) 我应该使用 FragmentManager
类方法尝试从后台堆栈中删除 MapFragment
还是应该使用 FragmentTransaction
方法?优缺点?
3) popBackStack()
和 popBackStackImmediate()
在用户界面上有什么区别?用户是否看到一些故障转换?
最佳答案
根据 FragmentTransaction 的文档,当您调用 addToBackStack 方法时,它只会记住您在该事务中执行的操作。当调用 popBackStack 方法时,它将反转这些操作并执行它们。
那么,会发生什么:
- 当我们从 MapFragment 转到 ViewMapFragment 时,FragmentManager 会记得移除 MapFragment 并添加 ViewMapFragment 操作。
- 然后我们使用抽屉导航转到任何其他 fragment ,这会导致 ViewMapFragment 删除操作,然后添加从抽屉中选择的 fragment 。
- 最后,当我们按下后退按钮时,popBackStackImmediate 被调用并且 FragmentManager 执行相反的操作:ViewMapFragment 被移除(实际上它已经被移除)并添加 MapFragment。问题出现在这里 - 所选 fragment 仍然存在,因此我们同时在屏幕上显示了两个 fragment 。
有几种方法可以处理这种情况:
- 只需将 Navigation Drawer 操作添加到返回堆栈即可。
- 每次通过 Navigation Drawer 启动 fragment 切换时清除返回堆栈。
要清除返回堆栈,您可以使用此代码 (related question):
getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
如果将此行放在 selectItem 方法的开头,错误将得到修复。
还有你的问题:
- 您可以使用FragmentManager.findFragmentByTag 检查 fragment 是否在返回堆栈中。方法。请参阅我的回答末尾的示例。
- 从技术上讲,返回堆栈的一个条目可以包含多个 fragment ,我认为没有办法从中删除单个 fragment 。您可以清除返回堆栈而不是删除(请引用 question )。
- 主要区别在于,popBackStack 会在它实际开始弹出过程 (it will be started when application returns to it's event loop) 之前为您提供做某事的机会。
例子。例如,我们添加了一个带有标签“fragment-1”的 fragment :
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.frame, new TestFragment(), "fragment-1")
.commit();
然后,我们将它放入一个返回栈中,并用另一个 fragment 替换它:
getSupportFragmentManager()
.beginTransaction()
.addToBackStack(null)
.replace(R.id.frame, new TestFragment(), "another-fragment")
.commit();
此时 getSupportFragmentManager().findFragmentByTag("fragment-1") 返回我们的第一个 fragment (它从返回堆栈条目中获取)。现在我们可以通过 isAdded 检查这个 fragment 是否被添加到它的 Activity 中。方法 - 如果它返回 false,那么我们可以假设 fragment 在返回堆栈中。
关于java - 处理 Android fragment 事务问题的正确/最佳方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25815621/