Как сохранить состояние фрагмента при добавлении в задний стек?

160

Я написал пустышку, которая переключается между двумя фрагментами. Когда вы переходите от FragmentA к FragmentB, FragmentA добавляется в задний стек. Однако когда я возвращаюсь к FragmentA (нажав назад), создается совершенно новый FragmentA, и состояние, в котором он находился, теряется. У меня такое ощущение, что мне нужно то же, что и в этом вопросе, но я включил полный пример кода, чтобы помочь устранить проблему:

public class FooActivity extends Activity {
  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(android.R.id.content, new FragmentA());
    transaction.commit();
  }

  public void nextFragment() {
    final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(android.R.id.content, new FragmentB());
    transaction.addToBackStack(null);
    transaction.commit();
  }

  public static class FragmentA extends Fragment {
    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      final View main = inflater.inflate(R.layout.main, container, false);
      main.findViewById(R.id.next_fragment_button).setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
          ((FooActivity) getActivity()).nextFragment();
        }
      });
      return main;
    }

    @Override public void onSaveInstanceState(Bundle outState) {
      super.onSaveInstanceState(outState);
      // Save some state!
    }
  }

  public static class FragmentB extends Fragment {
    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      return inflater.inflate(R.layout.b, container, false);
    }
  }
}

Добавлены некоторые сообщения журнала:

07-05 14:28:59.722 D/OMG     ( 1260): FooActivity.onCreate
07-05 14:28:59.742 D/OMG     ( 1260): FragmentA.onCreateView
07-05 14:28:59.742 D/OMG     ( 1260): FooActivity.onResume
<Tap Button on FragmentA>
07-05 14:29:12.842 D/OMG     ( 1260): FooActivity.nextFragment
07-05 14:29:12.852 D/OMG     ( 1260): FragmentB.onCreateView
<Tap 'Back'>
07-05 14:29:16.792 D/OMG     ( 1260): FragmentA.onCreateView

Он никогда не вызывает FragmentA.onSaveInstanceState и создает новый FragmentA при ответном ударе. Однако, если я нахожусь на FragmentA и блокирую экран, FragmentA.onSaveInstanceState действительно вызывается. Так странно ... я ошибаюсь, ожидая, что фрагмент, добавленный в задний стек, не нуждается в воссоздании? Вот что говорят доктора :

Принимая во внимание, что если вы вызываете addToBackStack () при удалении фрагмента, то этот фрагмент останавливается и будет возобновлен, если пользователь перейдет обратно.

Эрик
источник
3
@ Ян-Хенк Как насчет вещей, которые нужно получить? Например, позиция прокрутки a ListView. Похоже, слишком много прыжков, чтобы присоединить слушателя прокрутки и обновить переменную экземпляра.
Джейк Уортон
2
@JakeWharton Я согласен, что это должно быть проще, но, насколько я знаю, нет никакого способа обойти это, потому что onCreateView вызывается, когда фрагмент восстанавливается из backstack. Но я могу ошибаться :)
Ян-Хенк
1
onCreate не вызывается. Таким образом, очевидно, он повторно использует тот же экземпляр, но снова вызывает onCreateView? Ламе. Я предполагаю, что я могу просто кэшировать результат onCreateView и просто вернуть существующее представление, если onCreateView вызывается снова.
Эрик
1
Именно то, что я искал часами. Можете ли вы опубликовать, как вы достигли этого, используя переменную экземпляра?
Ума
1
Поэтому я недавно начал свою собственную реализацию в github.com/frostymarvelous/Folio и столкнулся с проблемой. Я могу создать около 5 сложных страниц / фрагментов, прежде чем я получу сбои OOM. Вот что привело меня сюда. Сокрытие и показ просто не достаточно. Представления слишком тяжелы для памяти.
морозный

Ответы:

120

Если вы вернетесь к фрагменту из заднего стека, он не создаст фрагмент заново, а повторно использует тот же экземпляр и начнет с onCreateView()жизненного цикла фрагмента, см. Жизненный цикл фрагмента .

Поэтому, если вы хотите сохранить состояние, вы должны использовать переменные экземпляра, а не полагаться на них onSaveInstanceState().

