Это правильный способ очистки стека фрагментов при выходе из глубоко вложенного стека?


130

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

В панели выбора слева у меня есть 5 выбираемых элементов - A B C D E.

Каждый загружает фрагмент (переходное отверстие FragmentTransaction:replace) в панели сведений -a b c d e

Теперь я расширил фрагмент, eчтобы он содержал кнопку, которая загружает другой фрагмент e1также в области сведений. Я сделал это eдля метода onClick фрагмента следующим образом:

FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
ft.replace(R.id.details_frag, newFrag);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();

Если я сделаю следующие выборы:

E - e - e1 - D - E

Затем фрагмент eнаходится в панели деталей. Это нормально, и я хочу. Однако, если я нажму backкнопку в этот момент, ничего не произойдет. Мне нужно дважды щелкнуть по нему, потому что e1он все еще находится в стеке. Кроме того, после щелчка я получил исключение нулевого указателя в onCreateView:

Чтобы «решить» эту проблему, я добавил следующее, когда A B C D Eвыбрано:

FragmentManager fm = getActivity().getSupportFragmentManager();
for(int i = 0; i < fm.getBackStackEntryCount(); ++i) {
    fm.popBackStack();
}

Просто интересно, правильное ли это решение или мне нужно сделать что-то другое?

Ответы:


252

Что ж, есть несколько способов сделать это в зависимости от предполагаемого поведения, но эта ссылка должна дать вам все лучшие решения, и неудивительно, что она принадлежит Дайанн Хакборн.

http://groups.google.com/group/android-developers/browse_thread/thread/d2a5c203dad6ec42

По сути, у вас есть следующие варианты

  • Используйте имя для исходного состояния заднего стека и используйте FragmentManager.popBackStack(String name, FragmentManager.POP_BACK_STACK_INCLUSIVE).
  • Используйте FragmentManager.getBackStackEntryCount()/ getBackStackEntryAt().getId() для получения идентификатора первой записи в заднем стеке и FragmentManager.popBackStack(int id, FragmentManager.POP_BACK_STACK_INCLUSIVE).
  • FragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) должен вытолкнуть весь задний стек ... Я думаю, что документация для этого неверна. (На самом деле, я думаю, это просто не касается случая, когда вы проходите POP_BACK_STACK_INCLUSIVE),

Что это значит? FragmentManager.getBackStackEntryCount () / getBackStackEntryAt (). GetId ()
— даннироа

4
2-й работал у меня. Что это означает для очистки всего стека: getSupportFragmentManager (). PopBackStack (getSupportFragmentManager (). GetBackStackEntryAt (0) .getId (), FragmentManager.POP_BACK_STACK_INCLUSIVE);
— NickL

@JorgeGarcia, возможно ли, что после popbackstack мы просто закончим наш фрагмент без перезапуска старого.
— duggu

Обратите внимание, что существует также версия popBackStackImmediate (), поскольку popBackStack () является асинхронным, что означает, что очистка не происходит в тот момент, когда вы вызываете метод.
— Genc

6
Утверждает, что группа запрещена как спам, и я не могу получить доступ к ссылке :( У кого-нибудь есть ресурс где-нибудь еще?
— EpicPandaForce

61

Другое чистое решение, если вы не хотите выталкивать все записи стека ...

getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
getSupportFragmentManager().beginTransaction().replace(R.id.home_activity_container, fragmentInstance).addToBackStack(null).commit();

Это сначала очистит стек, а затем загрузит новый фрагмент, поэтому в любой момент у вас будет только один фрагмент в стеке.


1
Это очень плохо: getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);перед тем, как заменить () , так как загружаются призвание Fragment onCreateView(), onActivityCreated()и т.д. Восстановление и Destroing фрагмент мгновенно плохо. Например, фрагмент может зарегистрировать получателя. Это влияет на производительность.
— VasileM

@VasileM, не могли бы вы подробно описать? В каких случаях это плохо? Это из-за асинхронного поведения FragmentTransaction? Есть только плохая производительность неправильного стека фрагментов?
— CoolMind

