How to resume existing Fragment from BackStack
Reading the documentation, there is a way to pop the back stack based on either the transaction name or the id provided by commit. Using the name may be easier since it shouldn't require keeping track of a number that may change and reinforces the "unique back stack entry" logic.
Since you want only one back stack entry per Fragment
, make the back state name the Fragment's class name (via getClass().getName()
). Then when replacing a Fragment
, use the popBackStackImmediate()
method. If it returns true, it means there is an instance of the Fragment in the back stack. If not, actually execute the Fragment replacement logic.
private void replaceFragment (Fragment fragment){
String backStateName = fragment.getClass().getName();
FragmentManager manager = getSupportFragmentManager();
boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);
if (!fragmentPopped){ //fragment not in back stack, create it.
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content_frame, fragment);
ft.addToBackStack(backStateName);
ft.commit();
}
}
EDIT
The problem is - when i launch A and then B, then press back button, B
is removed and A is resumed. and pressing again back button should
exit the app. But it is showing a blank window and need another press
to close it.
This is because the FragmentTransaction
is being added to the back stack to ensure that we can pop the fragments on top later. A quick fix for this is overriding onBackPressed()
and finishing the Activity if the back stack contains only 1 Fragment
@Override
public void onBackPressed(){
if (getSupportFragmentManager().getBackStackEntryCount() == 1){
finish();
}
else {
super.onBackPressed();
}
}
Regarding the duplicate back stack entries, your conditional statement that replaces the fragment if it hasn't been popped is clearly different than what my original code snippet's. What you are doing is adding to the back stack regardless of whether or not the back stack was popped.
Something like this should be closer to what you want:
private void replaceFragment (Fragment fragment){
String backStateName = fragment.getClass().getName();
String fragmentTag = backStateName;
FragmentManager manager = getSupportFragmentManager();
boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);
if (!fragmentPopped && manager.findFragmentByTag(fragmentTag) == null){ //fragment not in back stack, create it.
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content_frame, fragment, fragmentTag);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(backStateName);
ft.commit();
}
}
The conditional was changed a bit since selecting the same fragment while it was visible also caused duplicate entries.
Implementation:
I highly suggest not taking the the updated replaceFragment()
method apart like you did in your code. All the logic is contained in this method and moving parts around may cause problems.
This means you should copy the updated replaceFragment()
method into your class then change
backStateName = fragmentName.getClass().getName();
fragmentPopped = manager.popBackStackImmediate(backStateName, 0);
if (!fragmentPopped) {
ft.replace(R.id.content_frame, fragmentName);
}
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(backStateName);
ft.commit();
so it is simply
replaceFragment (fragmentName);
EDIT #2
To update the drawer when the back stack changes, make a method that accepts in a Fragment and compares the class names. If anything matches, change the title and selection. Also add an OnBackStackChangedListener
and have it call your update method if there is a valid Fragment.
For example, in the Activity's onCreate()
, add
getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
Fragment f = getSupportFragmentManager().findFragmentById(R.id.content_frame);
if (f != null){
updateTitleAndDrawer (f);
}
}
});
And the other method:
private void updateTitleAndDrawer (Fragment fragment){
String fragClassName = fragment.getClass().getName();
if (fragClassName.equals(A.class.getName())){
setTitle ("A");
//set selected item position, etc
}
else if (fragClassName.equals(B.class.getName())){
setTitle ("B");
//set selected item position, etc
}
else if (fragClassName.equals(C.class.getName())){
setTitle ("C");
//set selected item position, etc
}
}
Now, whenever the back stack changes, the title and checked position will reflect the visible Fragment
.
How to navigate to existing Fragment from BackStack?
If I got what you want correctly,
You can use popUpTo in Pop Behavior in the right side of your host xml where you connect fragments and navigate fragments and set popUpTo to a fragment that you want
or
you can use this code to say where to go when back button pressed:
Note: you need to paste them in fragment class
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
add your navigation destonation or do whatever you want
}
you have another option too.
You can use:
view?.findNavController().popBackStack()
And last but not the least
If you are in activty class I mean not Fragment you can use this:
override fun onBackPressed() {
do what you need
if you leave this fun empty, back button would not work any more
}
I think second option is your fit
Code to replace or remove existing fragment on backstack not working
You're using NavController
and therefore you should not be doing any manual FragmentTransaction
s at all.
Instead, you should Navigation to a destination, in this case, by using Navigation's support for popUpTo to pop your CategoryFragment off the stack.
Assuming you have a destination set up in your navigation XML for your CategoryFragment
with code such as:
<fragment
android:id="@+id/categoryFragment"
android:name=".CategoryFragment/>
You can create an action that includes the popUpTo
flag you need (you'd put this directly below the <fragment>
itself:
<action
android:id+"@+id/open_category"
app:popUpTo="@id/categoryFragment"
app:popUpToInclusive="true"
app:destination="@id/categoryFragment"/>
This says:
Create an action named
open_category
When you trigger this action, pop the stack back up to
categoryFragment
if it already exists on the back stack (otherwise, this does nothing). ThepopUpToInclusive
means that the previous instance is popped.After you pop the previous instance, create a new instance of
categoryFragment
and add it to your back stack.
Then you can trigger that action from your onOptionsItemSelected()'
:
override fun onOptionsItemSelected(item: MenuItem): Boolean {
Log.i("Lifecycle-Activity", "OnOptionsItemSelected() called")
return when (item.itemId) {
R.id.categoryFragment -> {
// Trigger the action
navController.navigate(R.id.open_category);
true
}
default -> {
// android.R.id.home is handled by the call to super,
// you do not need to handle it here.
super.onOptionsItemSelected(item)
}
}
}
Programmatically go back to the previous fragment in the backstack
Look at the getFragmentManager().popBackStack()
methods (there are several to choose from)
http://developer.android.com/reference/android/app/FragmentManager.html#popBackStack()
Related Topics
How to Change Spinner Text Size and Text Color
Custom Drawable For Progressbar/Progressdialog
Playing an Arbitrary Tone With Android
Show Image View from File Path
How to Save/Store Objects in Sharedpreferences on Android
How to Parse the Androidmanifest.Xml File Inside an .Apk Package
Sending Message Through Whatsapp
Tileprovider Using Local Tiles
How to Use Scrollview in Android
Taking Picture from Camera Without Preview
How to Refresh App Upon Shaking the Device
How to Refresh Mediastore on Android
Android Difference Between Two Dates
Perform a Task on Uninstall in Android
Change Value of R.String Programmatically
Execution Failed App:Processdebugresources Android Studio
How to Add Window Android.View.Viewroot$W@44Da9Bc0 -- Permission Denied for This Window Type