Jan-Henk
источник
32
Текущая версия документации противоречит этому требованию. Блок-схема говорит, что вы заявляете, но текст в основной области страницы говорит, что onCreateView () вызывается только при первом отображении фрагмента: developer.android.com/guide/components/fragments.html Я борюсь с этим сейчас, и я не вижу никаких методов, вызываемых при возврате фрагмента из backstack. (Android 4.2)
Колин М.
10
Пытался записать свое поведение. OnCreateView () всегда вызывается при отображении фрагмента.
Принцепье
4
@ColinM. Любое решение проблемы?
метель
9
Это не работает для меня. Мои переменные экземпляра равны нулю при возвращении к фрагменту! Как я могу сохранить состояние?
Дон Румми
5
так что, если мы не должны ретранслировать при сохранении экземпляра, как сохранить состояние фрагмента и данные?
Махди
80

По сравнению с Apple, UINavigationControllerи UIViewController, Google не преуспевает в архитектуре программного обеспечения Android. И документ об Android Fragmentне сильно помогает.

Когда вы вводите FragmentB из FragmentA, существующий экземпляр FragmentA не уничтожается. Когда вы нажимаете Назад в FragmentB и возвращаетесь во FragmentA, мы не создаем новый экземпляр FragmentA. onCreateView()Будут вызваны существующие экземпляры FragmentA .

Ключевым моментом является то, что мы не должны снова раздувать представление в FragmentA onCreateView(), потому что мы используем существующий экземпляр FragmentA. Нам нужно сохранить и повторно использовать rootView.

Следующий код работает хорошо. Он не только сохраняет состояние фрагмента, но и снижает нагрузку на ОЗУ и ЦП (поскольку мы раздуваем компоновку только при необходимости). Я не могу поверить, что пример кода Google и документ никогда не упоминают об этом, но всегда раздувают макет .

Версия 1 (Не используйте версию 1. Используйте версию 2)

public class FragmentA extends Fragment {
    View _rootView;
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (_rootView == null) {
            // Inflate the layout for this fragment
            _rootView = inflater.inflate(R.layout.fragment_a, container, false);
            // Find and setup subviews
            _listView = (ListView)_rootView.findViewById(R.id.listView);
            ...
        } else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove _rootView from the existing parent view group
            // (it will be added back).
            ((ViewGroup)_rootView.getParent()).removeView(_rootView);
        }
        return _rootView;
    }
}

------ Обновление от 3 мая 2005 года: -------

Как уже упоминалось в комментариях, иногда _rootView.getParent()ноль вonCreateView , что приводит к сбою. Версия 2 удаляет _rootView в onDestroyView (), как предложено dell116. Проверено на Android 4.0.3, 4.4.4, 5.1.0.

Версия 2

public class FragmentA extends Fragment {
    View _rootView;
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (_rootView == null) {
            // Inflate the layout for this fragment
            _rootView = inflater.inflate(R.layout.fragment_a, container, false);
            // Find and setup subviews
            _listView = (ListView)_rootView.findViewById(R.id.listView);
            ...
        } else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove _rootView from the existing parent view group
            // in onDestroyView() (it will be added back).
        }
        return _rootView;
    }

    @Override
    public void onDestroyView() {
        if (_rootView.getParent() != null) {
            ((ViewGroup)_rootView.getParent()).removeView(_rootView);
        }
        super.onDestroyView();
    }
}

ПРЕДУПРЕЖДЕНИЕ!!!

Это взлом! Хотя я использую его в своем приложении, вам нужно тщательно проверить и прочитать комментарии.

