如果存在,如何从BackStack恢复片段

我正在学习如何使用片段。 我有三个Fragment初始化在顶部的实例。 我将这个片段添加到这样一个活动中:

声明和初始化:

 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(); 

这些片段正常工作。 每个片段都附加到活动中,并保存到后端堆栈,没有任何问题。

所以当我启动ACB ,堆栈如下所示:

 | | |B| |C| |A| ___ 

而当我按下“后退”button, B被销毁, C被恢复。

但是,当我第二次启动片段A时,不是从后端堆栈恢复,而是在后端堆栈的顶部添加

 | | |A| |C| |A| ___ 

但是我想恢复A并且销毁所有的碎片(如果有的话)。 其实,我只是喜欢默认的后退堆栈行为。

我如何做到这一点?

预计:( A应该恢复,顶部碎片应该被销毁)

 | | | | | | |A| ___ 

编辑:(由A – Cbuild议)

这是我的尝试代码:

 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被恢复。 并再次按“返回”应该退出应用程序。 但它显示出一个空白的窗口,我不得不第三次压回来closures它。

另外,当我启动A ,然后B ,然后C ,然后再B

预期:

 | | | | |B| |A| ___ 

实际:

 | | |B| |B| |A| ___ 

我应该重写onBackPressed()与任何定制或我错过了什么?

阅读文档 ,有一种方法可以根据事务名称或提交提供的ID来popup后退堆栈。 使用这个名字可能会更容易一些,因为它不需要跟踪一个可能会改变的数字,并加强了“独特的后退式条目”逻辑。

由于每个Fragment只需要一个返回堆栈条目,因此请使用Fragment的类名(通过getClass().getName() )作为返回状态的名称。 然后,在replaceFragment ,使用popBackStackImmediate()方法。 如果返回true,则意味着在后端堆栈中存在Fragment的实例。 如果不是,则实际执行碎片replace逻辑。

 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(); } } 

编辑

问题是 – 当我启动A然后B时,然后按下返回button,B被删除,A被恢复。 再次按下退出button应该退出应用程序。 但它显示一个空白的窗口,需要另一个新闻closures它。

这是因为FragmentTransaction被添加到后端堆栈,以确保我们可以在后面popup片段。 对此的一个快速解决scheme是覆盖onBackPressed()并完成活动,如果后退堆栈只包含1 Fragment

 @Override public void onBackPressed(){ if (getSupportFragmentManager().getBackStackEntryCount() == 1){ finish(); } else { super.onBackPressed(); } } 

关于重复的堆栈条目,如果条件语句没有被popup,那么replace它的条件语句显然不同于我原来的代码片段。 你正在做的是添加到后面的堆栈,而不pipe后面的堆栈是否被popup。

像这样的东西应该更接近你想要的东西:

 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(); } } 

条件被改变了一点,因为select相同的片段,而它是可见的也造成重复的条目。

执行:

我强烈build议不要像在代码中那样使用更新后的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

为了在后退堆栈更改时更新抽屉,请创build一个在片段中接受并比较类名称的方法。 如果有任何匹配,改变标题和select。 还要添加一个OnBackStackChangedListener并让它调用你的更新方法,如果有一个有效的片段。

例如,在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 } } 

现在,每当后退堆栈改变时,标题和检查位置将反映可见的Fragment

第1步:实现与您的活动类的接口

 public class AuthenticatedMainActivity extends Activity implements FragmentManager.OnBackStackChangedListener{ @Override protected void onCreate(Bundle savedInstanceState) { ............. FragmentManager fragmentManager = getFragmentManager(); fragmentManager.beginTransaction().add(R.id.frame_container,fragment, "First").addToBackStack(null).commit(); } private void switchFragment(Fragment fragment){ FragmentManager fragmentManager = getFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.frame_container, fragment).addToBackStack("Tag").commit(); } @Override public void onBackStackChanged() { FragmentManager fragmentManager = getFragmentManager(); System.out.println("@Class: SummaryUser : onBackStackChanged " + fragmentManager.getBackStackEntryCount()); int count = fragmentManager.getBackStackEntryCount(); // when a fragment come from another the status will be zero if(count == 0){ System.out.println("again loading user data"); // reload the page if user saved the profile data if(!objPublicDelegate.checkNetworkStatus()){ objPublicDelegate.showAlertDialog("Warning" , "Please check your internet connection"); }else { objLoadingDialog.show("Refreshing data..."); mNetworkMaster.runUserSummaryAsync(); } // IMPORTANT: remove the current fragment from stack to avoid new instance fragmentManager.removeOnBackStackChangedListener(this); }// end if } } 

第二步:当你调用另一个片段时,添加这个方法:

 String backStateName = this.getClass().getName(); FragmentManager fragmentManager = getFragmentManager(); fragmentManager.addOnBackStackChangedListener(this); Fragment fragmentGraph = new GraphFragment(); Bundle bundle = new Bundle(); bundle.putString("graphTag", view.getTag().toString()); fragmentGraph.setArguments(bundle); fragmentManager.beginTransaction() .replace(R.id.content_frame, fragmentGraph) .addToBackStack(backStateName) .commit(); 

我想这个方法我解决你的问题:

 public static void attachFragment ( int fragmentHolderLayoutId, Fragment fragment, Context context, String tag ) { FragmentManager manager = ( (AppCompatActivity) context ).getSupportFragmentManager (); manager.findFragmentByTag ( tag ); FragmentTransaction ft = manager.beginTransaction (); if (manager.findFragmentByTag ( tag ) == null) { // No fragment in backStack with same tag.. ft.add ( fragmentHolderLayoutId, fragment, tag ); ft.addToBackStack ( tag ); ft.commit (); } else { ft.show ( manager.findFragmentByTag ( tag ) ).commit (); } } 

这是最初发布在这个问题

 getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() { @Override public void onBackStackChanged() { if(getFragmentManager().getBackStackEntryCount()==0) { onResume(); } } });