27

Благодаря ответу Иоахима я использую код, чтобы окончательно очистить все записи в обратном стеке.

// In your FragmentActivity use getSupprotFragmentManager() to get the FragmentManager.

// Clear all back stack.
int backStackCount = getSupportFragmentManager().getBackStackEntryCount();
for (int i = 0; i < backStackCount; i++) {

    // Get the back stack fragment id.
    int backStackId = getSupportFragmentManager().getBackStackEntryAt(i).getId();

    getSupportFragmentManager().popBackStack(backStackId, 
        FragmentManager.POP_BACK_STACK_INCLUSIVE);

} /* end of for */

18
Почему здесь используется цикл? Для меня FragmentManager.popBackStack (null, FragmentManager.POP_BACK_STACK_INCLUSIVE) очистил все записи обратного стека ...
— Марек

3
@Marek, потому что иногда не все фрагменты нужно удалять, для этого подойдет цикл.
— CoolMind

1
@CoolMind, для меня это все еще не имеет смысла. popBackStack(backStackId, INCLUSIVE)вернет все фрагменты (состояния) обратно в backStackId, поэтому, как только вы выберете самый низкий, iкоторый вы собираетесь вытолкнуть , все более высокие должны появиться одновременно. Так в чем смысл цикла?
— LarsH

@LarsH, ты прав. См. Stackoverflow.com/a/59158254/2914140 . Чтобы вставить другие фрагменты в требуемый тег, мы должны сначала добавить фрагмент с помощью addToBackStack ( tag ).
— CoolMind

7

Я много исследовал, как очистить Backstack, и, наконец, увидел Transaction BackStack и его управление . Вот решение, которое лучше всего сработало для меня.

 // CLEAR BACK STACK.
    private void clearBackStack() {
        final FragmentManager fragmentManager = getSupportFragmentManager();
        while (fragmentManager.getBackStackEntryCount() != 0) {
            fragmentManager.popBackStackImmediate();
        }
    }

Вышеупомянутый метод перебирает все транзакции в стеке и сразу удаляет их по одной.

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

Обновите метод ниже, чтобы удалить все фрагменты этого «имени» из стека.

FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.popBackStack("name",FragmentManager.POP_BACK_STACK_INCLUSIVE);
  • name Если не равно нулю, это имя предыдущего обратного состояния, которое нужно искать; если он найден, все состояния до этого состояния будут выскакивать.
  • Флаг POP_BACK_STACK_INCLUSIVE может использоваться, чтобы контролировать, выталкивается ли само названное состояние. Если значение null, отображается только верхнее состояние.

Я не понимаю, зачем вы удаляете их по одному. Есть ли причина, по которой вы выбрали это решение вместо других? Есть ли причина удалять их по одному, а не все сразу?
— miva2

нет способа удалить backstack за один раз, см. developer.android.com/reference/android/app/…
— Локеш

3

Я использую код, аналогичный тем, которые используют цикл while, но я вызываю счетчик записей в каждом цикле ... поэтому я полагаю, что он несколько медленнее

FragmentManager manager = getFragmentManager();
while (manager.getBackStackEntryCount() > 0){
        manager.popBackStackImmediate();
    }

0

Как написано в разделе Как извлечь фрагмент из бэкстэка и здесь Ларшем , мы можем вставить несколько фрагментов сверху вниз в конкретный тег ( вместе с тегированным фрагментом ), используя этот метод:

fragmentManager?.popBackStack ("frag", FragmentManager.POP_BACK_STACK_INCLUSIVE);

Замените "фрагмент" тегом вашего фрагмента. Помните, что сначала мы должны добавить фрагмент в стопку с помощью:

fragmentTransaction.addToBackStack("frag")

Если мы добавим фрагменты с помощью addToBackStack(null), мы не получим фрагменты таким образом.


-4
    // pop back stack all the way
    final FragmentManager fm = getSherlockActivity().getSupportFragmentManager();
    int entryCount = fm.getBackStackEntryCount(); 
    while (entryCount-- > 0) {
        fm.popBackStack();
    }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.