Винс Юань
источник
38
Держать ссылку на корневой фрагмент всего фрагмента - плохая идея IMO. Если вы постоянно добавляете несколько фрагментов в backstack, и все они хранят его root-вид (который имеет довольно большой объем памяти), тогда существует высокая вероятность того, что вы получите OutOfMemoryError, так как все фрагменты содержат ссылку на rootview и GC не могут собирать его. Я думаю, что лучший подход заключается в том, чтобы постоянно раздувать представление (и позволить системе Android обрабатывать создание / уничтожение своего представления) и onActivityCreated / onViewCreated проверять, не равны ли ваши данные нулю. Если да, то загрузите его, иначе установите данные в представления.
Traninho
15
Не делай этого! Когда создается иерархия представлений фрагмента, она содержит внутреннюю ссылку на действие, которое в то время содержало фрагмент. Когда происходит изменение конфигурации, действие часто воссоздается. Повторное использование старого макета сохраняет эту активность зомби в памяти вместе с любыми объектами, на которые она также ссылается. Подобная потеря памяти снижает производительность и делает ваше приложение лучшим кандидатом на немедленное завершение работы, если не на переднем плане.
Крылез
4
@AllDayAmazing Это хороший момент. Если честно, я очень смущен прямо сейчас. Кто-нибудь может попытаться объяснить, почему удерживать ссылку на корневое представление фрагмента не хорошо, но удерживать ссылку только для любого потомка rootview (который в любом случае имеет ссылку на rootview) нормально?
Traninho
2
ОСТАВАЙТЕСЬ ОТ ЭТОГО, если только вы не хотите тратить 5 часов на то, чтобы выяснить причину вашего кода ..... только тогда, чтобы понять, что это причина. Теперь я должен провести рефакторинг множества вещей, потому что я использовал этот хак. Гораздо лучше использовать fragTransaction.add, если вы хотите сохранить пользовательский интерфейс фрагмента, когда вы видите другой (даже сверху). FragmentTransaction.replace () предназначено для уничтожения представлений фрагмента ..... не боритесь с системой.
dell116
2
@ VinceYuan - я протестировал последнюю версию библиотеки v7-appcompat на Android 5.1, и это оставило 6 экземпляров фрагмента, который должен был быть удален в FragmentManager моей деятельности. Даже если GC справится с этим правильно (а я не верю, что это произойдет), это вызывает ненужную нагрузку на память как для вашего приложения, так и для устройства в целом. Простое использование .add () полностью устраняет необходимость во всем этом хакерском коде. Это полностью противоречит тому, что в первую очередь предполагалось использовать FragmentTransaction.replace ().
dell116
53

Я думаю, что есть альтернативный способ достичь того, что вы ищете. Я не говорю, что это полное решение, но оно послужило цели в моем случае.

То, что я сделал, вместо замены фрагмента, я просто добавил целевой фрагмент. Так что в основном вы будете использовать add()метод вместо replace().

Что еще я сделал. Я скрываю свой текущий фрагмент, а также добавляю его в backstack.

Следовательно, он перекрывает новый фрагмент поверх текущего фрагмента, не разрушая его вид. (Проверьте, что его onDestroyView()метод не вызывается. Плюс добавление его в backstateдает мне преимущество возобновления фрагмента.

Вот код:

Fragment fragment=new DestinationFragment();
FragmentManager fragmentManager = getFragmentManager();
android.app.FragmentTransaction ft=fragmentManager.beginTransaction();
ft.add(R.id.content_frame, fragment);
ft.hide(SourceFragment.this);
ft.addToBackStack(SourceFragment.class.getName());
ft.commit();

AFAIK System вызывает только onCreateView()если представление уничтожено или не создано. Но здесь мы сохранили вид, не удаляя его из памяти. Так что это не создаст новый взгляд.

И когда вы вернетесь из Destination Fragment, вы увидите последний FragmentTransaction вытолкнет последний удаляемый верхний фрагмент, который заставит самый верхний вид (SourceFragment) появиться на экране.

КОММЕНТАРИЙ: Как я уже сказал, это не полное решение, так как оно не удаляет вид фрагмента Source и, следовательно, занимает больше памяти, чем обычно. Но все же, служить цели. Кроме того, мы используем совершенно другой механизм сокрытия вида вместо его замены, что не является традиционным.

Так что дело не в том, как вы поддерживаете состояние, а в том, как вы поддерживаете представление.

Каушал Триведи
источник
В моем случае, добавление фрагмента вместо замены вызывает проблемы при использовании опроса или любой другой вид веб-запроса используется во фрагменте. Я хочу приостановить этот опрос во фрагменте A, когда добавлен фрагмент B. Есть идеи по этому поводу?
Ума
Как вы используете опрос в FirstFragment? Вы должны сделать это вручную, так как оба фрагмента остаются в памяти. Таким образом, вы можете использовать их экземпляры для выполнения необходимых действий. Это подсказка, генерирующая событие в основной деятельности, которое что-то делает, когда вы добавляете второй фрагмент. Надеюсь, это поможет.
Каушал Триведи
1
Спасибо за подсказку =). Я сделал это Но это единственный способ сделать это? И подходящий способ? Также, когда я нажимаю кнопку «Домой» и снова запускаю приложение, все фрагменты снова становятся активными. Предположим, я здесь, во фрагменте B, таким образом. Activity A{Fragment A --> Fragment B}когда я снова запускаю приложение после нажатия кнопки «Домой», onResume()вызывается оба фрагмента и, следовательно, они начинают опрос. Как я могу это контролировать?
Ума
1
К сожалению, вы не можете, система не работает в нормальном режиме, таким образом, она будет рассматривать оба фрагмента как прямой потомок активности. Несмотря на то, что она служила цели поддержания состояния фрагмента, другими нормальными вещами становится очень трудно управлять. обнаружил все эти проблемы, теперь мое предложение не идти по этому пути. Извините.
Каушал Триведи
1
Конечно, в заключение я скажу не использовать этот подход, пока вы не найдете какое-либо другое решение. Потому что им трудно управлять.
Каушал Триведи
7

