Как возобновить фрагмент из 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();
эти фрагменты работают правильно. Каждый фрагмент прикрепляется к действию и сохраняется в заднем стеке без каких-либо проблем.
поэтому, когда я запускаю A
,C
, и тогда B
стек выглядит так:
| |
|B|
|C|
|A|
___
и когда я нажимаю кнопку "Назад", B
разрушено и C
возобновлено.
но, когда я запускаю фрагмент A
во второй раз, вместо возобновления из заднего стека, он добавляется в верхней части заднего стека
| |
|A|
|C|
|A|
___
но я хочу возобновить A
и уничтожить все фрагменты на нем (если таковые имеются). На самом деле, мне просто нравится поведение заднего стека по умолчанию.
как я добиться этого?
ожидается: (A
должны быть возобновлены и верхние фрагменты должны быть уничтожены)
| |
| |
| |
|A|
___
Edit: (предлагается 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()
С настройка или я что-то пропустила?
4 ответа:
чтение документация, есть способ открыть задний стек на основе имени транзакции или идентификатора, предоставленного commit. Используя имя мая быть проще, так как он не должен требовать отслеживания числа, которое может измениться и усиливает логику "уникальная запись заднего стека".
так как вы хотите только одну запись обратно стека в
Fragment
, сделайте имя заднего состояния именем класса фрагмента (черезgetClass().getName()
). Потом при заменеFragment
используйтеpopBackStackImmediate()
метод. Если он возвращает true, это означает, что в заднем стеке есть экземпляр фрагмента. Если нет, то фактически выполните логику замены фрагмента.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(); } }
EDIT
проблема - когда я запускаю A, а затем B, а затем нажмите кнопку Назад, B удаляется и восстанавливается. и снова нажать кнопку Назад следует выйдите из приложения. Но он показывает пустое окно и нужен другой нажимать закрыть его.
это так
FragmentTransaction
добавляется в задний стек, чтобы убедиться, что мы можем поп фрагменты на вершине позже. Быстрое исправление для этого переопределяетonBackPressed()
и завершение работы, если задний стек содержит только 1Fragment
@Override public void onBackPressed(){ if (getSupportFragmentManager().getBackStackEntryCount() == 1){ finish(); } else { super.onBackPressed(); } }
что касается повторяющихся записей заднего стека, ваш условный оператор, который заменяет фрагмент, если он не был выскочил, явно разные чем то, что мой оригинал фрагмент кода. То, что вы делаете-это добавление к задней стеке, независимо от того, является ли стек был очищен.
что-то вроде этого должно быть ближе к тому, что вы хотите:
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(); } }
условие было немного изменено, так как выбор одного и того же фрагмента, пока он был виден, также вызвал дублирование записей.
реализация:
Я настоятельно рекомендую не принимать обновленную
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);
EDIT #2
чтобы обновить ящик при изменении заднего стека, создайте метод, который принимает во фрагменте и сравнивает имена классов. Если что-то совпадает, измените заголовок и выбор. Также добавьте
OnBackStackChangedListener
и пусть он вызовет ваш метод обновления, если есть допустимый фрагмент.например, в деятельности
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
.
Я думаю, что этот метод мой решить вашу проблему:
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 (); } }
который был первоначально размещен в Этот Вопрос
Шаг 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 } }
Шаг 2: при вызове другого фрагмента добавьте этот метод:
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();