Как возобновить fragment из BackStack, если он существует

Я изучаю, как использовать fragmentы. У меня есть три экземпляра Fragment , которые инициализируются в верхней части classа. Я добавляю 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(); 

Эти fragmentы работают правильно. Каждый fragment привязан к действию и сохраняется без проблем.

Поэтому, когда я запускаю 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() с любой настройкой, или я чего-то не хватает?

Читая документацию , есть способ поместить задний стек на основе либо имени транзакции, либо идентификатора, предоставленного фиксацией. Использование имени может быть проще, поскольку оно не должно требовать отслеживания числа, которое может измениться и подкрепляет логику «уникальной обратной записи стека».

Поскольку для Fragment getClass().getName() только одна обратная запись стека, сделайте имя состояния задней части имени classа 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(); } } 

РЕДАКТИРОВАТЬ

Проблема заключается в том, что когда я запускаю A, а затем B, затем нажимаю кнопку «Назад», B удаляется, и A возобновляется. и нажатие кнопки возврата назад должно выйти из приложения. Но он показывает пустое окно и нуждается в другом нажатии, чтобы закрыть его.

Это связано с тем, что FragmentTransaction добавляется в задний стек, чтобы гарантировать, что мы можем поместить fragmentы сверху позже. Быстрое исправление для этого является переопределением onBackPressed() и завершением Activity, если задний стек содержит только 1 Fragment

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

Что касается повторяющихся записей в стеке, то ваш условный оператор, который заменяет 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() в свой class, а затем изменить

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

EDIT # 2

Чтобы обновить ящик при изменении заднего стека, сделайте метод, который принимает во Фрагменте и сравнивает имена classов. Если что-то соответствует, измените заголовок и выбор. Также добавьте OnBackStackChangedListener и вызовите метод обновления, если есть действительный fragment.

Например, в Activity onCreate() , add

 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. Внедрите интерфейс с classом активности.

 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 } } 

Шаг 2. Когда вы вызываете другой fragment, добавьте этот метод:

 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(); } } }); 
  • Выполнение запроса HTTPS с использованием Android Volley
  • Как загрузить видеофайл на SD-карту?
  • Отладка Android InetAddress.isReachable
  • Экспорт и запуск проекта Unity3D в Android Studio
  • Удалить тень под панелью действий
  • Android Retrofit - onProgressUpdate для показа уведомления о ходе работы
  • Как определить, подписан ли пользователь Firebase с использованием аутентификации facebook
  • Как добавить нижний колонтитул в NavigationView - библиотеку дизайна поддержки Android?
  • DialogFragment.getDialog возвращает null
  • Как использовать MapView в android с помощью google map V2?
  • android java.lang.IllegalStateException: Не удалось прочитать строку 0, col 0 из CursorWindow
  • Давайте будем гением компьютера.