Я бы предложил очень простое решение.

Возьмите ссылочную переменную View и установите представление в OnCreateView. Проверьте, существует ли представление в этой переменной, затем верните то же представление.

   private View fragmentView;

   public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);

        if (fragmentView != null) {
            return fragmentView;
        }
        View view = inflater.inflate(R.layout.yourfragment, container, false);
        fragmentView = view;
        return view;
    }
Мандип Сингх
источник
1
Существует вероятность утечки памяти, если мы не удалили переменную фрагмента в onDestroy ()
Арун PM
@ArunPM так, как удалить фрагмент просмотра в onDestroy ()? if (_rootView.getParent() != null) { ((ViewGroup)_rootView.getParent()).removeView(_rootView); }уместно очистить память?
Мехмет Гюр
1
@ MehmetGür я использую это решение много раз. До сих пор я не получил ошибку утечки памяти. Но вы можете использовать решение ArunPM с этим, если хотите. Я думаю, что он говорит, чтобы установить для columnView значение null в методе OnDestroy ().
Мандип Сингх
1
Я использую LeakCanary для обнаружения утечек памяти и проблем с утечками, когда я следовал этому методу. Но , как @Mandeep вздыхать упоминается в комментариях , мы можем преодолеть эту проблему путем присвоения nullк fragmentView переменному в onDestroy()методе.
Арун PM
1
Насколько мне известно, при получении фрагмента уничтожается представление, связанное с фрагментом onDestroyView(). Эта очистка не происходит для нашей переменной представления резервной копии (здесь fragmentView ), и она вызовет утечку памяти, когда фрагмент обратно сложен / уничтожен. Вы можете найти ту же ссылку в [Общие причины утечек памяти] ( square.github.io/leakcanary/fundamentals/… ) во введении LeakCanery.
Арун PM
6

Я столкнулся с этой проблемой во фрагменте, содержащем карту, в которой слишком много подробностей настройки для сохранения / перезагрузки. Мое решение состояло в том, чтобы сохранить этот фрагмент активным все время (аналогично тому, что упоминал @kaushal).

Скажем, у вас есть текущий фрагмент A и вы хотите отобразить фрагмент B. Обобщая последствия:

  • replace () - удалить Фрагмент A и заменить его Фрагментом B. Фрагмент A будет воссоздан после того, как снова выведен на фронт
  • add () - (создать и) добавить фрагмент B, и он перекрывает фрагмент A, который все еще активен в фоновом режиме
  • remove () - может использоваться для удаления фрагмента B и возврата к A. Фрагмент B будет воссоздан при последующем вызове

Следовательно, если вы хотите сохранить оба фрагмента «сохраненными», просто переключите их, используя hide () / show ().

Плюсы : простой и простой способ поддерживать работу нескольких фрагментов.
Минусы : вы используете намного больше памяти, чтобы все они работали. Могут возникнуть проблемы, например, отображение большого количества растровых изображений.

