У меня огромная проблема с тем, как работает backstack фрагментов Android, и я буду очень благодарен за любую предложенную помощь.
Представьте, что у вас есть 3 фрагмента
[1] [2] [3]
Я хочу, чтобы пользователь мог перемещаться, [1] > [2] > [3]
но на обратном пути (нажатие кнопки возврата) [3] > [1]
.
Как я и предполагал, это можно сделать, если не вызывать addToBackStack(..)
при создании транзакции, которая переносит фрагмент [2]
в держатель фрагмента, определенный в XML.
На самом деле, кажется, что если я не хочу [2]
появляться снова, когда пользователь нажимает кнопку «Назад» [3]
, я не должен вызывать addToBackStack
транзакцию, которая показывает фрагмент [3]
. Это кажется совершенно нелогичным (возможно, из мира iOS).
В любом случае, если я сделаю это таким образом, когда я выйду [1] > [2]
и вернусь назад, я вернусь, [1]
как и ожидалось.
Если я пойду [1] > [2] > [3]
и нажму «назад», я вернусь к [1]
(как и ожидалось). Теперь странное поведение происходит, когда я пытаюсь [2]
снова перейти из [1]
. Прежде всего [3]
кратко отображается, прежде чем [2]
попадет в поле зрения. Если я вернусь в этот момент [3]
, отображается, и если я снова вернусь, приложение закроется.
Может ли кто-нибудь помочь мне понять, что здесь происходит?
А вот XML-файл макета для моей основной деятельности:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<fragment
android:id="@+id/headerFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
class="com.fragment_test.FragmentControls" >
<!-- Preview: layout=@layout/details -->
</fragment>
<FrameLayout
android:id="@+id/detailFragment"
android:layout_width="match_parent"
android:layout_height="fill_parent"
/>
Обновление. Это код, который я использую для сборки по иерархии навигации.
Fragment frag;
FragmentTransaction transaction;
//Create The first fragment [1], add it to the view, BUT Dont add the transaction to the backstack
frag = new Fragment1();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.commit();
//Create the second [2] fragment, add it to the view and add the transaction that replaces the first fragment to the backstack
frag = new Fragment2();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.addToBackStack(null);
transaction.commit();
//Create third fragment, Dont add this transaction to the backstack, because we dont want to go back to [2]
frag = new Fragment3();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.commit();
//END OF SETUP CODE-------------------------
//NOW:
//Press back once and then issue the following code:
frag = new Fragment2();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.addToBackStack(null);
transaction.commit();
//Now press back again and you end up at fragment [3] not [1]
Большое спасибо
источник
Ответы:
Пояснение: что здесь происходит?
Если иметь в виду, что
.replace()
это равно тому,.remove().add()
что мы знаем из документации:тогда что происходит примерно так (я добавляю числа к фрагменту, чтобы было понятнее):
(здесь начинают происходить все вводящие в заблуждение)
Помните, что
.addToBackStack()
сохраняется только транзакция, а не сам фрагмент ! Итак, теперь у нас естьfrag3
макет:Возможное решение
Подумайте о реализации,
FragmentManager.BackStackChangedListener
чтобы отслеживать изменения в заднем стеке и применять свою логику вonBackStackChanged()
методе:FragmentTransaction.addToBackStack(String name);
источник
FragmentManager.BackStackChangedListener
чтобы следить за изменениями в заднем стеке. Следите за всеми своими транзакциями с помощьюonBackStackChanged()
метода и действуйте по мере необходимости: например. отслеживать количество транзакций в BackStack; проверить конкретную транзакцию по имени (FragmentTransaction addToBackStack (String name)
) и т. д.Правильно!!! после долгого выдергивания волос я наконец-то понял, как заставить это работать должным образом.
Кажется, что фрагмент [3] не удаляется из представления при нажатии back, так что вам придется делать это вручную!
Прежде всего, не используйте replace (), а вместо этого используйте отдельно удалить и добавить. Кажется, что replace () не работает должным образом.
Следующая часть этого - переопределение метода onKeyDown и удаление текущего фрагмента при каждом нажатии кнопки возврата.
Надеюсь это поможет!
источник
Прежде всего, спасибо @Arvis за объяснение, открывающее глаза.
Я предпочитаю другое решение принятого здесь ответа для этой проблемы. Мне не нравится возиться с переопределением обратного поведения больше, чем это абсолютно необходимо, и когда я пытался добавлять и удалять фрагменты самостоятельно, без всплытия заднего стека по умолчанию при нажатии кнопки возврата, я обнаружил себя в аду фрагментов :) Если вы. добавьте f2 вместо f1 при его удалении. f1 не будет вызывать какие-либо методы обратного вызова, такие как onResume, onStart и т. д., и это может быть очень неудачным.
Во всяком случае, вот как я это делаю:
В настоящее время на виду только фрагмент f1.
f1 -> f2
здесь нет ничего необычного. Затем во фрагменте f2 этот код переносит вас на фрагмент f3.
f2 -> f3
Я не уверен, читая документы, должно ли это работать, этот метод всплывающей транзакции считается асинхронным, и, возможно, лучшим способом было бы вызвать popBackStackImmediate (). Но насколько я могу судить на своих устройствах, он работает безупречно.
Указанная альтернатива будет:
Здесь на самом деле будет краткое возвращение к f1, прежде чем переходить к f3, так что небольшой сбой.
На самом деле это все, что вам нужно сделать, нет необходимости переопределять поведение обратного стека ...
источник
Я знаю, что это старый вопрос, но у меня та же проблема, и я исправляю ее следующим образом:
Сначала добавьте фрагмент 1 в BackStack с именем (например, «Frag1»):
А затем, когда вы хотите вернуться к Fragment1 (даже после добавления 10 фрагментов над ним), просто вызовите popBackStackImmediate с именем:
Надеюсь, это кому-то поможет :)
источник
После ответа @Arvis я решил копнуть еще глубже и написал об этом техническую статью: http://www.andreabaccega.com/blog/2015/08/16/how-to-avoid-fragments-overlapping- из - за к backstack-кошмар-в-андроид /
Для ленивых разработчиков. Мое решение состоит в том, чтобы всегда добавлять транзакции в backstack и
FragmentManager.popBackStackImmediate()
при необходимости выполнять дополнительные операции (автоматически).Код представляет собой очень мало строк кода, и в моем примере я хотел перейти от C к A, не возвращаясь к «B», если пользователь не углубился в бэкстэк (например, из C переходит к D).
Следовательно, прикрепленный код будет работать следующим образом: A -> B -> C (назад) -> A & A -> B -> C -> D (назад) -> C (назад) -> B (назад) -> A
где
были выданы от «B» до «C», как в вопросе.
Хорошо, вот код :)
источник
Если вы боретесь с addToBackStack () и popBackStack (), просто используйте
В своей деятельности в OnBackPressed () найдите фрагмент по тегу, а затем выполните свои действия
Для получения дополнительной информации https://github.com/DattaHujare/NavigationDrawer Я никогда не использую addToBackStack () для обработки фрагментов.
источник
Когда я читаю ваш рассказ, я думаю, что [3] тоже находится на заднем плане. Это объясняет, почему вы видите его мигание.
Решением было бы никогда не помещать [3] в стек.
источник
s old method issue but now it
нормально. СпасибоУ меня была аналогичная проблема, когда у меня было 3 последовательных фрагмента в одном и том же
Activity
[M1.F0] -> [M1.F1] -> [M1.F2] с последующим вызовом новогоActivity
[M2]. Если пользователь нажимал кнопку в [M2], я хотел вернуться к [M1, F1] вместо [M1, F2], что уже делало поведение обратного нажатия.Для этого я удаляю [M1, F2], вызываю show на [M1, F1], фиксирую транзакцию, а затем добавляю [M1, F2] обратно, вызывая ее с помощью hide. Это устранило лишний пресс со спины, который иначе остался бы позади.
Привет, после выполнения этого кода: я не могу видеть значение Fragment2 при нажатии клавиши «Назад». Мой код:
источник
executePendingTransactions()
,commitNow()
не работал (Работал в androidx (джетпак).
источник