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


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.