Тео Инке
источник
Подскажите, пожалуйста, когда мы удалим фрагмент b и вернемся к A, какой метод вызывается во фрагменте A? Я хочу предпринять некоторые действия, когда мы удалим фрагмент B.
Google
5

onSaveInstanceState() вызывается только в случае изменения конфигурации.

Так как при переходе от одного фрагмента к другому не происходит никаких изменений конфигурации, поэтому нет вызова onSaveInstanceState() нет. В каком состоянии не сохраняются? Можете ли вы указать?

Если вы введете какой-либо текст в EditText, он будет сохранен автоматически. Любой элемент пользовательского интерфейса без идентификатора является элементом, состояние просмотра которого не должно быть сохранено.

user2779311
источник
onSaveInstanceState()также вызывается, когда система уничтожает активность, потому что ей не хватает ресурсов.
Марсель Бро
0

Здесь, так как onSaveInstanceStateво фрагмент не вызывает, когда вы добавляете фрагмент в backstack. Жизненный цикл фрагмента в backstack при восстановлении начинается onCreateViewи заканчивается, onDestroyViewпока onSaveInstanceStateвызывается между onDestroyViewи onDestroy. Мое решение - создать переменную экземпляра и инициализировать в onCreate. Образец кода:

private boolean isDataLoading = true;
private ArrayList<String> listData;
public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     isDataLoading = false;
     // init list at once when create fragment
     listData = new ArrayList();
}

И проверьте это в onActivityCreated:

public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    if(isDataLoading){
         fetchData();
    }else{
         //get saved instance variable listData()
    }
}

private void fetchData(){
     // do fetch data into listData
}
Лин Бу
источник
0
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener()
    {
        @Override
        public void onBackStackChanged()
        {
            if (getSupportFragmentManager().getBackStackEntryCount() == 0)
            {
                //setToolbarTitle("Main Activity");
            }
            else
            {
                Log.e("fragment_replace11111", "replace");
            }
        }
    });


YourActivity.java
@Override
public void onBackPressed()
{
 Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.Fragment_content);
  if (fragment instanceof YourFragmentName)
    {
        fragmentReplace(new HomeFragment(),"Home Fragment");
        txt_toolbar_title.setText("Your Fragment");
    }
  else{
     super.onBackPressed();
   }
 }


public void fragmentReplace(Fragment fragment, String fragment_name)
{
    try
    {
        fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.replace(R.id.Fragment_content, fragment, fragment_name);
        fragmentTransaction.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
        fragmentTransaction.addToBackStack(fragment_name);
        fragmentTransaction.commitAllowingStateLoss();
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}
Хардик Васани
источник
0

Моя проблема была похожей, но я преодолел ее, не сохранив фрагмент. Предположим, у вас есть действие, которое имеет 2 фрагмента - F1 и F2. F1 запускается изначально и, скажем, содержит некоторую информацию о пользователе, а затем при некотором условии появляется F2 с просьбой ввести дополнительный атрибут - номер телефона. Затем вы хотите, чтобы этот номер телефона вернулся к F1 и завершил регистрацию, но вы понимаете, что вся предыдущая информация пользователя потеряна, и у вас нет их предыдущих данных. Фрагмент воссоздается с нуля, и даже если вы сохранили эту информацию в onSaveInstanceStateкомплекте, он возвращается в ноль onActivityCreated.

Решение. Сохраните требуемую информацию в качестве переменной экземпляра при вызове. Затем передайте эту переменную экземпляра в ваш фрагмент.

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    Bundle args = getArguments();

    // this will be null the first time F1 is created. 
    // it will be populated once you replace fragment and provide bundle data
    if (args != null) {
        if (args.get("your_info") != null) {
            // do what you want with restored information
        }
    }
}

Итак, следуя моему примеру: перед отображением F2 я сохраняю пользовательские данные в переменной экземпляра с помощью обратного вызова. Затем я запускаю F2, пользователь вводит номер телефона и нажимает сохранить. Я использую другой обратный вызов в действии, собираю эту информацию и заменяю свой фрагмент F1, на этот раз он содержит данные пакета, которые я могу использовать.

