- Multiple back stacks
- A deep dive into what actually went into this feature
- The joys of the system back button
- Multiple back stacks in Fragments
- Paying down our technical debts in Fragments
- PopBackStack, но сохраните первый фрагмент в android
- PopBackStack, но сохраните первый фрагмент в android
- Проблемы с Android Fragment back stack
- 8 ответов:
- пояснение: что здесь происходит?
- возможное решение
Multiple back stacks
A deep dive into what actually went into this feature
If a ‘back stack’ is a set of screens that you can navigate back through via the system back button, ‘multiple back stacks’ is just a bunch of those, right? Well, that’s exactly what we’ve done with the multiple back stack support added in Navigation 2.4.0-alpha01 and Fragment 1.4.0-alpha01!
The joys of the system back button
Whether you’re using Android’s new gesture navigation system or the traditional navigation bar, the ability for users to go ‘back’ is a key part to the user experience on Android and doing that right is an important part to making your app feel like a natural part of the ecosystem.
In the simplest cases, the system back button just finishes your activity. While in the past you might have been tempted to override the onBackPressed() method of your activity to customize this behavior, it is 2021 and that is totally unnecessary. Instead, there are APIs for custom back navigation in the OnBackPressedDispatcher . This is actually the same API that FragmentManager and NavController already plug into.
That means when you use either Fragments or Navigation, they use the OnBackPressedDispatcher to ensure that if you’re using their back stack APIs, the system back button works to reverse each of the screens that you’ve pushed onto the back stack.
Multiple back stacks doesn’t change these fundamentals. The system back button is still a one directional command — ‘go back’. This has a profound effect on how the multiple back stack APIs work.
Multiple back stacks in Fragments
At the surface level, the support for multiple back stacks is deceptively straightforward, but requires a bit of an explanation of what actually is the ‘fragment back stack’. The FragmentManager ’s back stack isn’t made up of fragments, but instead is made up of fragment transactions. Specifically, the ones that have used the addToBackStack(String name) API.
This means when you commit() a fragment transaction with addToBackStack() , the FragmentManager is going to execute the transaction by going through and executing each of the operations (the replace , etc.) that you specified on the transaction, thus moving each fragment through to its expected state. FragmentManager then holds onto that transaction as part of its back stack.
When you call popBackStack() (either directly or via FragmentManager ’s integration with the system back button), the topmost transaction on the fragment back stack is reversed — an added fragment is removed, a hidden fragment is shown, etc. This puts the FragmentManager back into the same state that it was before the fragment transaction was initially committed.
Note: I cannot stress this enough, but you absolutely should never interleave transactions with addToBackStack() and transactions without in the same FragmentManager : transactions on your back stack are blissfully unaware of non-back stack changing fragment transactions — swapping things out from underneath those transactions makes that reversal when you pop a much more dicey proposition.
This means that popBackStack() is a destructive operation: any added fragment will have its state destroyed when that transaction is popped. This means you lose your view state, any saved instance state, and any ViewModel instances you’ve attached to that fragment are cleared. This is the main difference between that API and the new saveBackStack() . saveBackStack() does the same reversal that popping the transaction does, but it ensures that the view state, saved instance state, and ViewModel instances are all saved from destruction. This is how the restoreBackStack() API can later recreate those transactions and their fragments from the saved state and effectively ‘redo’ everything that was saved. Magic!
This didn’t come without paying down a lot of technical debt though.
Paying down our technical debts in Fragments
While fragments have always saved the Fragment’s view state, the only time that a fragment’s onSaveInstanceState() would be called would be when the Activity’s onSaveInstanceState() was called. To ensure that the saved instance state is saved when calling saveBackStack() , we need to also inject a call to onSaveInstanceState() at the right point in the fragment lifecycle transitions. We can’t call it too soon (your fragment should never have its state saved while it is still STARTED ), but not too late (you want to save the state before the fragment is destroyed).
This requirement kicked off a process to fix how FragmentManager moves to state to make sure there’s one place that manages moving a fragment to its expected state and handles re-entrant behavior and all the state transitions that go into fragments.
35 changes and 6 months into that restructuring of fragments, it turned out that postponed fragments were seriously broken, leading to a world where postponed transactions were left floating in limbo — not actually committed and not actually not committed. Over 65 changes and another 5 months later, and we had completely rewritten most of the internals of how FragmentManager manages state, postponed transitions, and animations. That effort is covered in more detail in my previous blog post:
Источник
PopBackStack, но сохраните первый фрагмент в android
Я работаю над транзакцией фрагментов, а стоп-запись выглядит так:
Я хотел бы вернуться к fragA после того,
Итак, я попробовал код вроде:
Но он очищает все задние части, как я могу сохранить первый фрагмент в задней части экрана? большое спасибо
Например, вы можете сделать следующее:
- Добавьте fragA, не добавляя его в backStack. Так что всегда
И не будет реагировать на кнопку «Назад». - Когда вы открываете fragD, вы должны очистить фрагмент BackStack . Поэтому, когда вы нажимаете кнопку «Назад» от D frag, вы возвращаетесь к A.
PS Есть другие способы, как делать то, что вы хотите. Это зависит…
Поскольку «задний стек» имеет поведение, подобное стеку … последний, первый из … последний фрагмент, добавленный в задний стек, сначала будет выгружен из предыдущего стека. Вам необходимо будет выполнить поведение, которое вам требуется вручную, указав свой собственный. Это не так сложно, используя методы класса FragmentManager .
Если вы «пометите» свои фрагменты, добавив их в транзакцию …
Вы можете позже определить, какой фрагмент будет отображаться при нажатии кнопки «Назад» …
Как вы определяете, какой фрагмент показать, что он полностью зависит от вас. Вы можете отслеживать текущий видимый фрагмент, или вы можете использовать метод isHidden класса Fragment … Кстати, я говорю о нативных фрагментах здесь, а не о фрагменте библиотеки.
Столбец содержит записи о транзакциях, а не фрагменты. Поэтому вы не должны добавлять запись первой транзакции (null -> fragA) в стопку. И запись всех других транзакций должна быть добавлена в backstack. В этом случае вы заготовки popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); Android удалил все фрагменты, кроме fragA, потому что нет никаких записей о том, как добавлен fragA.
Всего несколько дней назад я начал изучать фрагменты в Android. И я столкнулся с этой проблемой. Здесь я показываю свое решение и как я это разрешаю. Пожалуйста, исправьте, если мой код неправильный. Что мы имеем в это время? Acivity, много фрагментов и их backstack. Мы хотим открыть каждый фрагмент из меню Ящика и очистить все остальные фрагменты из backstack. Но мы должны держать только один фрагмент дома. Когда мы остаемся на домашнем фрагменте, а пользователь нажимает кнопку «Назад», приложение закрывается.
И при нажатии на элемент меню ящика
Я сделал следующий путь. Очистите весь фрагмент, затем добавьте первый фрагмент дома
Источник
PopBackStack, но сохраните первый фрагмент в android
Я работаю над транзакцией фрагментов, а стоп-запись выглядит так:
Я хотел бы вернуться к fragA после того,
Итак, я попробовал код вроде:
Но он очищает все задние части, как я могу сохранить первый фрагмент в задней части экрана? большое спасибо
Например, вы можете сделать следующее:
- Добавьте fragA, не добавляя его в backStack. Так что всегда
И не будет реагировать на кнопку «Назад». - Когда вы открываете fragD, вы должны очистить фрагмент BackStack . Поэтому, когда вы нажимаете кнопку «Назад» от D frag, вы возвращаетесь к A.
PS Есть другие способы, как делать то, что вы хотите. Это зависит…
Поскольку «задний стек» имеет поведение, подобное стеку … последний, первый из … последний фрагмент, добавленный в задний стек, сначала будет выгружен из предыдущего стека. Вам необходимо будет выполнить поведение, которое вам требуется вручную, указав свой собственный. Это не так сложно, используя методы класса FragmentManager .
Если вы «пометите» свои фрагменты, добавив их в транзакцию …
Вы можете позже определить, какой фрагмент будет отображаться при нажатии кнопки «Назад» …
Как вы определяете, какой фрагмент показать, что он полностью зависит от вас. Вы можете отслеживать текущий видимый фрагмент, или вы можете использовать метод isHidden класса Fragment … Кстати, я говорю о нативных фрагментах здесь, а не о фрагменте библиотеки.
Столбец содержит записи о транзакциях, а не фрагменты. Поэтому вы не должны добавлять запись первой транзакции (null -> fragA) в стопку. И запись всех других транзакций должна быть добавлена в backstack. В этом случае вы заготовки popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); Android удалил все фрагменты, кроме fragA, потому что нет никаких записей о том, как добавлен fragA.
Всего несколько дней назад я начал изучать фрагменты в Android. И я столкнулся с этой проблемой. Здесь я показываю свое решение и как я это разрешаю. Пожалуйста, исправьте, если мой код неправильный. Что мы имеем в это время? Acivity, много фрагментов и их backstack. Мы хотим открыть каждый фрагмент из меню Ящика и очистить все остальные фрагменты из backstack. Но мы должны держать только один фрагмент дома. Когда мы остаемся на домашнем фрагменте, а пользователь нажимает кнопку «Назад», приложение закрывается.
И при нажатии на элемент меню ящика
Я сделал следующий путь. Очистите весь фрагмент, затем добавьте первый фрагмент дома
Источник
Проблемы с Android Fragment back stack
у меня есть огромная проблема с тем, как Android фрагмент backstack, кажется, работает и был бы очень благодарен за любую помощь, которая предлагается.
представьте, что у вас есть 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-файла для моего основного деятельность:
обновление Это код, который я использую для создания nav heirarchy
8 ответов:
пояснение: что здесь происходит?
если мы будем иметь в виду, что .replace() равен .remove().add() что мы знаем документацией:
заменить существующий фрагмент, который был добавлен в контейнер. Это по сути то же самое, что и вызов remove(Fragment) для всех добавленных в настоящее время фрагментов, которые были добавлены с тем же containerViewId а то add(int, Fragment, String) С теми же аргументами, приведенными здесь.
тогда то, что происходит, похоже на это (я добавление чисел к фрагменту, чтобы сделать его более ясным):
(здесь все вводящие в заблуждение вещи начинают происходить)
помните, что .addToBackStack() спасает только сделки не фрагмент как и сама! Так что теперь у нас есть frag3 на макете:
возможное решение
рассмотреть вопрос об осуществлении FragmentManager.BackStackChangedListener чтобы следить за изменениями в заднем стеке и применять свою логику в onBackStackChanged() methode:
правильно. после того, как много волос тянет я, наконец, понял, как сделать эту работу правильно.
Кажется, что фрагмент [3] не удаляется из вида при нажатии назад, поэтому вам нужно сделать это вручную!
прежде всего, не используйте replace (), но вместо этого используйте remove и add отдельно. Похоже, что replace () не работает должным образом.
следующая часть к этому переопределяет метод onKeyDown и удаляет текущий фрагмент каждый раз, когда назад кнопка нажата.
надеюсь, что это помогает!
прежде всего спасибо @Arvis за объяснение открытия глаз.
Я предпочитаю другое решение принятого ответа здесь для этой проблемы. Мне не нравится возиться с переопределением обратного поведения больше, чем абсолютно необходимо, и когда я попытался добавить и удалить фрагменты самостоятельно без всплывающего стека по умолчанию при нажатии кнопки «назад», я нашел себя в фрагменте ада 🙂 если вы .добавьте f2 поверх f1 при его удалении f1 не будет вызывать ни один из методов обратного вызова, таких как onResume, onStart etc. и это может быть очень печально.
во всяком случае, вот как я это делаю:
В настоящее время на дисплее отображается только фрагмент f1.
ничего необычного здесь. Чем во фрагменте f2 этот код приведет вас к фрагменту f3.
Я не уверен, читая документы, если это должно работать, этот метод транзакции poping называется асинхронным, и, возможно, лучшим способом было бы вызвать popBackStackImmediate (). Но насколько я могу судить на своих устройствах, он работает безупречно.
упомянутая альтернатива будет:
здесь на самом деле будет краткое возвращение к f1 beofre переход к f3, поэтому небольшой глюк там.
это на самом деле все, что вам нужно сделать, не нужно переопределять поведение заднего стека.
Я знаю, что это старый quetion, но я получил ту же проблему и исправить это так:
во-первых, добавьте Фрагмент1 в BackStack с именем (например, «Frag1»):
и затем, когда вы хотите вернуться к Fragment1 (даже после добавления 10 фрагментов над ним), просто вызовите popBackStackImmediate с именем:
надеюсь, что это поможет кому-то 🙂
для ленивых разработчиков ПО. Мое решение состоит в том, чтобы всегда добавлять транзакции в backstack и выполнять дополнительный FragmentManager.popBackStackImmediate() при необходимости (автоматически).
код очень мало строк кода и, в моем примере, я хотел перейти от C к A, не прыгая обратно в «B», если пользователь не пошел глубже в backstack (ex от C переходит к D).
следовательно, прилагаемый код будет работать следующим образом A — > B — > C (назад) — > A & А -> б -> c -> Д (задняя) -> С (назад) -> Б (назад) — а
были выпущены от «B» до «C», как в вопросе.
Если вы боретесь с addToBackStack () & popBackStack (), то просто используйте
в вашей деятельности в OnBackPressed () узнайте fargment по тегу, а затем сделайте свой материал
для получения дополнительной информации https://github.com/DattaHujare/NavigationDrawer Я никогда не использую addToBackStack() для обработки фрагмента.
Я думаю, когда я читаю ваш рассказ, что [3] также находится на заднем плане. Это объясняет, почему вы видите, что он мигает.
решение было бы никогда не устанавливать [3] в стеке.
у меня была аналогичная проблема, где у меня было 3 подряд фрагментов в том же Activity [M1.F0] — >[M1.F1] — >[M1.F2] с последующим вызовом нового Activity [M2]. Если пользователь нажал кнопку в [M2], я хотел вернуться к [M1,F1] вместо [M1, F2], что уже было сделано при обратном нажатии.
для этого я удаляю [M1, F2], вызываю show на [M1, F1], фиксирую транзакцию,а затем добавляю [M1, F2] обратно, вызывая ее с помощью hide. Это убрали еще пресс, который в противном случае остался бы позади.
Привет После выполнения этого кода: я не могу видеть значение Fragment2 при нажатии клавиши Back. Мой Код:
Источник