Problems with Android Fragment back stack
Explanation: on what's going on here?
If we keep in mind that .replace()
is equal with .remove().add()
that we know by the documentation:
Replace an existing fragment that was added to a container. This is essentially the same as calling
remove(Fragment)
for all currently added fragments that were added with the samecontainerViewId
and thenadd(int, Fragment, String)
with the same arguments given here.
then what's happening is like this (I'm adding numbers to the frag to make it more clear):
// transaction.replace(R.id.detailFragment, frag1);
Transaction.remove(null).add(frag1) // frag1 on view
// transaction.replace(R.id.detailFragment, frag2).addToBackStack(null);
Transaction.remove(frag1).add(frag2).addToBackStack(null) // frag2 on view
// transaction.replace(R.id.detailFragment, frag3);
Transaction.remove(frag2).add(frag3) // frag3 on view
(here all misleading stuff starts to happen)
Remember that .addToBackStack()
is saving only transaction not the fragment as itself! So now we have frag3
on the layout:
< press back button >
// System pops the back stack and find the following saved back entry to be reversed:
// [Transaction.remove(frag1).add(frag2)]
// so the system makes that transaction backward!!!
// tries to remove frag2 (is not there, so it ignores) and re-add(frag1)
// make notice that system doesn't realise that there's a frag3 and does nothing with it
// so it still there attached to view
Transaction.remove(null).add(frag1) //frag1, frag3 on view (OVERLAPPING)
// transaction.replace(R.id.detailFragment, frag2).addToBackStack(null);
Transaction.remove(frag3).add(frag2).addToBackStack(null) //frag2 on view
< press back button >
// system makes saved transaction backward
Transaction.remove(frag2).add(frag3) //frag3 on view
< press back button >
// no more entries in BackStack
< app exits >
Possible solution
Consider implementing FragmentManager.BackStackChangedListener
to watch for changes in the back stack and apply your logic in onBackStackChanged()
methode:
- Trace a count of transaction;
- Check particular transaction by name
FragmentTransaction.addToBackStack(String name);
- Etc.
Fragment BackStack doesn't work properly
The issue with the 'back' button in your Toolbar
, is that it is not designed for back navigation. The backwards facing arrow in the Toolbar
is for 'up' navigation (incredibly confusing at times -- and often shares the same behavior as the device 'back' button). See this link for further explanation:
http://developer.android.com/design/patterns/navigation.html
While there are certainly ways to override 'up' behavior, (e.g., overriding android.R.id.home
in the onOptionsItemSelected
method), it might make more sense to add a SettingsActivity that manages the SettingsFragment. You can then set the parent Activity
of the SettingsActivity to the MainActivity in your manifest. Then both back and up should work the way you want without overriding any standard OS behavior:
http://developer.android.com/training/implementing-navigation/ancestral.html
Hope that helps!
Fragment and BackStack not working properly
try this,
call method .addToBackStack("returnFragment") while loading second fragment and remove it from first transaction.
How to fix fragments backstack issues in android
Create a singleton named NavigationHandler and add the below functions to it:
Function to open MainFragment:
public void openMainFragment(FragmentManager fragmentManager, MainFragment fragment){
String backStackName = fragment.getClass().getSimpleName();
fragmentManager.beginTransaction()
.replace(R.id.fl_main_container, fragment)
.addToBackStack(backStackName)
.commit();
}
Function to open SubFragment:
public void openSubFragment(FragmentManager fragmentManager, SubFragment fragment){
String backStackName = fragment.getClass().getSimpleName();
fragmentManager.popBackStackImmediate(backStackName, POP_BACK_STACK_INCLUSIVE);
fragmentManager.beginTransaction()
.replace(R.id.fl_main_container, fragment)
.addToBackStack(backStackName)
.commit();
}
For backpress:
public void navigateBackBy(AppCompatActivity activity, int numOfFragments){
if(mFragmentManager.getBackStackEntryCount()==1){
activity.finish();
}
else {
int i;
for (i = 0; i < numOfFragments; i++) {
mFragmentManager.popBackStackImmediate();
}
}
}
In your Activity:
Call openMainFragment(...) to open the MainFragment with the list.
While calling your adapter constructor pass FragmentManager as a parameter.
Override onBackPressed() & add call navigateBackBy(this, 1);. This method is useful to go back by any no. of fragments.
Inside your adapter:
- On click of an item call openSubFragment(...).
Fragment back stack clearing Issue
The problem you have lies in the way you are dealing with the fragments lifecycle. You want Fragment C to do onCreateView
only once (to show the popup), but onCreateView
get's called every time the View is created (e.g, every time you call remove on a fragment(replace works pretty much the same, remove + add) and then add it back from backstack with popbackstack).
For your problems there are two solutions:
Cleaner one: instead of showing your popup from onCreateView
, call it from onCreate
in Fragment C. With this you will guarantee that it only get's called when the fragment instance is created.
Not so clean: Instead of using replace
between Fragment C and D transaction, call add
, this way when you pop the backstack in Fragment D, Fragment C onCreateView
won't be called because the View was never destroyed (never called remove/replace upon).
Back button not working when adding fragment to backstack
Override onBackPressed()
into your activity and call this in order to remove current fragment from backstack, since you add it.
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStackImmediate()
} else {
finish();
}
This will work only when you add fragment to backstack using addToBackStack()
method.
When you add a fragment to backstack to keep tracking your back flow and all the previous changes, that instance will be keeped into FragmentManager
. When you want to go back to previous fragment, just pop the latest fragment from backstack. If you don't add it to stack, you will not be able to roll back the taken path and all the previous oprations.
Back stack not working properly in Jetpack Navigation
As per the documentation:
- The
app:defaultNavHost="true"
attribute ensures that yourNavHostFragment
intercepts the system Back button. Note that only oneNavHost
can be the default. If you have multiple hosts in the same layout (two-pane layouts, for example), be sure to specify only one defaultNavHost
.
You are missing that attribute so Navigation is not handling the system back button.
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nave_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:navGraph="@navigation/nav_graph"
app:defaultNavHost="true"
/>
Related Topics
How to Change the Text Color of Menu Item in Android
Android Listview Not Refreshing After Notifydatasetchanged
How to Execute Async Task Repeatedly After Fixed Time Intervals
How Does One Use Glide to Download an Image into a Bitmap
How to Capture the Android Device Screen Content
Best Way to Work with Dates in Android SQLite
Calling Startactivity() from Outside of an Activity Context
How to Mock Location on Device
Android New Bottom Navigation Bar or Bottomnavigationview
Android Sdk Manager Won't Open
How to Provide Shadow to Button