@Override
public void onPhoneAdded(String phone) {
        //replace fragment
        F1 f1 = new F1 ();
        Bundle args = new Bundle();
        yourInfo.setPhone(phone);
        args.putSerializable("you_info", yourInfo);
        f1.setArguments(args);

        getFragmentManager().beginTransaction()
                .replace(R.id.fragmentContainer, f1).addToBackStack(null).commit();

    }
}

Дополнительную информацию о обратных вызовах можно найти здесь: https://developer.android.com/training/basics/fragments/communicating.html

Доди
источник
0

первый : просто используйте метод add вместо метода replace класса FragmentTransaction, затем необходимо добавить secondFragment в стек с помощью метода addToBackStack

второе : при обратном щелчке вы должны вызвать popBackStackImmediate ()

Fragment sourceFragment = new SourceFragment ();
final Fragment secondFragment = new SecondFragment();
final FragmentTransaction ft = getChildFragmentManager().beginTransaction();
ft.add(R.id.child_fragment_container, secondFragment );
ft.hide(sourceFragment );
ft.addToBackStack(NewsShow.class.getName());
ft.commit();
                                
((SecondFragment)secondFragment).backFragmentInstanceClick = new SecondFragment.backFragmentNewsResult()
{
        @Override
        public void backFragmentNewsResult()
        {                                    
            getChildFragmentManager().popBackStackImmediate();                                
        }
};
Милад Ахмади
источник
0

Замените фрагмент, используя следующий код:

Fragment fragment = new AddPaymentFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.frame, fragment, "Tag_AddPayment")
                .addToBackStack("Tag_AddPayment")
                .commit();

Активность onBackPressed ():

  @Override
public void onBackPressed() {
    android.support.v4.app.FragmentManager fm = getSupportFragmentManager();
    if (fm.getBackStackEntryCount() > 1) {

        fm.popBackStack();
    } else {


        finish();

    }
    Log.e("popping BACKSTRACK===> ",""+fm.getBackStackEntryCount());

}
Пратибха Сароде
источник
0
Public void replaceFragment(Fragment mFragment, int id, String tag, boolean addToStack) {
        FragmentTransaction mTransaction = getSupportFragmentManager().beginTransaction();
        mTransaction.replace(id, mFragment);
        hideKeyboard();
        if (addToStack) {
            mTransaction.addToBackStack(tag);
        }
        mTransaction.commitAllowingStateLoss();
    }
replaceFragment(new Splash_Fragment(), R.id.container, null, false);
Хитеш Манвани
источник
1
Спасибо за этот фрагмент кода, который может предоставить некоторую ограниченную, немедленную помощь. Надлежащее объяснение было бы значительно улучшить свою долгосрочную ценность , показывая , почему это хорошее решение проблемы, и сделает его более полезным для читателей будущих с другими подобными вопросами. Пожалуйста, измените свой ответ, чтобы добавить некоторые объяснения, в том числе предположения, которые вы сделали.
Machavity
0

Идеальное решение, которое находит старый фрагмент в стеке и загружает его, если существует в стеке.

/**
     * replace or add fragment to the container
     *
     * @param fragment pass android.support.v4.app.Fragment
     * @param bundle pass your extra bundle if any
     * @param popBackStack if true it will clear back stack
     * @param findInStack if true it will load old fragment if found
     */
    public void replaceFragment(Fragment fragment, @Nullable Bundle bundle, boolean popBackStack, boolean findInStack) {
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        String tag = fragment.getClass().getName();
        Fragment parentFragment;
        if (findInStack && fm.findFragmentByTag(tag) != null) {
            parentFragment = fm.findFragmentByTag(tag);
        } else {
            parentFragment = fragment;
        }
        // if user passes the @bundle in not null, then can be added to the fragment
        if (bundle != null)
            parentFragment.setArguments(bundle);
        else parentFragment.setArguments(null);
        // this is for the very first fragment not to be added into the back stack.
        if (popBackStack) {
            fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        } else {
            ft.addToBackStack(parentFragment.getClass().getName() + "");
        }
        ft.replace(R.id.contenedor_principal, parentFragment, tag);
        ft.commit();
        fm.executePendingTransactions();
    }

используйте это как

Fragment f = new YourFragment();
replaceFragment(f, null, boolean true, true); 
Khemraj
источник