我正在学习如何使用 fragment 。我有三个在类顶部初始化的 Fragment
实例。我将 fragment 添加到这样的 Activity 中:
声明和初始化:
Fragment A = new AFragment();
Fragment B = new BFragment();
Fragment C = new CFragment();
替换/添加:
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, A);
ft.addToBackStack(null);
ft.commit();
这些代码段运行正常。每个 fragment 都附加到 Activity 中,并毫无问题地保存到后台堆栈。
所以当我启动 A
、C
,然后是 B
时,堆栈如下所示:
| |
|B|
|C|
|A|
___
当我按下“返回”按钮时,B
被销毁,C
被恢复。
但是,当我第二次启动 fragment A
时,它不是从后台堆栈恢复,而是添加到后台堆栈的顶部
| |
|A|
|C|
|A|
___
但我想恢复 A
并销毁它上面的所有 fragment (如果有的话)。实际上,我只是喜欢默认的回栈行为。
我该如何做到这一点?
预期:(A
应恢复,顶部 fragment 应销毁)
| |
| |
| |
|A|
___
编辑:(由 A--C 建议)
这是我尝试的代码:
private void selectItem(int position) {
Fragment problemSearch = null, problemStatistics = null;
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
String backStateName = null;
Fragment fragmentName = null;
boolean fragmentPopped = false;
switch (position) {
case 0:
fragmentName = profile;
break;
case 1:
fragmentName = submissionStatistics;
break;
case 2:
fragmentName = solvedProblemLevel;
break;
case 3:
fragmentName = latestSubmissions;
break;
case 4:
fragmentName = CPExercise;
break;
case 5:
Bundle bundle = new Bundle();
bundle.putInt("problem_no", problemNo);
problemSearch = new ProblemWebView();
problemSearch.setArguments(bundle);
fragmentName = problemSearch;
break;
case 6:
fragmentName = rankList;
break;
case 7:
fragmentName = liveSubmissions;
break;
case 8:
Bundle bundles = new Bundle();
bundles.putInt("problem_no", problemNo);
problemStatistics = new ProblemStatistics();
problemStatistics.setArguments(bundles);
fragmentName = problemStatistics;
default:
break;
}
backStateName = fragmentName.getClass().getName();
fragmentPopped = manager.popBackStackImmediate(backStateName, 0);
if (!fragmentPopped) {
ft.replace(R.id.content_frame, fragmentName);
}
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(backStateName);
ft.commit();
// I am using drawer layout
mDrawerList.setItemChecked(position, true);
setTitle(title[position]);
mDrawerLayout.closeDrawer(mDrawerList);
}
问题是,当我启动 A
然后 B
,然后按“返回”,B
被删除,A
恢复。并再次按“返回”应退出应用程序。但它显示一个空白窗口,我必须第三次按下才能关闭它。
另外,当我启动 A
,然后是 B
,然后是 C
,然后是 B
...
预期:
| |
| |
|B|
|A|
___
实际:
| |
|B|
|B|
|A|
___
我应该用任何自定义覆盖 onBackPressed()
还是我遗漏了什么?
最佳答案
阅读documentation ,有一种方法可以根据事务名称或提交提供的 id 弹出回栈。使用名称可能会更容易,因为它不需要跟踪可能更改的数字并强化“唯一的返回堆栈条目”逻辑。
由于您只希望每个 Fragment
有一个返回堆栈条目,因此将返回状态名称设为 Fragment 的类名(通过 getClass().getName()
)。然后在替换 Fragment
时,使用 popBackStackImmediate()
方法。如果它返回 true,则意味着在后堆栈中有一个 Fragment 的实例。如果没有,实际执行Fragment替换逻辑。
private void replaceFragment (Fragment fragment){
String backStateName = fragment.getClass().getName();
FragmentManager manager = getSupportFragmentManager();
boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);
if (!fragmentPopped){ //fragment not in back stack, create it.
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content_frame, fragment);
ft.addToBackStack(backStateName);
ft.commit();
}
}
编辑
The problem is - when i launch A and then B, then press back button, B is removed and A is resumed. and pressing again back button should exit the app. But it is showing a blank window and need another press to close it.
这是因为 FragmentTransaction
被添加到后栈中,以确保我们以后可以将 fragment 弹出到顶部。如果返回堆栈仅包含 1 个 Fragment
onBackPressed()
并完成 Activity
@Override
public void onBackPressed(){
if (getSupportFragmentManager().getBackStackEntryCount() == 1){
finish();
}
else {
super.onBackPressed();
}
}
关于重复的返回堆栈条目,如果 fragment 尚未弹出,则替换 fragment 的条件语句与我的原始代码 fragment 明显不同。您正在做的是添加到后台堆栈,无论后台堆栈是否被弹出。
这样的东西应该更接近你想要的:
private void replaceFragment (Fragment fragment){
String backStateName = fragment.getClass().getName();
String fragmentTag = backStateName;
FragmentManager manager = getSupportFragmentManager();
boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);
if (!fragmentPopped && manager.findFragmentByTag(fragmentTag) == null){ //fragment not in back stack, create it.
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content_frame, fragment, fragmentTag);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(backStateName);
ft.commit();
}
}
条件有所改变,因为在可见时选择相同的 fragment 也会导致重复条目。
实现:
我强烈建议不要像在代码中那样将更新后的 replaceFragment()
方法分开。所有逻辑都包含在此方法中,移动部件可能会导致问题。
这意味着您应该将更新后的 replaceFragment()
方法复制到您的类中,然后更改
backStateName = fragmentName.getClass().getName();
fragmentPopped = manager.popBackStackImmediate(backStateName, 0);
if (!fragmentPopped) {
ft.replace(R.id.content_frame, fragmentName);
}
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(backStateName);
ft.commit();
原来如此
replaceFragment (fragmentName);
编辑#2
要在返回堆栈更改时更新抽屉,请创建一个接受 Fragment 并比较类名的方法。如果有任何匹配,请更改标题和选择。还添加一个 OnBackStackChangedListener
并让它调用你的更新方法,如果有一个有效的 fragment 。
例如在Activity的onCreate()
中,添加
getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
Fragment f = getSupportFragmentManager().findFragmentById(R.id.content_frame);
if (f != null){
updateTitleAndDrawer (f);
}
}
});
还有另一种方法:
private void updateTitleAndDrawer (Fragment fragment){
String fragClassName = fragment.getClass().getName();
if (fragClassName.equals(A.class.getName())){
setTitle ("A");
//set selected item position, etc
}
else if (fragClassName.equals(B.class.getName())){
setTitle ("B");
//set selected item position, etc
}
else if (fragClassName.equals(C.class.getName())){
setTitle ("C");
//set selected item position, etc
}
}
现在,每当返回堆栈发生变化时,标题和检查位置都会反射(reflect)可见的Fragment
。
关于android - 如何从 BackStack 恢复现有的 Fragment,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18305945/